Нулевое завершение массива char
Рассмотрим следующий случай:
#include<stdio.h>
int main()
{
char A[5];
scanf("%s",A);
printf("%s",A);
}
Мой вопрос в том, что char A[5]
содержит только два символа. Скажите "ab", затем A[0]='a'
, A[1]='b'
и A[2]='\0'
.
Но если вход скажет: "abcde", тогда где '\0'
в этом случае. Будет ли A[5]
содержать '\0'
?
Если да, то почему?
sizeof(A)
всегда будет возвращать 5 в качестве ответа. Затем, когда массив заполнен, есть ли дополнительный байт, зарезервированный для '\0'
, который sizeof()
не учитывается?
Ответы
Ответ 1
Если вы введете более четырех символов, дополнительные символы и нулевой терминатор будут записаны вне конца массива, перезаписывая память, не принадлежащую массиву. Это переполнение буфера.
C не мешает вам скрывать память, которой вы не владеете. Результатом этого является undefined поведение. Ваша программа могла бы что-нибудь сделать, она могла бы сработать, она могла бы без проблем уничтожить другие переменные и вызвать непонятное поведение, это может быть безвредно или что-то еще. Обратите внимание, что нет гарантии, что ваша программа будет работать надежно или аварийно. Вы даже не можете зависеть от того, что он сбой немедленно.
Это отличный пример того, почему scanf("%s")
является опасным и никогда не должен использоваться. Он не знает о размере вашего массива, что означает, что нет возможности безопасно его использовать. Вместо этого избегайте scanf и используйте что-то более безопасное, например fgets():
fgets() читает не более один меньше, чем размер символов из потока и сохраняет их в буфер, на который указывает s. Чтение останавливается после EOF или новой строки. Если читается новая строка, она сохраняется в буфере. Конечный нулевой байт ('\ 0') сохраняется после последнего символа в буфере.
Пример:
if (fgets(A, sizeof A, stdin) == NULL) {
/* error reading input */
}
Раздражающе, fgets() оставит конечный символ новой строки ('\n') в конце массива. Таким образом, вы также можете удалить код.
size_t length = strlen(A);
if (A[length - 1] == '\n') {
A[length - 1] = '\0';
}
Тьфу. Простой (но сломанный) scanf("%s")
превратился в 7-строчное чудовище. И это второй урок дня: C не подходит для обработки ввода-вывода и обработки строк. Это можно сделать, и это можно сделать безопасно, но C будет пинать и кричать все время.
Ответ 2
Как уже указывалось, вам нужно определить/выделить массив длины N + 1, чтобы правильно хранить N символов. Можно ограничить количество символов, прочитанных scanf. В вашем примере это будет:
scanf("%4s", A);
чтобы читать макс. 4 символа от stdin.
Ответ 3
Нет символа, который зарезервирован, поэтому вы должны быть осторожны, чтобы не заполнить весь массив до такой степени, что он не может быть завершен нулем. Char функции зависят от нулевого терминатора, и вы получите от них катастрофические результаты, если окажетесь в ситуации, которую вы описываете.
Многое C-код, который вы увидите, будет использовать производные от n таких функций, как strncpy. На этой странице можно прочитать:
Функции strcpy() и strncpy() возвращают s1. Stpcpy() и Функции stpncpy() возвращают указатель на завершающий символ `\ 0 's1. Если stpncpy() не завершает s1 с помощью NUL character, вместо этого возвращает указатель на s1 [n] (который не обязательно ссылается на действительный mem- ory.)
strlen также полагается на нулевой символ, чтобы определить длину символьного буфера. Если и когда вам не хватает этого символа, вы получите неверные результаты.
Ответ 4
В итоге вы получите поведение undefined.
Как вы говорите, размер A
всегда будет 5, поэтому, если вы прочитаете 5 или более char
s, scanf
попытается записать в память, что он не должен изменяться.
И нет, нет зарезервированного пространства / char для символа \0
.
Ответ 5
Любая строка длиной более 4 символов вызовет запись scanf
за пределы массива. Полученное поведение undefined, и если вам повезет, это приведет к сбою вашей программы.
Если вам интересно, почему scanf
не прекращает записывать строки, которые слишком длинны для хранения в массиве A
, это потому, что нет возможности для scanf
знать sizeof(A)
равно 5. Когда вы передаете массив как параметр функции C, массив распадается на указатель, указывающий на первый элемент в массиве. Таким образом, нет способа запросить размер массива внутри функции.
Чтобы ограничить количество символов, считанных в массиве, используйте
scanf("%4s", A);
Ответ 6
массивы символов в c - это просто указатели на блоки памяти. Если вы сообщите компилятору зарезервировать 5 байт для символов, это произойдет. Если вы попытаетесь разместить там более 5 байтов, он просто перезапишет память за 5 байтов, которые вы зарезервировали.
Вот почему c может иметь серьезные реализации безопасности. Вы должны знать, что вы собираетесь писать только 4 символа + a\0. C позволит вам перезаписать память до сбоя программы.
Пожалуйста, не думайте о char foo [5] как строку. Подумайте об этом как о месте, чтобы поместить 5 байт. Вы можете сохранить там 5 символов без нулевого значения, но вы должны помнить, что вам нужно сделать memcpy (otherCharArray, foo, 5) и не использовать strcpy. Вы также должны знать, что у otherCharArray достаточно места для этих 5 байтов.
Ответ 7
нулевой символ используется для завершения массива. он находится в конце массива и показывает, что массив заканчивается в этой точке. массив автоматически делает последний символ нулевым символом, так что компилятор может легко понять, что массив закончен.
Ответ 8
\ 0 - оператор терминатора, который завершается, когда массив заполнен
если массив не заполнен, то \0 будет в конце массива
когда вы вводите строку, она будет считываться с конца массива