2012-04-07 3 views
2

Хорошо, поэтому я преподавал своей девушке несколько C++, и она написала программу, которая, как я думал, не сработает, но это было так. Он обращается к еще одному элементу массива, тогда есть (например, доступ к массиву [5] для массива размером 5). Является ли это экземпляром переполнения буфера? Мои мысли о том, что он пишет/получает доступ к памяти непосредственно после массива, это правильно? В основном, мой вопрос здесь ... почему это работает?Переполнение массива (почему это работает?)

#include <iostream> 

using namespace std; 

int main() 
{ 
int size; 

cout << "Please enter a size for the array." << endl; 
cin >> size; 
cout << endl; 

cout << "There are " << size << " elements in this array." << endl; 
cout << endl; 
cout << endl; 
cout << endl; 

int array[size]; 

for (int counter = 1; counter <= size; counter++) 

{ 
    cout << "Please enter a value for element " << counter << "." << endl; 
    cin >> array[counter]; 

} 

cout << endl; 
cout << endl; 


for (int counter = 1; counter <= size; counter++) 

{ 
    cout << "Element " << counter << " is " << array[counter] << "." << endl; 
    cout << endl; 

} 

cout << "*bing! :)" << endl; 
cout << endl; 


return 0; 
} 

ответ

16

Это неопределенное поведение. UB поставляется во многих вариантах. Вот несколько:

1) Это ударит вашу собаку.

2) Он переформатирует ваш жесткий диск.

3) Он будет работать без проблем.

В вашем случае с вашим компилятором и на вашей платформе и в этот конкретный день вы видите (3). Но попробуйте в другом месте, и вы можете получить (1), (2) или что-то еще полностью (скорее всего, нарушение прав доступа).

+5

Это также может вызвать сбой в матрице. – dreamlax

4

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

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

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

C/C++ опасен с границами массивов, помните об этом!

+0

Мне немного любопытно расположение памяти стека с этой переменной динамического размера. На моем ноутбуке (i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (точка 3)) gdb «print (char *) & size - (char *) & array [size] «говорит что-то вроде 84 или 96 (в зависимости от того, что я ввожу в качестве размера). Я не уверен, что между ними. valgrind даже не обнаруживает ошибку. Я разобрал функцию, но сборка была сложнее, чем я надеялся. –

0

Во-первых, размеры массива должны быть постоянными, так

int arr[size] 

не будет работать, как размер переменной, поэтому, либо зафиксировать размер массива во время компиляции или использовать динамическое выделение памяти.

т.е. либо

const int size =5; 
    int arr[size]; 

или

int arr[5]; 

или

int* arr = new int[5]; 

И ..

Принимая указатель на один элемент за конец массив гарантированно работает на C++. Это важно для многих алгоритмов, предоставляемых STL. Однако, поскольку такой указатель не указывает на элемент элемента aray, он не может использоваться для чтения и записи. С другой стороны, результат принятия адреса элемента до исходного элемента не определен и его следует избегать.

т. Е. Вы можете взять адрес этой переменной, а затем вернуться, но не можете ее использовать! !!

+0

что? Невозможно работать с элементами вне массива (ошибки сегментации). Кроме того, Size имеет тип int, который просто говорит int size = 5; int arr [5]. Этот ответ довольно ошибочен. – Kevin

+2

Массивы переменной длины - это функция C, которая была реализована как расширение во многих компиляторах на C++. 'int arr [size];' действительно C99. – dreamlax

+0

@Kevn, вы можете взять адрес элемента сразу за концом массива, но вы не можете разыменовать этот адрес (http://stackoverflow.com/questions/1021021/c-element-beyond-the-end -of-ан-массив). Однако это не актуально для этого вопроса. –

0

Здесь, вы знаете, что индекс массива начинается с 0 и Гота 5

Вы вводите ваш счетчик как 5, который начинается с 1 и Гота 5

Вы используете индекс 1 для счетчика 1, индекс 2 для бит 2 и т. д.

Индекс 0 все еще не имеет входного значения и содержит значение мусора.

1

Стек очень большая. В Windows, it's 1 MB.

Поскольку программа не делает много, массив будет выделен близко к началу стека. Это означает, что между концом массива и концом стека будет почти 1 МБ свободного пространства.

Так что это значит? Когда вы пишете за концом массива, вы просто сбиваете собственное пространство стека, а не другие программы, поэтому ОС не останавливает вас, и программа продолжает работать.

+1

Стек растет, поэтому пустая часть находится в начале массива, а не в конце. В противном случае правильно. –

+0

Да, в VS2013 я однажды объявил массив 'int a [65535]', и до тех пор, пока для 'a [i]', 'i == 84330', программа выдает исключение. –

Смежные вопросы