Ошибка вне поля

У меня есть этот код в C, который берет кучу char s

#include<stdio.h> 
# define NEWLINE '\n'
int main()
{

char c;
char str[6];
int i = 0;
while( ((c = getchar()) != NEWLINE))
{
        str[i] = c;
        ++i;
        printf("%d\n", i);
}

return 0;
}

Вход: testtesttest

Вывод: 1 2 3 4 5 6 7 8 117 118 119 120

Мои вопросы:

  • Почему бы мне не получить исключение (исключение сегментации), хотя я явно превысил емкость массива?

  • Почему числа на выходе внезапно переходят на очень большие цифры?

Я попробовал это на С++ и получил то же поведение. Может кто-нибудь объяснить, в чем причина этого?

Ответ 1

  • C не проверяет границы массива. Ошибка сегментации будет возникать только в том случае, если вы попытаетесь разыменовать указатель на память, в котором у вашей программы нет разрешения на доступ. Просто пройти мимо конца массива вряд ли вызовет такое поведение. Undefined поведение - это просто - undefined. Возможно, он работает нормально, но вы не должны полагаться на его безопасность.
  • Ваша программа вызывает поведение Undefined, обращаясь к памяти за конец массива. В этом случае, похоже, что одна из ваших записей str[i] = c перезаписывает значение в i.
  • С++ имеет те же правила, что и C в этом случае.

Ответ 2

Когда вы обращаетесь к индексу массива, C и С++ не выполняют проверку привязки. Ошибки сегментации возникают только при попытке чтения или записи на страницу, которая не была выделена (или попытаться сделать что-то на странице, которая не разрешена, например, попытка записи на страницу только для чтения), но поскольку страницы обычно довольно большой (кратные несколько килобайт, в Mac OS, кратные 4 КБ), он часто оставляет вам много места для переполнения.

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

Значения, которые вы получаете, когда читаете, - это то, что происходит в этом конкретном месте. Они полностью undefined.

Если вы используете С++ (и вам повезло работать с С++ 11), стандарт определяет тип std::array<T, N>, который представляет собой массив, который знает свои границы. Метод at будет бросаться, если вы попытаетесь прочитать его конец.

Ответ 3

Потому что C/С++ не проверяет границы.

Массивы внутренне указывают на местоположение в памяти. Когда вы вызываете arr[index], что он делает, это:

type value = *(arr + index);

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

Ответ 4

C не проверяет границы массива.

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

Ответ 5

Запись внешних границ массива (на самом деле даже просто выполнение арифметики указателя/массива, даже если вы не используете результат для чтения или записи чего-либо) приводит к undefined поведению. Undefined поведение не является сообщенной или отчетной ошибкой; это мешает вашей программе вообще что-либо сделать. Это очень опасно, и вы несете полную ответственность за его устранение. C не является Java/Python/и т.д.

Ответ 6

Распределение памяти сложнее, чем кажется. Переменная "str" в этом случае находится в стеке, рядом с другими переменными, поэтому не следует нераспределенная память. Память также обычно выравнивается по слову (одно "слово" - от четырех до восьми байтов.) Возможно, вы сошлись со значением для другой переменной или с некоторым "дополнением" (пустое пространство, добавленное для поддержания выравнивания слов), или что-то еще полностью.

Как сказал Р., это поведение undefined. Условия вне границ могут вызвать segfault... или они могут привести к повреждению памяти. Если вы изменяете уже выделенную память, это не будет зависеть от операционной системы. Вот почему ошибки вне границ настолько коварны в C.

Ответ 8

C и C++, в отличие от Java, не имеют проверок границ массива. Например, если у вас есть такой массив: int myArray[2] = {1, 2} а затем позже вы напечатаете std::cout << myArray[5]; компилятор не выдаст никаких ошибок, скорее он выведет значение мусора.