2016-01-25 1 views
0

В C, когда я инициализировать мой массив таким образом:Что произошло, когда мы не включили ' 0' в конец строки в C?

char full_name[] = { 
    't', 'o', 'a', 'n' 
}; 

и распечатать его с printf("%s", full_name);

и запустить его с Valgrind я получил ошибку

значение неиницализированные было создать с помощью распределение штатов

Зачем это происходит?

+0

@dasblinkenlight Благодарим за редактирование сообщения. – toantruong

+3

Если нет нулевого завершения, то это не строка, касающаяся C. – Lundin

ответ

4

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

Это не означает, что все массивы символов должны иметь нуль-конец: правило применяется только к массивам символов, предназначенным для использования в качестве строк C, например. который должен быть передан в printf по спецификатору формата %s или передан в strlen или другими строковыми функциями библиотеки Standard C.

Если вы хотите использовать массив char для чего-то еще, его не нужно пустым завершать. Например, это использование полностью определено:

char full_name[] = { 
    't', 'o', 'a', 'n' 
}; 
for (size_t i = 0 ; i != sizeof(full_name) ; i++) { 
    printf("%c", full_name[i]); 
} 
+0

Я бы использовал 'i! = Sizeof (full_name)/sizeof (char)', потому что spec только говорит, что 'char' является * минимальным * одного байта, и вы никогда не знаете. –

+1

@ Стандарт @JohnSensebe C требует 'sizeof (char)', чтобы всегда создавать '1'. – dasblinkenlight

+0

Достаточно честный. Лучшим выбором будет 'i! = Sizeof (full_name)/sizeof (full_name [0])' в любом случае, на всякий случай, если будет изменено определение 'full_name'. –

5

Если вы не обеспечиваете '\0' в конце для запятой брекета списка закрытых инициализаторов, технически full_name не строки, как char массива не оканчивается нулем.

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

Таким образом, в случае определения, как

char full_name[] = { 
    't', 'o', 'a', 'n' 
}; 

размер массива равен 4, и она имеет 't', 'o', 'a', 'n' в него.

Ото, в случае

char full_name[] = "toan"; 

full_name будет иметь размер 5 и будет содержать 't', 'o', 'a', 'n' и '\0' в него.

При попытке использовать бывший массив с любой функцией, работающей на строках (т.е. ожидает завершающий нуль char массива), вы получите undefined behavior как большинство из строковых функций будет выходить из границы в поисках нуль-терминатора.

В вашем конкретном примере, для %s спецификатора формата с printf(), цитируя C11 стандарт, глава §7.21.6.1, fprintf() описания функции (курсив моего)

s
Если нет модификатора l длины не является present, аргумент должен быть указателем на начальный элемент массива типа символа. 280)Символы из массива: записано до (но не включая) завершающий нулевой символ. Если указана точность , пишется не более чем много байтов. Если точность не указана или больше размера массива, массив должен содержать нулевой символ.

Это значит, printf() будет искать нуль-терминатором, чтобы отметить/понять конец массива. В вашем примере отсутствие нулевого терминатора приведет к тому, что printf() выйдет за выделенную память (full_name[3]) и получит доступ к внешней памяти (full_name[4]), которая вызовет UB.

+0

lol, FGITW забыл использовать свой speedloader :) Иметь голос в любом случае. –

+0

@MartinJames :) Обновлено наконец. :) –

1

printf интерпретирует «% s» как стандартную строку C. Это означает, что генерируемый код будет просто читать символы до тех пор, пока не найдет нулевой ограничитель (\ 0).

Часто это будет означать, что этот странствующий указатель отправится в неизведанную память, и Valgrind заметит это как ошибку.

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

0

Если вы используете последовательность символов с нулевым завершением в виде строки, функции C будут продолжать работать. Это '\ 0', который говорит им прекратить. Итак, что бы ни случилось в памяти после того, как последовательность будет взята как часть строки. Это может в конечном счете пересечь границу памяти и вызвать ошибку, или она может просто напечатать тарабарщину, если это произойдет, когда где-нибудь найдется «\ 0» и остановится.

0

Перед передачей инструкции указателю на функцию, ожидающую строку c, вы неявно вступаете в юридически обязывающий контракт с этим кодовым блоком. В основном разделе этого договора обе стороны соглашаются воздерживаться от обмена информацией о выделенной строке и утверждать, что все переданные параметры, объявленные как строки, указывают на последовательность символов, заканчивающихся \0, которая дает каждой стороне возможность рассчитать длину.

Если вы не указали завершающий \0, вы совершите существенное нарушение договора.

Судья ОС будет случайным образом подавать в суд на ваш исполняемый файл с безумием или даже смертью.