Передача строки с нулевым завершением в 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";