Top.Mail.Ru
Ответы

(C++) Выход за пределы массива. Почему при динам. выделении памяти указание размера массива игнорируется?



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

Объясните почему так и как сделать правильно

По дате
По рейтингу
Аватар пользователя
Новичок
12лет

Когда мы выделяем данным образом память под массив,
int *a = new int[2];
, то мы выделяем место в оперативной памяти под два элемента Integer, и в переменную-указатель помещается адресс первого элемента.
a[1] - будет ссылаться на участок памяти, смещенный от адреса первого элемента на размер, занимаемый одной переменной типа Integer, то есть на 4 байта.
a[2] - смещение на 8 байт, a[3] - на 12 байт, a[4] - на 16 байт и т. д
А вот чтооо будет на этом участке памяти - не известно, может так выйти, что ты затрёшь значения других переменных. . Поэтому надо контролировать выход за границы массива вручную))

Источник: Прочитай про указатели в языке C++
Аватар пользователя
Просветленный
12лет

Как говорится, в С/С++ есть тысяча способов выстрелить себе в ногу. Вы попали на один из них. Вообщето это анахронизм, работать смассивами нужно через vector.Просто и безопасно.
vector < int > a;
a.push_back(2);
a.push_back(1);
a.push_back(3);
a.push_back(4);
a.push_back(5);
for( i = 0; i < a.size(); i++ )
cout < < a [ i ] < < " ";
Для чего же тогда C++ придумали?

Аватар пользователя
Искусственный Интеллект
12лет

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

Аватар пользователя
Профи
12лет

Правильно будет не совать нос дальше 1-ого индекса в этом случае.

int *a = new int[2];
На деле выделяется больше памяти, вероятно, 24 байта - 8 байт под 2 int'а и ещё 16 для проверки выхода за границы. Лишние байты можно использовать на своё усмотрение, но это не правильно. Есть функция, которая проверяет состояние кучи через проверку этих лишних байт (HeapValidate), и если ты какие-то из них перезаписал, она вернёт значение, свидетельствующее об ошибке.

PS
char *c = new char('a'); // Выделится в общем, вероятно, 1(наш чар) + 16(проверка) + 7(выравнивание) = 24 байт. (кратно 8)
char *c = new char[10]; // 10 + 16 + 6 = 32 (кратно 8)

Аватар пользователя
Мыслитель
12лет

Так можно записать данные в соседний массив, который находится в памяти после данного массива. (под массивом понимается не только "массив", но и просто экземпляр класса и всё, что угодно, выделенное из кучи) . То есть, грубо выражаясь, возможна такая ситуация:
int *a;
int *b;
a = new int;
b = new int;
b[0] = 2;
a[1] = 1111;
cout<< b[0];
и может выводится 1111, а не 2. В реальности, может затереться (а может и нет - зависит от реализации выделителя) служебная информация, и следующие new\delete приведут к ошибке. Так же, если выход за предел массива приходится на незанятую область виртуальной памяти (т. е. в адресном пространстве там "дырка") или атрибуты страницы памяти не позволяют с ней делать заданное действие, то тоже будет ошибка.

>> как сделать правильно
не выходить за пределы массива или сделать класс-обёртку, который будет проверять индекс и выбрасывать исключение, если тот выходит за предел массива или заходит за начало массива (отрицательный индекс)