#c #c-strings
#c #c-строки
Вопрос:
Я изучаю C и столкнулся с проблемой при работе со строками. В задаче, которую я решал, я должен был написать функцию для получения строки и символа и удаления всех вхождений данного символа, а затем я должен был вернуть измененную строку. Функция, которую я написал, такова:
char *strdelc3(char *s, char ch){
for(int i=0,j=0; i!=strlen(s) 1; i)
if(s[i]!=ch){
s[j]=s[i];
j;
}
return s;
}
И когда я передаю строку и символ в качестве аргументов:
main(){
char s[20]="mary";
puts(strdelc3(s,'r'));
}
The output is: Segmentation fault(core dumped),
что, согласно моим исследованиям, означает, что я обращаюсь к памяти, которая мне не принадлежит.
Решения имели этот код:
char *strdelc4(char *s, char ch){ /*Correct*/
int i,j;
for(i=0, j=0; s[i]!=''; i)
if(s[i]!=ch){
s[j]=s[i];
j;
}
s[j]='';
return s;
}
Которая в основном равна моей, однако эта часть работает нормально!
Поскольку два кода настолько похожи, я не вижу ничего плохого в моем…
Я уже изучил оба, но я не вижу, в чем проблема с моим… Может кто-нибудь помочь?
Комментарии:
1. Почему
i!=strlen(s) 1
в первом примере? Я думаю, что в этом и заключается проблема. Зачем добавлять один?2. Между функциями есть два различия. Попробуйте их одну за другой, чтобы понять, почему каждая из них создает разные проблемы.
3. Проблема в том, что когда
i
естьstrlen(s)
, вы переместите нулевой байт, и длина строки уменьшится, поэтомуi
никогда не будет равнаstrlen(s) 1
. Всегда плохая идея помещатьstrlen
в условный цикл (это огромная трата времени). Но в этом случае это особенно плохо.4. В любом случае, возьмите простую строку длиной в несколько символов и запустите на ней свою программу с помощью отладчика. Это то, что мы обычно делаем, если не можем обнаружить проблему, просмотрев код.
5. Ааааа, вау, спасибо, пользователь3386109, теперь я понимаю! когда i = 5, strlen (s) 1 = 4, и я буду продолжать увеличиваться, не будучи равным моему условию.
Ответ №1:
Проблема в вашем условном цикле:
i!=strlen(s) 1
Вы пытаетесь использовать strlen(s) 1
здесь, чтобы избежать необходимости добавлять нулевой байт. Но при этом strlen(s)
изменяется, как только вы перемещаете завершающий нулевой байт.
На первых 4 итерациях цикла strlen(s)
равно 4. На следующей итерации i
равно 4, а strlen(s) 1
равно 5, поэтому вы снова входите в цикл. Затем вы перемещаете нулевой байт. Теперь на следующей итерации strlen(s)
равно 3, а i
равно 5. Условие по-прежнему верно, поэтому вы продолжаете, отходя от конца строки. Это вызывает неопределенное поведение, которое в этом случае вызывает сбой.
Вторая часть кода решает эту проблему, явно ища нулевой байт на основе индекса i
и добавляя нулевой байт к результирующей строке после цикла.
Ответ №2:
Еще более простая версия кода использовала бы do - while
цикл вместо for()
:
char *strdelc5idx(char *s, char ch){
int i=0, j=0;
do {
if (s[i] != ch)
s[j ] = s[i];
} while (s[i ] != 0);
return s;
}
Это скопирует завершающий строку символ NUL перед его тестированием, поэтому вам не нужно иметь для этого отдельную инструкцию. Однако для этого требуется отложить i
приращение, чтобы условие цикла в конце итерации проверяло тот же символ, который был скопирован в итерации. В результате i
и j
больше не отображаются вместе, что может сделать этот код менее разборчивым на первый взгляд.
Эквивалентная версия указателя:
char *strdelc5ptr(char *s, char ch){
char *d = s, *f = s;
do {
if (*f != ch)
*d = *f;
} while (*f );
return s;
}