Хорошо, я думаю, что мы все согласны с тем, что то, что происходит со следующим кодом, не определено, в зависимости от того, что передано,
void deleteForMe(int* pointer)
{
delete[] pointer;
}
Указатель может быть разного рода, поэтому выполнение безусловного delete[]
для него не определено. Тем не менее, давайте предположим, что мы действительно передаем указатель массива,
int main()
{
int* arr = new int[5];
deleteForMe(arr);
return 0;
}
Мой вопрос, в этом случае, когда указатель является массивом, кто это знает? Я имею в виду, с точки зрения языка / компилятора, он понятия не имеет, arr
является ли указатель массива по сравнению с указателем на одно целое. Черт возьми, он даже не знает, arr
был ли он создан динамически. Тем не менее, если я сделаю следующее вместо этого,
int main()
{
int* num = new int(1);
deleteForMe(num);
return 0;
}
ОС достаточно умен , чтобы удалить только один междунар и не идти на какой - то типа «Череда убийств», удалив остальную часть памяти за этой точкой (контраст , что с strlen
и не- \0
-завершённый строка - она будет продолжать идти до него Хиты 0).
Так чья работа помнить эти вещи? Сохраняет ли ОС какой-либо тип записи в фоновом режиме? (Я имею в виду, я понимаю, что начал этот пост, сказав, что то, что происходит, не определено, но на самом деле сценарий «убийства» не происходит, поэтому в практическом мире кто-то вспоминает.)
Ответы:
Компилятор не знает, что это массив, он доверяет программисту. Удаление указателя на одиночную
int
сdelete []
может привести к неопределенному поведению. Ваш второйmain()
пример небезопасен, даже если он не сразу падает.Компилятор должен отслеживать, сколько объектов нужно каким-либо образом удалить. Это может быть сделано путем перераспределения, достаточного для хранения размера массива. Для получения более подробной информации см. C ++ Super FAQ .
источник
Один вопрос, на который пока не даны ответы: если библиотеки времени выполнения (на самом деле не ОС) могут отслеживать количество элементов в массиве, то зачем вообще нужен
delete[]
синтаксис? Почему нельзя использовать однуdelete
форму для обработки всех удалений?Ответ на это восходит к корням C ++ как к C-совместимому языку (которым он больше не стремится). Философия Страуструпа заключалась в том, что программисту не нужно платить за любые функции, которые они не используют. Если они не используют массивы, то они не должны нести стоимость массивов объектов для каждого выделенного фрагмента памяти.
То есть, если ваш код просто делает
тогда выделенное пространство памяти
foo
не должно включать в себя дополнительные издержки, необходимые для поддержки массивовFoo
.Поскольку для переноса дополнительной информации о размере массива устанавливаются только распределения массива, вам необходимо указать библиотекам времени выполнения искать эту информацию при удалении объектов. Вот почему мы должны использовать
вместо просто
если бар это указатель на массив.
Для большинства из нас (включая меня) эта суета, связанная с несколькими дополнительными байтами памяти, кажется странной в наши дни. Но все еще есть ситуации, когда сохранение нескольких байтов (из того, что может быть очень большим количеством блоков памяти) может быть важным.
источник
Да, ОС хранит некоторые вещи в фоновом режиме. Например, если вы запустите
ОС может выделить 4 дополнительных байта, сохранить размер выделения в первых 4 байтах выделенной памяти и вернуть указатель смещения (т. е. она выделяет области памяти от 1000 до 1024, но возвращенный указатель указывает на 1004, с местоположениями 1000- 1003 хранения размера выделения). Затем, когда вызывается delete, он может просмотреть 4 байта, прежде чем указатель будет передан ему, чтобы найти размер выделения.
Я уверен, что есть другие способы отслеживания размера размещения, но это один из вариантов.
источник
for(int i = 0; i < *(arrayPointer - 1); i++){ }
Это очень похоже на этот вопрос, и в нем есть много деталей, которые вы ищете.
Но достаточно сказать, что задача ОС - не отслеживать это. Это на самом деле библиотеки времени выполнения или базовый менеджер памяти, который будет отслеживать размер массива. Обычно это делается путем выделения дополнительной памяти заранее и сохранения размера массива в этом месте (большинство используют головной узел).
Это можно увидеть в некоторых реализациях, выполнив следующий код
источник
size_t size = *(reinterpret_cast<size_t *>(pArray) - 1)
вместо этогоdelete
илиdelete[]
, вероятно, оба освободили бы выделенную память (указала на память), но большая разница в том, чтоdelete
в массиве не будет вызываться деструктор каждого элемента массива.Во всяком случае, смешивание
new/new[]
иdelete/delete[]
, вероятно, UB.источник
Он не знает, что это массив, поэтому вы должны поставить
delete[]
вместо обычного старогоdelete
.источник
У меня был похожий вопрос к этому. В C вы выделяете память с помощью malloc () (или другой аналогичной функции) и удаляете ее с помощью free (). Существует только один malloc (), который просто выделяет определенное количество байтов. Существует только одна функция free (), которая просто принимает указатель в качестве параметра.
Так почему же в C вы можете просто передать указатель на free, а в C ++ вы должны указать, является ли это массивом или единственной переменной?
Я узнал, что ответ связан с деструкторами классов.
Если вы выделите экземпляр класса MyClass ...
И удалить его с помощью delete, вы можете получить деструктор только для первого экземпляра MyClass. Если вы используете delete [], вы можете быть уверены, что деструктор будет вызываться для всех экземпляров в массиве.
Это важное отличие. Если вы просто работаете со стандартными типами (например, int), вы действительно не увидите эту проблему. Кроме того, вы должны помнить, что поведение при использовании delete для new [] и delete [] для new не определено - оно может работать не одинаково на всех компиляторах / системах.
источник
Это зависит от времени выполнения, которое отвечает за распределение памяти, так же, как вы можете удалить массив, созданный с помощью malloc в стандартном C, используя free. Я думаю, что каждый компилятор реализует это по-своему. Один из распространенных способов - выделить дополнительную ячейку для размера массива.
Однако среда выполнения не достаточно умна, чтобы определить, является ли она массивом или указателем, вы должны сообщить об этом, и если вы ошибаетесь, вы либо не удаляете корректно (например, ptr вместо массива), либо в конечном итоге вы берете несвязанное значение для размера и наносите значительный ущерб.
источник
Один из подходов для компиляторов - выделить немного больше памяти и сохранить количество элементов в элементе head.
Пример того, как это можно сделать: здесь
Компилятор выделит размер (int) * 5 байт.
Будет хранить
4
в первыхsizeof(int)
байтахи установить
i
Таким образом,
i
указывает на массив из 4 элементов, а не 5.И
будет обработан следующим образом
источник
Семантически обе версии оператора удаления в C ++ могут «съесть» любой указатель; однако, если указан указатель на один объект
delete[]
, то будет получен UB, означающий, что может произойти что угодно, в том числе системный сбой или вообще ничего.C ++ требует, чтобы программист выбрал правильную версию оператора удаления в зависимости от предмета освобождения: массив или отдельный объект.
Если компилятор может автоматически определить, является ли указатель, переданный оператору удаления, массивом указателей, тогда в C ++ будет только один оператор удаления, что будет достаточно для обоих случаев.
источник
Согласитесь, что компилятор не знает, массив это или нет. Это зависит от программиста.
Иногда компилятор отслеживает, сколько объектов нужно удалить, перераспределяя их достаточно для хранения размера массива, но это не всегда необходимо.
Чтобы получить полную спецификацию при выделении дополнительной памяти, обратитесь к C ++ ABI (как реализованы компиляторы): Itanium C ++ ABI: новые файлы cookie для оператора массива
источник
Вы не можете использовать delete для массива, и вы не можете использовать delete [] для не-массива.
источник
«неопределенное поведение» просто означает, что спецификация языка не дает никаких гарантий относительно того, что произойдет. Это не обязательно означает, что случится что-то плохое.
Здесь обычно два слоя. Базовый менеджер памяти и реализация C ++.
Обычно диспетчер памяти запоминает (помимо прочего) размер выделенного блока памяти. Это может быть больше, чем запрашиваемая реализация C ++. Обычно диспетчер памяти будет хранить свои метаданные перед выделенным блоком памяти.
Реализация C ++, как правило, запоминает размер массива только в том случае, если это необходимо для собственных целей, как правило, потому, что тип имеет нетривальный деструктор.
Таким образом, для типов с тривиальным деструктором реализация «delete» и «delete []» обычно одинакова. Реализация C ++ просто передает указатель на базовый менеджер памяти. Что-то вроде
С другой стороны, для типов с нетривиальным деструктором «delete» и «delete []» могут быть разными. «delete» будет выглядеть примерно так (где T - тип, на который указывает указатель)
Хотя «удалить []» будет что-то вроде.
источник
переберите массив объектов и вызовите деструктор для каждого из них. Я создал этот простой код, который перегружает выражения new [] и delete [] и предоставляет шаблонную функцию для освобождения памяти и вызова деструктора для каждого объекта при необходимости:
источник
просто определите деструктор внутри класса и выполните ваш код с обоим синтаксисом
в соответствии с выходом вы можете найти решения
источник
Ответ:
int * pArray = new int [5];
int size = * (pArray-1);
Вышеуказанное неверно и выдает неверное значение. «-1» считает элементы В 64-битной ОС Windows правильный размер буфера находится в Ptr - 4 байта адреса
источник