Нет выхода за границы - PullRequest
       2

Нет выхода за границы

11 голосов
/ 04 февраля 2012

У меня есть этот код на 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. Почему я не получаю исключение за пределами границ (ошибка сегментации), хотя я явно превышаю емкость массива?

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

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

Ответы [ 8 ]

23 голосов
/ 04 февраля 2012
  1. C не проверяет границы массива.Ошибка сегментации произойдет, только если вы попытаетесь разыменовать указатель на память, доступ к которой у вашей программы отсутствует.Простое прохождение конца массива вряд ли вызовет такое поведение.Неопределенное поведение - только это - неопределенное.Может показаться, что выглядит нормально , но вы не должны полагаться на его безопасность.
  2. Ваша программа вызывает неопределенное поведение, обращаясь к памяти после конца массива.В этом случае, похоже, что одна из ваших str[i] = c записей перезаписывает значение в i.
  3. C ++ имеет те же правила, что и C в этом случае.
6 голосов
/ 04 февраля 2012

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

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

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

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

3 голосов
/ 04 февраля 2012

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

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

1 голос
/ 04 февраля 2012

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

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

1 голос
/ 04 февраля 2012

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

1 голос
/ 04 февраля 2012

Поскольку C / C ++ не проверяет границы.

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

type value = *(arr + index);

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

0 голосов
/ 14 декабря 2018

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

0 голосов
/ 05 сентября 2015

Вы должны скомпилировать так:

gcc -fsanitize=address -ggdb -o test test.c

Более подробная информация здесь.

...