Что на самом деле происходит, когда мы делаем следующее:
1)
int i = -1; // 32 bit
void *p;
p = reinterpret_cast<void*>(i)
в 64-битной архитектуре sizeof (void *) == 8
2)
long long i; // 64 bit
void *p = (unsigned int)(-1);
i = retinterpret_cast<long long>(p)
на 32-битной архитектуре sizeof (void *) = 4
Я обычно понимаю, каков будет результат, но я хочу, чтобы кто-нибудь описал этот механизм в терминах стандарта C ++ для лучшего понимания.
Во втором случае поведение аналогично описанному в «интегральных продвижениях» (4.5) (я буду -1)
для случая «int — unsigned long», поэтому мы обычно говорим, что указатели преобразуются как целые числа со знаком?
3)
int i = ...;
unsigned long long L = ...;
i = L;
Какие правила применяются здесь?
Все преобразования между указателями и целочисленными типами
реализация определена. Единственная гарантия, которую стандарт делает
в том, что:
Значение целочисленного типа или типа перечисления может быть явно
преобразован в указатель Указатель, преобразованный в целое число
достаточный размер (если таковой существует в реализации) и
обратно к тому же типу указателя будет иметь свое первоначальное значение
Стандарт, однако, делает заявление (в
ненормативная справка), которая:
Это должно быть неудивительно для тех, кто знает
структура адресации базовой машины.
С точки зрения качества реализации, если машина
имеет линейную адресацию, int
указатель должен
привести к значению, которое соответствует значению int
, если
слово (независимо от его размера) рассматривается как целостный тип; в
Другими словами, битовый рисунок не изменился. Если интеграл
тип меньше и отрицательный, это открытый вопрос
должен быть расширен знак, или должны ли остальные биты
установить на 0. Я предпочитаю второе, но я думаю, что оба могут быть
считается «неудивительным».
С прагматической точки зрения: существует значительное количество
программное обеспечение там, которое будет время от времени бросать небольшой
неотрицательное целое значение void*
и ожидает получить его
вернемся с актерами позже. Формально, чтобы получить его обратно, требуется
преобразование сначала в intptr_t
(или больше); в противном случае код
не должен компилироваться. Но я не могу представить, чтобы компилятор сломал это
иначе. Для отрицательных значений, с другой стороны, я бы чувствовал
значительно менее уверен. И я не уверен, насколько маленький маленький,
или. (В настоящее время я использую технику в одном особом случае для
значения меньше, чем 40 или 50. Я работаю с MSC, G ++ и Sun
СиСи, по крайней мере, и я не могу себе представить, что это не сработает ни на одном другом
основные Unix-машины, которые я использовал в прошлом. Но я бы не стал
рассчитывать на это на 16-битной Intel, или некоторых других более экзотических
машины, которые я видел, и, конечно, не на встроенной системе.)
Наконец, что касается ваших точных вопросов: это может измениться. Я бы попробовал
и видите, рассчитывая на то, что все, что он делает, есть
какой-то код, который рассчитывает на то, что он делает это, и что
продавец не рискует изменить свое поведение. Формально, так как это
реализация определена, разработчик обязан документировать
это (и может, конечно, изменить его в следующей версии), но
Я вообще нашел это очень, очень трудно найти это
документация.
РЕДАКТИРОВАТЬ:
Я только заметил, что ваш последний вопрос касается unsigned long
в
longint
, Это интегральное преобразование, а не
reinterpret_cast
, поэтому применяются разные правила. Или скорее
правила указаны в другом разделе: основное правило
все еще «реализация определена»:
Если тип назначения подписан, значение не изменяется, если оно
может быть представлен в типе назначения (и битовом поле
ширина); в противном случае значение определяется реализацией.
Стандарт C здесь несколько иной, в том смысле, что он явно
позволяет повысить сигнал, определенный реализацией. (Это
несколько растягивая, чтобы сказать, что «значение» в C ++
Стандарт может быть повышение сигнала, даже если это будет
предпочтительная реализация.) На практике: все
компиляторы, которых я знаю, просто игнорируют дополнительные биты старшего разряда.
Поведение не определено. Все может случиться. Даже не гарантируется, что результат будет последовательным. reinterpret_cast<void*>(1) == reinterpret_cast<void*>(1)
может быть false
,