Есть ли опасность в вызове free () или delete вместо delete[]? [дубликат]

Возможные Дубликаты:
( POD )освобождение памяти : delete[] равно delete ?

тут delete освободить элементы за пределами первого в массиве?

char *s = new char[n];
delete s;

имеет ли это значение в приведенном выше случае, видя, как все элементы s выделяются смежно, и это не должно быть возможно delete только часть массива?

для более сложных типов, будет delete вызвать деструктор объектов за пределами первого?

Object *p = new Object[n];
delete p;

как delete[] вывести количество Objects за первым, не означает ли это, что он должен знать размер выделенной области памяти? Что делать, если область памяти была выделена с некоторым выступом по соображениям производительности? Например, можно предположить, что не все распределители обеспечат гранулярность одного байта. Тогда любое конкретное распределение может превышать требуемый размер для каждого элемента на целое элемент или больше.

для примитивных типов, таких как char, int, есть ли разница между:

int *p = new int[n];
delete p;
delete[] p;
free p;

за исключением маршрутов, принятых соответствующими вызовами через delete ->free оборудование для освобождения?

10 ответов


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

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

delete[] использует специфичные для компилятора служебные данные для определения количества элементов. Обычно больший блок выделяется, когда , номер сохраняется в начале, и вызывающему присваивается адрес за сохраненным номером. Во всяком случае delete[] полагается на блок, выделяемый new[], а не что-нибудь еще. Если вы соедините что-нибудь, кроме new[] С delete[] или наоборот, вы сталкиваетесь с неопределенным поведением.


прочитайте FAQ:16.3 могу ли я освободить () указатели, выделенные new? Можно ли удалить указатели, выделенные с помощью malloc ()?

имеет ли значение в приведенном выше случае, поскольку все элементы s распределены смежно, и не должно быть возможно удалить только часть массива?

Да, это делает.

Как удалить[] вывести количество объектов за пределами первого, не означает ли это, что он должен знать размер выделенная область памяти?

компилятор должен знать. См.FAQ 16.11

потому что компилятор хранит эту информацию.

Я имею в виду, что компилятор нуждается в другом deletes для создания соответствующего кода бухгалтерского учета. Надеюсь, теперь все ясно.


Да, это опасно!

не делайте этого!

это приведет к сбоям программы или даже худшему поведению!

для объектов, выделенных с new вы должны использовать delete;

для объектов, выделенных с new [] вы должны использовать delete [];

для объектов, выделенных с malloc() или calloc() вы должны использовать free();

будет известно также, что для всех этих случаев его незаконно удалить/освободить уже удаленный/освобожденный указатель во второй раз. free также не может вызываться с null. зову delete/delete[] С NULL является законным.


Да, существует реальная практическая опасность. Даже детали реализации в сторону, помните, что operator new/operator delete и operator new[]/operator delete[] функции можно заменить совершенно независимо. По этой причине разумно думать о new/delete, new[]/delete[], malloc/free etc. как разные, совершенно независимые методы выделения памяти, которые не имеют абсолютно ничего общего.


Raymond Chen (разработчик Microsoft) имеет углубленную статью, охватывающую масштабирование против векторных удалений, и дает некоторый фон для различий. См.:

http://blogs.msdn.com/oldnewthing/archive/2004/02/03/66660.aspx


удалить освобождает элементы за первым в массиве?

нет. delete освободит только первый элемент независимо от того, на каком компиляторе вы это сделаете. В некоторых случаях это может сработать, но это случайность.

имеет ли это значение в приведенном выше случае, поскольку все элементы s выделены смежно, и не должно быть возможности удалить только часть массива?

зависит от того, как память марке так же свободна. Опять же реализация зависит.

для более сложных типов удалить вызов деструктора объектов за пределами первого?

нет. Попробуйте это:

#include <cstdio>

class DelTest {
    static int next;
    int i;
public:
    DelTest() : i(next++) { printf("Allocated %d\n", i); }
    ~DelTest(){ printf("Deleted %d\n", i); }
};

int DelTest::next = 0;

int main(){
    DelTest *p = new DelTest[5];
    delete p;
    return 0;
}

Как удалить[] вывести количество Объекты за пределами первого, не это означает, что он должен знать размер выделенная область памяти?

да, размер хранится в некотором месте. Где он хранится, зависит от реализация. Например, распределитель может хранить размер в заголовке, предшествующем выделенному адресу.

Что делать, если область памяти была выделено с некоторым свесом для причины производительности? Например, один можно предположить, что не все распределители обеспечит детализацию один байт. Затем какой-либо конкретной ассигнования могут превышать требуемые размер для каждого элемента в целом элемент или больше.

именно по этой причине возвращаемый адрес выполняется для выравнивания по границам слов. "Свес" можно увидеть с помощью оператора sizeof и применяется к объектам в стеке.

для примитивных типов, таких как char, int и есть ли разница между ...?

да. malloc и new могут использовать отдельные блоки памяти. Даже если бы это было не так, рекомендуется не предполагать, что они одинаковы.


Это неопределенное поведение. Следовательно, Ансер: да, может быть опасность. И невозможно точно предсказать, что вызовет проблемы. Даже если это сработает один раз, сработает ли это снова? Зависит ли это от типа? Количество элементов?


для примитивных типов, таких как char, int, есть ли разница между:

Я бы сказал, что вы получите неопределенное поведение. Так что не стоит рассчитывать на стабильное поведение. Вы всегда должны использовать пары new/delete, new[]/delete[] и malloc/free.


хотя может показаться, что вы можете смешивать new[] и free или delete вместо delete [], это в предположении о том, что компилятор является довольно упрощенным, т. е. что он всегда будет использовать malloc() для реализации выделения памяти для new[].

проблема в том, что если ваш компилятор имеет достаточно умный оптимизатор, он может увидеть, что нет "delete []", соответствующего новому[] для созданного вами объекта. Поэтому он может предположить, что он может получить память для него из любого места, включая стек, чтобы сэкономить стоимость вызова реального malloc () для нового[]. Затем, когда вы пытаетесь вызвать free () или неправильный вид удаления на нем, он, вероятно, будет неисправен.


Шаг 1 Прочитайте это:что такое разница между новым удалить и malloc-free

вы смотрите только на то, что видите на стороне разработчика.
То, что вы не рассматриваете, - это то, как std lib выполняет управление памятью.

первое отличие заключается в том, что new и malloc выделяют memroy из двух разных областей памяти (New из FreeStore и malloc из кучи (не фокусируйтесь на именах, которые они оба в основном кучи, они просто есть официальные названия из стандарта)). Если вы выделяете из одного и де-выделяете другому, вы испортите структуры данных, используемые для управления памятью (нет гарантии, что они будут использовать одну и ту же структуру для управления памятью).

когда вы выделяете такой блок:

int*   x= new int; // 0x32

память может выглядеть так: это, вероятно, не будет, так как я сделал это, не думая, что трудно.

Memory   Value      Comment
0x08     0x40       // Chunk Size  
0x16     0x10000008 // Free list for Chunk size 40
0x24     0x08       // Block Size
0x32     ??         // Address returned by New.
0x40     0x08       // Pointer back to head block.
0x48     0x0x32     // Link to next item in a chain of somthing.

дело в том, что в выделенный блок, а не только int, выделенный для обработки управления памятью.

в стандарте не указано, как это делается, потому что (в стиле C/C++) они не хотели, чтобы у производителей компиляторов/библиотек была возможность реализовать наиболее эффективный метод управления памятью для архитектуры.

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

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