Безопасно ли приводить указатель к int, а затем обратно к указателю снова?
Как насчет того, чтобы знать, является ли указатель 32-битная длина, а int 32-битная?
long* juggle(long* p) {
static_assert(sizeof(long*) == sizeof(int));
int v = reinterpret_cast<int>(p); // or if sizeof(*)==8 choose long here
do_some_math(v); // prevent compiler from optimizing
return reinterpret_cast<long*>(v);
}
int main() {
long* stuff = new long(42);
long* ffuts = juggle(stuff);
std::cout << "Is this always 42? " << *ffuts << std::endl;
}
Это охватывается стандартом?
Да и нет.
В спецификации языка явно указано, что это безопасно (что означает, что в конце вы получите исходное значение указателя), пока размер интегрального типа достаточен для хранения [зависящего от реализации] интегрального представления указателя. .
Так что в общем случае это не «безопасно», так как в общем случае int
легко может оказаться слишком маленьким. В вашем конкретном случае это может быть безопасно, поскольку ваш int
может быть достаточно большим для хранения вашего указателя.
Обычно, когда вам нужно сделать что-то подобное, вы должны использовать типы intptr_t
/ uintptr_t
, которые специально введены для этой цели. К сожалению, intptr_t
/ uintptr_t
не являются частью текущего стандарта C ++ (они являются стандартными типами C99), но, тем не менее, многие реализации предоставляют их. Конечно, вы всегда можете определить эти типы самостоятельно.
Это безопасно? Не совсем.
В большинстве случаев это сработает? Да
Конечно, если int
слишком мал для хранения полного значения указателя и усекает, вы не получите обратно свой исходный указатель (надеюсь, ваш компилятор предупредит вас об этом случае, с усекающими преобразованиями GCC от указателя к целым числам - серьезные ошибки). long
или uintptr_t
, если ваша библиотека поддерживает его, могут быть лучшим выбором.
Даже если ваш целочисленный тип и типы указателя имеют одинаковый размер, это не обязательно будет работать в зависимости от времени выполнения вашего приложения. В частности, если вы используете в своей программе сборщик мусора, он может легко решить, что указатель больше не является выдающимся, и когда вы позже вернете свое целое число обратно в указатель и попытаетесь разыменовать его, вы обнаружите объект уже был пожат.
Нет, это не так. Даже если мы исключим проблему архитектуры, размер указателя и целого числа будет отличаться. В C ++ указатель может быть трех типов: ближний, дальний и огромный. У них разные размеры. И если мы говорим о целых числах, обычно это 16- или 32-битные. Таким образом, преобразование целого числа в указатели и наоборот небезопасно. Следует проявлять особую осторожность, так как очень велика вероятность потери точности. В большинстве случаев целому числу не хватает места для хранения указателя, что приводит к потере значения.
Используйте uintptr_t из "stdint.h" или из "boost/stdint.h". Гарантированно будет достаточно памяти для указателя.
В целом нет; указатели могут быть больше, чем int
, и в этом случае невозможно восстановить значение.
Если известно, что целочисленный тип достаточно велик, вы можете; согласно стандарту (5.2.10/5):
Указатель, преобразованный в целое число достаточного размера... и обратно в тот же тип указателя, будет иметь исходное значение
.Однако в C++03 нет стандартного способа определить, какие целые типы достаточно велики. C++11 и C99 (и, следовательно, на практике большинство реализаций C++03), а также Boost.Integer определяют для этой цели intptr_t
и uintptr_t
. . Или вы можете определить свой собственный тип и утверждать (предпочтительно во время компиляции), что он достаточно велик; или, если у вас нет особой причины использовать целочисленный тип, используйте void*
.
№.
Например, на x86-64 указатель имеет длину 64 бита, а int
имеет длину только 32 бита. Приведение указателя к int и обратно приводит к потере старших 32-битных значений указателя.
Вы можете использовать тип intptr_t
в
, если вам нужен целочисленный тип, длина которого гарантированно равна указателю. Вы можете безопасно переинтерпретировать_приведение от указателя к intptr_t
и обратно.
Если вы собираетесь выполнять какое-либо переносное транслирование системы, вам нужно использовать что-то вроде Microsoft INT_PTR/UINT_PTR, после этого безопасность зависит от целевых платформ. и что вы собираетесь делать с INT_PTR. как правило, для большинства арифметических char* или uint_8* работает лучше, будучи типобезопасным (ish)
Абсолютно нет. Выполнение некоторых делает неверное предположение, что размер int
и указателя одинаковы. Это почти всегда не так на 64-битных платформах. Если они не совпадают, произойдет потеря точности, и конечное значение указателя будет неверным.
MyType* pValue = ...
int stored = (int)pValue; // Just lost the upper 4 bytes on a 64 bit platform
pValue = (MyType*)stored; // pValue is now invalid
pValue->SomeOp(); // Kaboom