Есть уже много ответов, объясняющих неопределенное поведение, которое возникает от чтения мимо границ массива довольно хорошо. Но есть еще одна (потенциальная) проблема, о которой никто еще не упомянул.
Если ваш массив может содержать 0 в качестве допустимого значения, вы допустите неправильный размер. То есть,
void
f(float * p)
{
do
{
printf("% .01f\n", *p);
}
while(*p++);
}
int
main()
{
float p[] = {-1.0f, -0.5f, 0.0f, 0.5f, 1.0f};
f(p);
return 0;
}
Выведет
-1.00
-0.50
0.00
и «выходят» остальные элементы. Хотя этот конкретный пример не вызывает неопределенное поведение, это может быть не то, что вы ожидали.
Я предполагаю, что вы столкнулись с этим шаблоном со строковым программированием. По соглашению, мы требуем массив символов, который предназначен для представления текстовой строки
- не содержат байты NUL и
- прекращается путем размещения байт NUL после последнего байта.
Если оба требования выполнены, делая
void
roll_all_over_it(char * string)
{
while(*string);
{
putchar(*string++);
}
}
безопасен по контракту.
Это делает использование строк немного более удобным, так как нам не нужно поддерживать целое число рядом с каждой строкой, чтобы отслеживать ее длину. С другой стороны, в нем сделано немало (небрежно написанных) программ, уязвимых для переполнения буфера, и есть другие проблемы, которые возникают из этого предположения. Смотри, например, обсуждение fgets
в GNU libc manual:
Предупреждение: Если входные данные имеют нулевой символ, вы не можете сказать. Поэтому не используйте fgets
, если вы не знаете, что данные не могут содержать нуль. Не используйте его для чтения файлов, отредактированных пользователем, потому что, если пользователь вставляет нулевой символ, вы должны либо обработать его должным образом, либо напечатать ясное сообщение об ошибке. Мы рекомендуем использовать getline
вместо fgets
.
Короткий ответ: да. Вы можете создать указатель на один элемент за концом массива, но разыменование этого указателя дает неопределенное поведение. –
Хорошо, если вы * знаете *, что в вашем массиве будет 0 (т. Е. * Require * это по контракту вашей функции), то он будет таким же безопасным или небезопасным, как и полагающийся на параметр длины, предоставленный пользователем. В отсутствие такого контракта это, конечно, безумие. – 5gon12eder
О, и в вашем случае вам повезло, что в массиве было 0. Предполагая, что это полагается на неопределенное поведение. – 5gon12eder