В чем причина возврата уникальных адресов для выделения нулевого размера в C++?
Предпосылки: стандарт C11 говорит о malloc (7.20.3 Функции управления памятью):
Если размер запрашиваемого пространства равен нулю, поведение определяется реализацией: возвращается нулевой указатель или поведение такое, как если бы размер был некотором ненулевым значением, за исключением того, что возвращаемый указатель не должен использоваться для доступа к объекту.
То есть, как я вижу, malloc
всегда преуспевает для выделения нулевого размера, поскольку единственное, что вы можете сделать с указателем нулевого размера, - это вызов другой функции выделения памяти, такой как free
с ней:
- если
malloc
возвращаетNULL
,free(NULL)
в порядке, так что это можно считать успешным, - если он возвращает какое-то другое значение, это также приводит к успеху (потому что это не
NULL
), единственным условием является то, чтоfree
от значения также должен работать.
Кроме того, C11 (также 7.20.3) не указывает, что адрес, возвращаемый из malloc, должен быть уникальным, только чтобы они указывали на непересекающиеся области памяти:
Указатель возвращается, если выделение успешно выполняется соответствующим образом, чтобы его можно было назначить указателю на любой тип объекта, а затем использовать для доступа к такому объекту или массиву таких объектов в выделенном пространстве (пока пространство явно не освобождено), Время жизни выделенного объекта простирается от выделения до освобождения. Каждое такое распределение должно давать указатель на объект, не связанный с каким-либо другим объектом.
Все объекты нулевого размера являются непересекающимися AFAICT, и это будет означать, что malloc
может возвращать один и тот же указатель для множественных распределений нулевого размера (например, NULL
будет в порядке) или разных указателей каждый раз или одного и того же указателя для некоторых и т.д.
Затем C++ 98 объединил две функции распределения памяти:
void* operator new(std::size_t size);
void* operator new(std::size_t size, std::align_val_t alignment);
Обратите внимание, что эти функции возвращают необработанную память: они не создают или не инициализируют какие-либо объекты AFAICT любого типа.
Вы называете их следующим образом:
#include <iostream>
#include <new>
int main() {
void* ptr = operator new(std::size_t{0});
std::cout << ptr << std::endl;
operator delete(ptr, std::size_t{0});
return 0;
}
Раздел [new.delete.single]
в стандарте C++ 17 объясняет их, но гарантия ключа, как я вижу, приведена в [basic.stc.dynamic.allocation]
:
Даже если размер запрашиваемого пространства равен нулю, запрос может выйти из строя. Если запрос завершается успешно, возвращаемое значение должно быть значением ненулевого указателя (7.11) p0, отличным от любого ранее возвращенного значения p1, если только это значение p1 не было впоследствии передано удалению оператора. Кроме того, для функций распределения библиотек в пунктах 21.6.2.1 и 21.6.2.2 p0 должен представлять адрес блока хранения, не связанного с хранилищем, для любого другого объекта, доступного для вызывающего. Эффект косвенности через указатель, возвращаемый как запрос на нулевой размер, не определен.
То есть, они всегда должны возвращать отдельные указатели на успех. Это немного изменится с malloc
.
Мой вопрос: в чем причина этого изменения? (то есть за возвратом уникальных адресов для выделения нулевого размера в C++)
В идеале ответ был бы просто ссылкой на документ (или какой-то другой источник), который изучал альтернативы и мотивировал их семантику. Обычно я обращаюсь к Design and Evolution C++ для этих C++ 98 вопросов, но в Разделе 10 (Управление памятью) ничего об этом не упоминается. В противном случае какая-то авторитетная ссылка была бы приятной.
Отказ от ответственности: я спросил его по reddit, но я не спросил достаточно красиво, поэтому у меня не было никакого полезного ответа. Я хотел бы просить вас, чтобы, если у вас есть только гипотеза, не стесняйтесь публиковать ее в качестве ответа, но говорите, что это всего лишь гипотеза.
Кроме того, на reddit люди продолжали и продолжали примерно нулевые размеры, есть ли у меня предложение изменить стандарт и т.д. Этот вопрос касается семантики функций выделения необработанных памяти при передаче размера, равного нулю. Если для вашего ответа важны темы типа нулевого размера, пожалуйста, включите их! Но постарайтесь не срываться с тангенциальными проблемами.
Кроме того, на reddit люди также высказывали такие аргументы, как "что для целей оптимизации", не имея возможности упомянуть ничего более конкретного. Я бы ожидал чего-то более конкретного, чем "из-за оптимизации" в ответе. Например, один redditor упомянул оптимизацию псевдонимов, но я задавался вопросом, какие из оптимизаций псевдонимов применяются к указателям, которые не могут быть разыменованы, и не могли заставить кого-либо прокомментировать это. Поэтому, возможно, если вы собираетесь упомянуть об оптимизации, небольшой пример, который показывает это, обогатит обсуждение.