C ++: Безопасно ли приводить указатель на int, а затем снова обратно на указатель?

Безопасно ли приводить указатель к 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;
}

Это охватывается стандартом?

24
задан towi 26 July 2013 в 07:19
поделиться

8 ответов

Да и нет.

В спецификации языка явно указано, что это безопасно (что означает, что в конце вы получите исходное значение указателя), пока размер интегрального типа достаточен для хранения [зависящего от реализации] интегрального представления указателя. .

Так что в общем случае это не «безопасно», так как в общем случае int легко может оказаться слишком маленьким. В вашем конкретном случае это может быть безопасно, поскольку ваш int может быть достаточно большим для хранения вашего указателя.

Обычно, когда вам нужно сделать что-то подобное, вы должны использовать типы intptr_t / uintptr_t , которые специально введены для этой цели. К сожалению, intptr_t / uintptr_t не являются частью текущего стандарта C ++ (они являются стандартными типами C99), но, тем не менее, многие реализации предоставляют их. Конечно, вы всегда можете определить эти типы самостоятельно.

21
ответ дан 28 November 2019 в 22:14
поделиться

Это безопасно? Не совсем.

В большинстве случаев это сработает? Да

Конечно, если int слишком мал для хранения полного значения указателя и усекает, вы не получите обратно свой исходный указатель (надеюсь, ваш компилятор предупредит вас об этом случае, с усекающими преобразованиями GCC от указателя к целым числам - серьезные ошибки). long или uintptr_t , если ваша библиотека поддерживает его, могут быть лучшим выбором.

Даже если ваш целочисленный тип и типы указателя имеют одинаковый размер, это не обязательно будет работать в зависимости от времени выполнения вашего приложения. В частности, если вы используете в своей программе сборщик мусора, он может легко решить, что указатель больше не является выдающимся, и когда вы позже вернете свое целое число обратно в указатель и попытаетесь разыменовать его, вы обнаружите объект уже был пожат.

3
ответ дан 28 November 2019 в 22:14
поделиться

Нет, это не так. Даже если мы исключим проблему архитектуры, размер указателя и целого числа будет отличаться. В C ++ указатель может быть трех типов: ближний, дальний и огромный. У них разные размеры. И если мы говорим о целых числах, обычно это 16- или 32-битные. Таким образом, преобразование целого числа в указатели и наоборот небезопасно. Следует проявлять особую осторожность, так как очень велика вероятность потери точности. В большинстве случаев целому числу не хватает места для хранения указателя, что приводит к потере значения.

0
ответ дан 28 November 2019 в 22:14
поделиться

Используйте uintptr_t из "stdint.h" или из "boost/stdint.h". Гарантированно будет достаточно памяти для указателя.

0
ответ дан 28 November 2019 в 22:14
поделиться

В целом нет; указатели могут быть больше, чем int, и в этом случае невозможно восстановить значение.

Если известно, что целочисленный тип достаточно велик, вы можете; согласно стандарту (5.2.10/5):

Указатель, преобразованный в целое число достаточного размера... и обратно в тот же тип указателя, будет иметь исходное значение

.Однако в C++03 нет стандартного способа определить, какие целые типы достаточно велики. C++11 и C99 (и, следовательно, на практике большинство реализаций C++03), а также Boost.Integer определяют для этой цели intptr_t и uintptr_t. . Или вы можете определить свой собственный тип и утверждать (предпочтительно во время компиляции), что он достаточно велик; или, если у вас нет особой причины использовать целочисленный тип, используйте void*.

5
ответ дан 28 November 2019 в 22:14
поделиться

№.

Например, на x86-64 указатель имеет длину 64 бита, а int имеет длину только 32 бита. Приведение указателя к int и обратно приводит к потере старших 32-битных значений указателя.

Вы можете использовать тип intptr_t в , если вам нужен целочисленный тип, длина которого гарантированно равна указателю. Вы можете безопасно переинтерпретировать_приведение от указателя к intptr_t и обратно.

37
ответ дан 28 November 2019 в 22:14
поделиться

Если вы собираетесь выполнять какое-либо переносное транслирование системы, вам нужно использовать что-то вроде Microsoft INT_PTR/UINT_PTR, после этого безопасность зависит от целевых платформ. и что вы собираетесь делать с INT_PTR. как правило, для большинства арифметических char* или uint_8* работает лучше, будучи типобезопасным (ish)

0
ответ дан 28 November 2019 в 22:14
поделиться

Абсолютно нет. Выполнение некоторых делает неверное предположение, что размер 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
2
ответ дан 28 November 2019 в 22:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: