Является ли выделение памяти, а затем освобождение представляет собой побочный эффект в программе на C ++? - PullRequest
11 голосов
/ 08 июля 2011

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

delete[] new char[10];

Ничего полезного.Но имеет ли это побочный эффект?Считается ли выделение кучи сразу после освобождения побочным эффектом?

Ответы [ 5 ]

10 голосов
/ 08 июля 2011

Это до реализации.Выделение и освобождение памяти не является «наблюдаемым поведением», если реализация не решит, что это наблюдаемое поведение.

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

Кроме того, я не могу вспомнить, как определенные пользователем глобальные new[] и delete[] работают [Мне напомнили].Поскольку код может вызывать определения этих вещей в другом определяемом пользователем TU, который позднее будет связан с этим TU, вызовы не могут быть оптимизированы во время компиляции.Они могут быть удалены во время соединения, если выяснится, что операторы не определены пользователем (хотя тогда применимы все, что касается библиотеки времени выполнения), или определены пользователем, но не имеют побочных эффектов (если пара из нихвстроен - это кажется довольно неправдоподобным в разумной реализации, на самом деле [*]).

Я почти уверен, что вам не разрешено полагаться на исключение из new[], чтобы «доказать»,не закончилась памятьДругими словами, то, что new char[10] не выбрасывает на этот раз, не означает, что оно не сработает после того, как вы освободите память и попробуйте снова.И только потому, что это бросило в прошлый раз, и с тех пор вы ничего не освободили, это не значит, что это бросит в этот раз.Таким образом, я не вижу никакой причины на том основании, почему два вызова не могут быть устранены - нет ситуации, когда стандарт гарантирует, что new char[10] сгенерирует, поэтому нет необходимости для реализации выяснять, будет ли это или нет,Насколько вам известно, какой-то другой процесс в системе освободил 10 байтов непосредственно перед вызовом new[] и выделил его сразу после вызова delete[].

[*]

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

// new[]
global_last_allocation = global_next_allocation;
global_next_allocation += 10 + sizeof(size_t);
char *tmp = global_last_allocation;
*((size_t *)tmp) = 10; // code to handle alignment requirements is omitted
tmp += sizeof(size_t);

// delete[]
tmp -= sizeof(size_t);
if (tmp == global_last_allocation) {
    global_next_allocation -= 10 + *((size_t*)tmp);
}

, который почти все можно удалить, если предположить, что ничто не является изменчивым, просто оставив global_last_allocation = global_next_allocation;.Вы также можете избавиться от этого, сохранив предыдущее значение last в заголовке блока вместе с размером и восстановив это предыдущее значение после освобождения последнего выделения.Это довольно экстремальная реализация распределителя памяти, однако вам понадобится однопоточная программа с быстрым программистом, который уверен, что программа не использует больше памяти, чем было доступно для начала.

3 голосов
/ 08 июля 2011

new[] и delete[] могут в конечном итоге привести к системным вызовам. Кроме того, new[] может бросить. Имея это в виду, я не вижу, как последовательность new-delete может быть законно признана свободной от побочных эффектов и оптимизирована.

(Здесь я предполагаю, что перегрузка new[] и delete[] не задействована.)

2 голосов
/ 08 июля 2011

Нет.Он не должен быть удален компилятором и не должен рассматриваться как побочный эффект.Рассмотрим ниже:

struct A {
  static int counter;
  A () { counter ++; }
};

int main ()
{
  A obj[2]; // counter = 2
  delete [] new A[3]; // counter = 2 + 3 = 5
}

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

A(); // simple object allocation and deallocation

не оптимизировано.

1 голос
/ 08 июля 2011

new и delete в обычных случаях приводят к вызовам диспетчера кучи операционной системы, и это вполне может иметь некоторые побочные эффекты. Если ваша программа имеет только один поток, код, который вы показываете , должен не иметь побочных эффектов, но мои наблюдения на окнах (в основном на 32-битных платформах) показывают, что, по крайней мере, большие выделения и последующие освобождения часто приводят к «куче» раздор, даже если вся память была освобождена. Смотрите также этот пост на MSDN .

Более сложные проблемы могут возникнуть, если запущено несколько потоков. Хотя ваш код освобождает память тем временем, другой поток может выделить (или освободить) память, и ваше распределение может привести к дальнейшей фрагментации кучи. Это все довольно теоретически, но иногда может возникнуть.

Если ваш вызов new завершится неудачно, в зависимости от используемой версии компилятора, вероятно, будет выдано исключение bad_alloc , что, конечно, будет иметь побочные эффекты.

1 голос
/ 08 июля 2011

Компилятор не может видеть реализацию delete [] и new [] и должен предполагать, что это так.

Если вы реализовали delete [] и new [] над ним, компилятор может встроить / оптимизировать прочьэти функции полностью.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...