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

StackOverflow https://stackoverflow.com/questions/1612031

Вопрос

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

Делает delete освободить элементы, находящиеся за пределами первого в массиве?

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

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

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

  

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

Да, это так.

  

Как delete [] может определить количество объектов, превышающих первый, не означает ли это, что он должен знать размер выделенной области памяти?

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

  

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

Я имею в виду, что компилятору нужны разные delete для генерации соответствующего кода учета. Надеюсь, теперь это понятно.

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

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

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

Для объектов, выделенных с помощью new , вы ДОЛЖНЫ использовать delete ;

Для объектов, выделенных с помощью new [] , вы ДОЛЖНЫ использовать delete [] ;

Для объектов, размещенных с помощью malloc () или calloc () , вы ДОЛЖНЫ использовать free () ;

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

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

Рэймонд Чен (разработчик Microsoft) содержит подробную статью, посвященную удалению скейлера и вектора, и дает некоторые сведения о различиях. См:

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

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

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

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

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

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

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

#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;
}

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

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

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

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

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

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

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

  

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

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

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

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

Шаг 1, прочитайте это: то, что-это-разностной-между-нового удалить-и-таНос свободной

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

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

Когда вы выделяете блок следующим образом:

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 ++, вы обнаружите, что они сильно различаются. И, таким образом, немаловажно использовать совершенно разные методы управления памятью для оптимизации под тип приложения. Это еще одна причина, по которой в C ++ предпочтительнее new, а не malloc (), поскольку она, вероятно, будет более эффективной (хотя более важной причиной всегда будет уменьшение сложности (IMO)).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top