Почему эта программа не потребляет память? - PullRequest
1 голос
/ 22 мая 2011

Меня беспокоит, что на самом деле делает delete [], поэтому я просто попробовал какой-то код и был шокирован результатами

Тест № 1:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete []d;
    }
}

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

Тест № 2:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete [](d + 5);
    }
}

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

Тест № 3:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete []d;
        *d=1;
    }
}

Это вызвало нарушение прав доступа, как и ожидалось (кажется, что вся память удалена после delete []d).

Тест № 4:

int main()
{
    int *d;
    while(true)
    {
        d = new int[10];
        delete [](d+5);
        *d=1;
    }
}

Это было самое удивительное, хотя программа не потребляет памяти, хотя она и не потребляет никаких нарушений доступа, мне просто интересно, где * d хранит свои данные?

(Кстати, все программы компилируются без оптимизации!)

Теперь главный вопрос:

Что, если я выделил массив и работал с половиной его, не могу ли я случайно освободить эту половину и сохранить другую половину?

Ответы [ 5 ]

5 голосов
/ 22 мая 2011

меня беспокоит то, что на самом деле делает delete [] 1003 *

Вы не должны беспокоиться о том, что на самом деле делает delete[].По сути, это черный ящик с определенными правилами правильного использования.Единственный раз, когда вам нужно беспокоиться о том, что он должен делать на самом деле, - это если вы пишете компилятор или среду выполнения C ++ (например, операционные системы и т. Д.)

В отношении этих "определенных правил о том, какиспользуйте его правильно ", тесты № 2 и № 4 вызывают неопределенное поведение:

Стандарт ISO C ++ 2003 5.3.5 Удалить [expr.delete]

1 Оператор удаления выражения уничтожает наиболее производный объект (1.8) или массив, созданный новым выражением.

    delete-expression:
        ::opt delete cast-expression
        ::opt delete [ ] cast-expression

Первая альтернатива для немассив объектов, а второй для массивов.Операнд должен иметь тип указателя или тип класса, имеющий одну функцию преобразования (12.3.2) в тип указателя.Результат имеет тип void.

2 Если операнд имеет тип класса, операнд преобразуется в тип указателя путем вызова вышеупомянутой функции преобразования, и используется преобразованный операндвместо исходного операнда для оставшейся части этого раздела.В любом из вариантов, если значение операнда удаления является нулевым указателем, операция не имеет никакого эффекта.В первом варианте (объект удаления) значение операнда удаления должно быть указателем на объект, не являющийся массивом, или указателем на подобъект (1.8), представляющий базовый класс такого объекта (раздел 10).Если нет, поведение не определено. Во втором варианте (массив массива) значение операнда удаления должно быть значением указателя, полученным из предыдущего выражения new-выражения массива.Если нет, поведение не определено. [Примечание: это означает, что синтаксис выражения удаления должен соответствовать типу объекта, выделенного new, а не синтаксису выражения new.]

«Неопределенное поведение» означает, что может случиться что угодно , включая поведение, которое вы только что описали.

Эти выражения, которые вы имеете в Тестах № 2 и #4 нарушают 5.3.5 / 2 и приводят к неопределенному поведению (Тест № 3 также вызывает неопределенное поведение, но по другой причине).

d = new int[10];
delete [](d + 5);

Строка delete[] нарушает 5.3.5/ 2 потому что значение указателя, которое вы передаете delete[], не совпадает со значением, которое было вам дано из new int[].

Так что если команда new int[] дает вам 0xA01D2CE9, и вы передаете 0xA01D2CE9 + 5 до delete[], вы не можете рассуждать или предсказывать, что произойдет, потому что вы нарушили правила языка .Что на самом деле произойдет, будет зависеть от того, как компилятор и / или операционная система обрабатывает new[] и delete[].Это может варьироваться от ничего плохого до полной поломки вашей системы, и везде между ними.

Другими словами, просто не пишите такие вещи, как delete [](d + 5);.

2 голосов
/ 22 мая 2011

Когда вы говорите «не потребляет никакой памяти», вы говорите, когда просматриваете монитор производительности в стиле «менеджер задач»? Потому что, если это так, 40 байтов не будут отображаться как «использование памяти» ... вам придется выделить гораздо больше памяти, чтобы это отображалось в большинстве стандартных мониторов производительности процессов.

1 голос
/ 22 мая 2011
delete [](d + 5);

Звучит так, как будто вы ожидаете, что это освободит только часть памяти, выделенную new int[10].

Это не так, это вызывает неопределенное поведение и можетвызвать что-либо.

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

Другое соображение заключается в том, что new int[10] не инициализирует выделенную память, поэтому операционная система может просто выделить некоторое адресное пространство и не нуждается в резервировании выделения какими-либо физическимиместо хранения.Это означает, что даже если вы вызываете new int[10] в цикле без delete[], во многих инструментах мониторинга памяти может не наблюдаться увеличение использования памяти, даже до того момента, когда new[] выдает исключение std::bad_alloc, когда вы исчерпываетелогическое адресное пространство(Это может занять некоторое время, просто выделив 10 байтов за раз.)

1 голос
/ 22 мая 2011
delete [](d + 5);

(из вашего теста # 2) - это определенно то, что вы должны не делать, это повредит память, если не segfault.Это на самом деле segfaults на моей платформе.

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

0 голосов
/ 22 мая 2011

Запускаете ли вы эти тесты под сборкой релиза?Вы смотрели на сгенерированные инструкции по сборке?Как вы измеряете потребление памяти?Вы только выделяете 40 байтов, а затем немедленно очищаете его.

Кроме того, выполнение чего-то вроде delete [](d + 5) вызывает неопределенное поведение.Вам нужно передать указатель, возвращаемый operator new, чтобы delete работал правильно.

...