Ссылаясь на http://www.cplusplus.com/doc/tutorial/dynamic/, говорится, что new
оператор возвращает указатель на начало выделенного нового выделенного блока памяти. Предположим еще раз, что любой указатель имеет функцию арифметики указателя, которая может сделать так, что p + 1
указывает на следующую позицию в памяти, если p
является указателем. Исходя из вышеприведенных предположений, я попытался сделать следующий блок,
#include <iostream>
using namespace std;
int main(){
int * ptr = (new int [4]) + 1;
ptr[3] = 2;
cout << ptr[3] << endl;
/*Before delete the dynamic allocated memory,
it should be necessary to let ptr point to the
beginning of it, add one line to do that:
--ptr;*/
delete [] ptr;
}
Я ожидал, что ptr[3]
должен выходить за пределы диапазона выделенной памяти. Однако результат все же дает 2
. Почему арифметика указателя становится бесполезной? Или это действительно new Type
возвращает объект *Type
?
Спасибо за любую помощь.
Я ожидал, что
ptr[3]
должен выходить за пределы диапазона выделенной памяти.
Это действительно так. Вы должны быть осторожны, чтобы не делать этого, поскольку (как вы заметили), что ошибка, которая часто не может быть обнаружена. Инструменты динамического анализа, такие как valgrind, могут помочь диагностировать эти проблемы, но не идеально.
Однако результат все же дает 2.
Это означает, что доступная память была недоступна за пределами массива, поэтому не удалось обнаружить недопустимый доступ. Вместо этого он перезаписал память, которая не принадлежала массиву - возможно, она не использовалась, или, возможно, вы перезаписали некоторые другие данные, что позже приведет к ошибочным ошибкам.
Почему арифметика указателя становится бесполезной?
Это полезно как любая арифметика указателя. Арифметика указателя не может обнаружить конец массива, поэтому вы получаете неопределенное поведение, если оно выйдет за пределы диапазона.
Или это действительно
new Type
возвращает объект*Type
?
new Type[]
возвращает указатель Type*
на выделенный массив. Это ведет себя так же, как и любой другой указатель, поэтому добавление одного дает вам указатель на второй элемент.
delete [] ptr
также дает неопределенное поведение, поскольку ptr
больше не указывает на начало выделенного массива.
Выход за пределы границы - неопределенное поведение.
Вы можете получить разные результаты при компиляции с помощью компилятора differet, разных флагов компиляции, даже работающих в разное время. Компьютер может сдуться, и ваш монитор может гореть.
Возврат правильного значения является одним из неопределенных поведений тоже.
int * ptr = new int [4];
будет указывать на ptr[0]
, поэтому
int * ptr = (new int [4]) + 1;
будет pont для ptr[1]
и вы не сможете освободить ptr
если вы не сделаете что-то вроде delete (ptr--)
;
C/C++ позволяет назначать/считывать значения в/из вне границ памяти, так как это неопределенное поведение.
Например, это действительный код:
int *array = new int[10];
array[10] = 5;
std::cout<<array[10];
Однако при этом вы можете повредить другие структуры данных, если они имеют память, расположенную рядом с вашим массивом.