Передача строки с нулевым завершением в printf приводит к неожиданному значению

Эта программа C дает странный результат:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
   char str1[5] = "abcde";
   char str2[5] = " haha";

   printf("%s\n", str1);
   return 0;
}

когда я запускаю этот код, я получаю:

abcde haha

Я хочу только напечатать первую строку, как видно из кода.
Почему они печатают оба из них?

Ответ 1

"abcde" на самом деле составляет 6 байтов из-за нулевого символа завершения в строках C. Когда вы это сделаете:

char str1[5] = "abcde";

Вы не сохраняете нулевой завершающий символ, поэтому он не является правильной строкой.

Когда вы это сделаете:

char str1[5] = "abcde";
char str2[5] = " haha";
printf("%s\n", str1);

Просто случается, что вторая строка сохраняется сразу после первого, хотя это не требуется. Вызывая printf в строке, которая не является нулевой, вы уже вызвали поведение undefined.

Update:

Как указано в комментариях clcto, этого можно избежать, если явно не указать размер массива и позволить компилятору определить его на основе строки:

char str1[] = "abcde";

или вместо этого используйте указатель, если это работает для вашего варианта использования, хотя они не совпадают:

char *str1 = "abcde";

Ответ 2

Обе строки str1 и str2 не завершаются нулем. Поэтому утверждение

 printf("%s\n", str1);  

вызывается поведение undefined.
printf печатает символы в строке один за другим, пока не встретит '\0', который отсутствует в вашей строке. В этом случае printf продолжается до конца строки, пока не найдет нулевой символ где-нибудь в памяти. В вашем случае кажется, что printf прошел конец строки "abcde" и продолжает печатать символы из второй строки " haha", которая случайно помещена сразу после первой строки в памяти.

Лучше изменить блок

   char str1[5] = "abcde";
   char str2[5] = " haha";  

to

 char str1[] = "abcde";
 char str2[] = " haha";  

чтобы избежать этой проблемы.

Ответ 3

Технически это поведение не является неожиданным, оно undefined: ваш код передает указатель на строку C, которая не имеет нулевого терминатора до printf, который является undefined.

В вашем случае, однако, случается, что компилятор помещает две строки обратно в обратную сторону в памяти, поэтому printf запускается в терминатор null после печати str2, что объясняет результат, который вы получаете.

Если вы хотите напечатать только первую строку, добавьте пробел для нулевого терминатора, например:

char str1[6] = "abcde";

Еще лучше, пусть компилятор рассчитает правильный размер для вас:

char str1[] = "abcde";

Ответ 4

Вы вызвали поведение undefined. Здесь:

   char str1[5] = "abcde";

str1 имеет место для этих пяти букв, но не имеет нулевого терминатора. Затем, как вы пытаетесь передать строку с нулевым завершением в printf для печати, вызывает поведение undefined.

В большинстве случаев не рекомендуется передавать строки с нулевым завершением в стандартные функции, которые ожидают (C) строки.

Ответ 5

В C такие объявления

char str1[5] = "abcde";

разрешены. На самом деле есть 6 инициализаторов, потому что строковый литерал включает в себя нулевой конец. Однако в левой части объявлен массив символов, который имеет только 5 элементов. Таким образом, он не включает завершающий нуль.

Похоже на

char str1[5] = { 'a', 'b', 'c', 'd', 'e', '\0' };

Если вы скомпилируете это объявление в С++, компилятор выдает ошибку.

Было бы лучше объявить массив, не указав явно его размер.

char str1[] = "abcde";

В этом случае он будет иметь размер, равный числу символов в строковом литерале, включая оканчивающийся нуль, равный 6. И вы можете написать

printf("%s\n", str1);

В противном случае функция продолжает печатать символы за пределами массива, пока не встретит нулевой символ.

Тем не менее это не ошибка. Просто вы должны правильно указать спецификатор формата в вызове printf:

printf("%5.5s\n", str1);

и вы получите ожидаемый результат.

Ответ 6

Спецификатор %s выполняет поиск нулевого завершения. Поэтому вам нужно добавить '\0' в конец строки

char str1[6] = "abcde\0";