Простое тестовое приложение:
cout << new int[0] << endl;
выходы:
0x876c0b8
Похоже, это работает. Что стандарт говорит об этом? Всегда ли законно «выделять» пустой блок памяти?
Простое тестовое приложение:
cout << new int[0] << endl;
выходы:
0x876c0b8
Похоже, это работает. Что стандарт говорит об этом? Всегда ли законно «выделять» пустой блок памяти?
Ответы:
От 5.3.4 / 7
Когда значение выражения в direct-new-таком объявителе равно нулю, функция выделения вызывается для выделения массива без элементов.
От 3.7.3.1/2
Эффект разыменования указателя, возвращаемого как запрос нулевого размера, не определен.
Также
Даже если размер запрошенного [нового] пространства равен нулю, запрос может завершиться неудачно.
Это означает, что вы можете сделать это, но вы не можете юридически (четко определенным образом на всех платформах) разыменовать полученную память - вы можете только передать ее в удаление массива - и вы должны удалить ее.
Вот интересная сноска (то есть не нормативная часть стандарта, но включенная в пояснительных целях), приложенная к предложению из 3.7.3.1/2
[32. Намерение состоит в том, чтобы оператор new () мог быть реализован путем вызова malloc () или calloc (), так что правила практически одинаковы. C ++ отличается от C тем, что требует нулевого запроса для возврата ненулевого указателя.]
new[]
с delete[]
- независимо от размера. В частности, когда вы вызываете, new[i]
вам нужно немного больше памяти, чем той, которую вы пытаетесь выделить, чтобы сохранить размер массива (который позже используется delete[]
при освобождении)
Да, это законно, чтобы выделить массив нулевого размера, как это. Но вы также должны удалить его.
int ar[0];
это незаконно, почему новое хорошо?
sizeof (type)
ожидается, что никогда не вернет ноль. См. Например: stackoverflow.com/questions/2632021/can-sizeof-return-0-zero
Что стандарт говорит об этом? Всегда ли законно «выделять» пустой блок памяти?
Каждый объект имеет уникальную идентичность, то есть уникальный адрес, который подразумевает ненулевую длину (фактический объем памяти будет незаметно увеличен, если вы запросите нулевые байты).
Если вы выделите более одного из этих объектов, вы обнаружите, что они имеют разные адреса.
operator []
также где-то хранит размер массива (см. isocpp.org/wiki/faq/freestore-mgmt#num-elems-in-new-array ). Таким образом, если реализация выделяет счетчик байтов для данных, она может просто выделить счетчик байтов и 0 байтов для данных, возвращая указатель 1-last-last.
operator new[]
должны возвращать разные указатели. Кстати, new expression
есть некоторые дополнительные правила (5.3.4). Я не смог найти никакой подсказки, что new
с размером 0 на самом деле требуется что-то выделить. Извините, я понизил голос, потому что обнаружил, что ваш ответ не отвечает на вопросы, но дает некоторые противоречивые утверждения.
new[]
реализация могла бы возвращать адреса в диапазоне, где нет сопоставленной динамической памяти, следовательно, фактически не используя какую-либо память (при использовании адресного пространства)
while(!exitRequested) { char *p = new char[0]; delete [] p; }
цикл без повторного использования указателей, может превратиться в пыль, прежде чем он может исчерпать адресное пространство, но на платформе с 32-разрядными указателями, которая будет гораздо менее разумное предположение.
Да, это совершенно законно, чтобы выделить 0
размерный блок с new
. Вы просто не можете сделать что-нибудь полезное с этим, так как для вас нет действительных данных. int[0] = 5;
незаконно
Однако я считаю, что стандарт допускает такие вещи, как malloc(0)
возвращение NULL
.
Вам все равно понадобится delete []
указатель, который вы получите от распределения.
Любопытно, что C ++ требует, чтобы оператор new возвращал законный указатель, даже когда запрашивается ноль байтов. (Требование такого странного поведения упрощает вещи в другом месте языка.)
Я обнаружил, что третье издание Effective C ++ говорит так: «Статья 51: придерживайтесь соглашения при написании нового и удалении».
Я гарантирую вам, что новый int [0] стоит вам дополнительного места, так как я его протестировал.
Например, использование памяти
int **arr = new int*[1000000000];
значительно меньше, чем
int **arr = new int*[1000000000];
for(int i =0; i < 1000000000; i++) {
arr[i]=new int[0];
}
Использование памяти второго фрагмента кода минус использование первого фрагмента кода - это память, используемая для многочисленных новых int [0].