Является ли хранилище объектов разрешено неявно или явно повторно использовать, если срок службы закончился?

Рассмотрим следующий пример:

// create some storage
alignas(int) char buffer[2 * sizeof(int)];

// new object of type int at the storage of buffer, the int pointed
// to by p begins its lifetime here, buffer lifetime is over
int* p = new (buffer) int{42};

// some entirely unrelated int
int j = 17;

Допускается ли это для другого хранилища в конце buffer, часть, которая еще не была использована новым объектом int, на который указывает p, должна быть снова открыта в стек и неявно повторно использована последующие объекты автоматического хранения? Другими словами, допустимая реализация позволяет иметь &j == p+1?


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

alignas(int) char buffer[2 * sizeof(int)];
int* p = new (buffer) int{42};
int* q = new (p+1) int{6}; 

То есть, оба int указываются на p и q еще в течение их жизней?

Ответ 1

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

Пример

int a;
// do some stuff with a.

int b; 
// do some stuff with b.

Предполагая, что как a, так и b используют стек, они могут совместно использовать адрес, поскольку компилятор может сказать свое различное использование.

Создание элемента в стеке - это простая инструкция

sub stackPointer, #AmountOfSpaceNeeded

В этом примере объем пространства не влияет на скорость выделения.

Создание элемента в куче требует поиска некоторой неиспользуемой области памяти или "сопоставления" некоторого нового адресного пространства внутри и требует много раз стоимости одного (или двух с указателем фрейма) инструкций для использования кучи.

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

Таким образом, оптимизация использования местоположения в куче имеет мало значения для сохранения хранилища в стеке, поскольку

  • Не все функции используют кучу
  • Нельзя экономить время при использовании только кучи
  • Механизмы кода за кучей могут выполнять измерение (обнаружение переполнения буфера), что приводит к тому, что компилятор нарушает правило as-if.
  • Помимо определения того, что пространство не используется, компилятору также необходимо определить, что срок службы памяти правильный, поскольку объекты кучи могут быть уничтожены до завершения функции/блока кода.

Ответ 2

Да, это совершенно нормально.

alignas(int) char buffer[2 * sizeof(int)];

Выделите 8 байтов в стеке.

 int* p = new (buffer) int{42};
 int* q = new (p+1) int{6};

Эти новые операторы не выделяют память для построения объектов, вместо этого они используют буфер.

Но обратите внимание, что время жизни этих объектов будет временем жизни буферной переменной. Когда буфер будет уничтожен, память, на которую ссылаются p и q, будет уничтожена.

Также в общем случае вы можете вызвать деструктор вручную до того, как буфер будет уничтожен.

Таким образом, ответ на вопрос "Является ли хранилище объектов разрешено неявно или явно использовать его, если срок службы закончился?" Нет. Когда срок службы буфера закончился, небезопасно продолжать использовать его память, потому что позже он будет использоваться для других переменных стека.

P.S. Я считаю, что вы понимаете, что в вашем случае будет легче управлять памятью следующим образом:

int buffer[2];
int* p = &buffer[0];
int* q = &buffer[1];

image