Как выполняется оператор while в C или как работают указатели, на которые ссылается массив?

#c #pointers #while-loop

Вопрос:

Я начал изучать Си, и у меня было это упражнение из книги «Прентис Холл — Язык программирования Си«.

Глава 5 Упражнение 3. Напишите версию указателя функции strcat, которую мы показали в главе 2. strcat(s, t) копирует строку t в конец s.

Я выполнил упражнение, но первый метод, который пришел мне в голову, был:

 void stringcat(char *s, char *t){    int i,j;  i = j = 0;    while(*(s i) != ''){  printf("%d", i);  i  ;    }   while ( (*(t j)) != ''){  *(s i) = *(t j);  i  ;  j  ;  } }  

В основном у меня были:

  int main(){  char s[] = "Hola";  char t[] = "lala";   stringcat(s,t);  printf("%sn", s); }   

На первый взгляд я подумал, что это правильно, но фактический результат был Holalalaa . Конечно, это был не тот результат, который я ожидал, но потом я закодировал это:

 void stringcat(char *s, char *t){    int i,j;  i = j = 0;    while(*(s i) != ''){  printf("%d", i);  i  ;    }    while((*(s i) = *(t j)) != ''){  i  ;  j  ;  } }  

И результат был правильным.

Но потом я много думал о первом коде, потому что он очень похож на второй, но почему первый вывод был неправильным?. Это как-то связано с утверждением while? или что-то с указателями?. Мне было действительно трудно понять, потому что вы не можете видеть, что происходит в массиве.

Большое спасибо.

Комментарии:

1. Вы получите переполнение массива, потому char s[] = "Hola"; что в нем нет места для объединения. Вам будет легче читать, написав *(s i) как s[i] .

2. @WeatherVane Я тоже думал об этом, но для этого я должен вернуть новый массив, верно? Или есть какой-либо способ использовать недействительную подпись?.

3. Кроме того, обратите внимание, что ваш первый фрагмент кода не копирует завершающий нулевой символ из t строки.

4. Выделите для другого массива сумму двух длин 1 и верните ее из char *stringcat(char *s, char *t)

5. @AdrianMole большое спасибо, я этого не заметил.

Ответ №1:

В вашем коде есть не одна проблема, которую вы обнаружили, но давайте начнем с нее.

На самом деле вы спрашиваете, почему

 /* ... */  while ((*(t j)) != '') {  *(s i) = *(t j);  /* ... */  

работает иначе, чем

 /* ... */  while ((*(s i) = *(t j)) != '') {  /* ... */  

Я надеюсь, вы уже это видите, теперь, когда оба случая стоят бок о бок, фактически вертикально ;-). В первом случае значение t[j] сравнивается до того, как оно будет скопировано s[i] . Во втором случае сравнение выполняется после копирования. Вот почему во втором случае завершение копируется '' в целевую строку, а в первом случае-нет.


Вывод, который вы получаете, работает случайно, это неопределенное поведение, так как вы пишете за пределами целевого массива. К счастью для вас, обе строки последовательно хранятся в памяти, и вы перезаписываете исходную строку ее собственными символами.

Поскольку ваш первый случай не копирует '' , в конечном printf() итоге выводится больше символов, пока '' не будет найдено a. По случайности это последнее 'a' .


Как отмечали другие, в целевой строке недостаточно места для объединенной строки. Предоставьте еще немного места, как это:

 char s[10] = "Hola"; /* 10 is enough for both strings and the terminating ''. */  

Однако, если бы вы уже сделали это, ошибка не была бы обнаружена, потому что последние 6 символов s инициализируются с '' помощью . Не копирование завершения '' не имеет никакого значения. Вы можете увидеть это, если используете

 char s[10] = "Holaxxxx";  

Я не думаю, что ваше решение является ожидаемым. Вместо s[i] того , чтобы вы используете *(s i) , что по сути одно и то же, доступ к массиву. Рассмотрите возможность изменения s (и, конечно же, t ) в функции и используйте just *s .


Примечание: Функция printf() в функции, скорее всего, является остатком отладки. Но я уверен, что ты знаешь.