Является ли переинтерпретация приведения целого числа к указателю биективным, если целое число имеет тот же размер, что и указатель?

Гарантируется ли целочисленный тип IntT, такой что sizeof(IntT) == sizeof(void*), и переменная указанного типа i, что reinterpret_cast<IntT>(reinterpret_cast<void*>(i)) == i? Это похоже на этот вопрос, но этот вопрос рассматривал любое целое число произвольного размера, поэтому ответ был прямым «нет». Ограничение его целыми числами точно такого же размера, как указатель, делает его более интересным.

Мне кажется, что ответ должен быть «да», потому что в спецификации указано, что существует отображение на любое целое число, достаточно большое для хранения значения указателя. Если переменные имеют одинаковый размер, то это отображение должно быть биективным. Если оно биективно, то это также означает, что преобразование из int в void* также должно быть биективным.

Но есть ли в этой логике дыра? Есть ли в спецификации слово, которое я не учитываю?


person Cort Ammon    schedule 11.10.2018    source источник
comment
std::intptr_t/std::uintptr_t (если существует) может содержать указатель.   -  person Jarod42    schedule 12.10.2018
comment
Критическая проблема заключается в том, что не каждая строка битов дает допустимое значение void*, поэтому не каждое целое число должно быть затронуто преобразованием в этом направлении.   -  person Davis Herring    schedule 12.10.2018
comment
Я не думаю, что это гарантировало наличие 2 ** (sizeof(void*)) действительных (void*) указателей.   -  person Jarod42    schedule 12.10.2018
comment
@Jarod42: В этом выражении не хватает CHAR_BIT, но идея у тебя правильная   -  person Ben Voigt    schedule 12.10.2018
comment
@ Jarod42: Я был бы удивлен, если бы не было указателей 2 ** (sizeof(void*)), поскольку многим программам требуется более 16 или 256 допустимых значений указателя. :-) (2 ** (CHAR_BIT * sizeof(void*)) было бы ближе к разумному пределу)   -  person ShadowRanger    schedule 12.10.2018
comment
Слишком поздно исправлять опечатку в моих комментариях :-( (действительно, я имел в виду 2 ** (CHAR_BIT * sizeof(void*))).   -  person Jarod42    schedule 12.10.2018


Ответы (1)


Я не думаю, что это гарантировано. Стандарт гарантирует, что указатель, преобразованный в достаточно большое целое число и обратно, будет иметь исходное значение. Из этого следует, что существует отображение указателей на подмножество подходящих больших целых чисел и обратно. Чего это не означает, так это того, что для каждого достаточно большого целочисленного значения существует соответствующее значение указателя…

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

Просто представьте себе какую-то странную архитектуру, где по какой-то причине каждый третий бит адреса должен быть равен нулю. Или немного более разумная архитектура, которая использует только младшие 42 бита 64-битного значения для хранения адреса. Независимо от того, насколько это разумно, компилятор может предположить, что целочисленное значение, приведенное к указателю, должно следовать шаблону действительного адреса и, например, маскировать каждый третий бит или использовать только младшие шесть байтов. соответственно…

person Michael Kenzel    schedule 11.10.2018
comment
Это и мое прочтение. Кажется, они действительно изо всех сил стараются назвать это реализацией, не упоминая размер целого числа. - person zzxyz; 12.10.2018
comment
@DavisHerring Вы правы. У меня было забавное чувство, когда я называл это биективным, но почему-то не стал подвергать сомнению мое использование этого термина. Спасибо, что указали на это, я обновлю свой ответ, чтобы исправить это… - person Michael Kenzel; 12.10.2018
comment
Стандарт даже не требует, чтобы преобразование было инъективным. Все, что для этого требуется, — это преобразование указателя в подходящее целое число и обратно для получения указателя, который сравнивается с оригиналом. Это вызывается на cppreference: один и тот же указатель может иметь несколько целочисленных представлений. - person Raymond Chen; 12.10.2018
comment
@RaymondChen Хорошо, но если бы он не был инъективным, это означало бы, что несколько разных указателей имеют одно и то же целочисленное представление, и в этом случае не было бы возможности обратно преобразовать целое число в указатель !? По крайней мере, насколько я понимаю, один и тот же указатель, имеющий несколько целочисленных представлений, будет просто означать, что отношение не является уникальным справа (функциональным), поэтому я попытался указать, что это не функция в моем ответе выше. Он все равно должен быть левоуникальным (инъективным), чтобы было преобразование обратно… - person Michael Kenzel; 12.10.2018
comment
@RaymondChen: Множественные представления являются еще одной причиной необратимости — в частности, круговой путь не гарантируется, даже если целое число было получено из указателя! - person Davis Herring; 12.10.2018
comment
@MichaelKenzel Ах, я пропустил одну деталь в стандарте. Преобразование возврата должно производить «исходное значение», а не просто «сравнивать с исходным значением». Я думал о системах, в которых может быть несколько представлений для одного и того же базового указателя, и круговое путешествие может создать эквивалентный (но не идентичный) указатель. (Например, рассмотрим систему, подобную valgrind, с толстыми указателями, которые добавляют метаданные к необработанному указателю, например «Максимум, до которого вы можете увеличить этот указатель, прежде чем вы выйдете за конец массива».) - person Raymond Chen; 12.10.2018
comment
@RaymondChen Исходное значение не подразумевает объект с таким же побитовым представлением. Это означает указывать на один и тот же объект. - person curiousguy; 01.12.2018
comment
@любопытно, я не уверен. Если они имели в виду указание на один и тот же объект, то почему бы не написать это? исходное значение звучит так, как будто значение должно быть идентичным. - person Raymond Chen; 01.12.2018
comment
@RaymondChen Потому что указание на один и тот же объект неверно в качестве спецификации! Это может быть нуль. Это может быть за концом. Правильным описанием будет указывающее на тот же объект, на который указывал оригинал, или один за концом, если был оригинал, или null.... То же значение гораздо более лаконично и позволяет избежать неотъемлемого риска при написании перечисления случаев в спецификации (вы можете забыть один). Также то же значение лучше выражает намерение. И если вы не покажете мне, что битовый шаблон обязательно сохраняется при копировании тривиального типа... - person curiousguy; 02.12.2018
comment
@RaymondChen Какой тривиальный тип (кроме тех, которые используются для доступа к представлению произвольного объекта с помощью псевдонимов) когда-либо гарантировал уникальное побитовое представление для данного значения? Нет, НА САМОМ ДЕЛЕ. Не имеет значения: результат memcmp не должен вообще ничего значить. - person curiousguy; 02.12.2018
comment
@RaymondChen похоже, что значение должно быть идентичным Это ЗНАЧИТ именно это. Значение то же самое, оно сравнивается с оригиналом, его можно использовать так же. Его можно разыменовать, если оригинал может (и тогда ссылается на тот же объект). Кроме того, поскольку целое число по своей природе анонимно, а не волшебно, преобразованное значение должно иметь возможность ссылаться на любой другой объект, который находится по тому же адресу! - person curiousguy; 02.12.2018
comment
@curious Не могу понять, согласен ты со мной или нет. - person Raymond Chen; 02.12.2018
comment
@RaymondChen Я согласен с тем, что идентичное значение подразумевает либо равное нулевое значение, либо указание на один и тот же объект, либо за конец одного и того же массива. Поэтому, если он не нулевой, это подразумевает указание на один и тот же байт. Возвратное преобразование должно давать «исходное значение», а не просто «сравнивать с исходным значением». Да, это сильнее: все, что допустимо для исходного значения, должно быть также допустимо для преобразованного значения. Но я также считаю, что преобразованное значение является более достоверным, чем исходное, поскольку его можно использовать для ссылки на любой объект, который СЕЙЧАС находится по этому адресу, даже если исходное значение было признано недействительным. - person curiousguy; 02.12.2018
comment
(...) например, если исходный указатель ссылается на объект, который не существует, но другой существует СЕЙЧАС по тому же адресу, преобразование приведет к повторной проверке указателя IMO. - person curiousguy; 02.12.2018