C++: Is it safe to cast pointer to int and later back to pointer again?

前端 未结 12 1788
梦如初夏
梦如初夏 2020-11-28 11:38

Is it safe to cast pointer to int and later back to pointer again?

How about if we know if the pointer is 32 bit long and int is 32 bit long?

long* j         


        
相关标签:
12条回答
  • 2020-11-28 12:17

    Absolutely not. Doing some makes a bad assumption that the size of an int and a pointer are the same. This is almost always no the case on 64 bit platforms. If they are not the same a precision loss will occur and the final pointer value will be incorrect.

    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
    
    0 讨论(0)
  • 2020-11-28 12:25

    No it is not. Even if we rule out the architecture issue, size of a pointer and an integer have differences. A pointer can be of three types in C++ : near, far, and huge. They have different sizes. And if we talk about an integer its normally of 16 or 32 bit. So casting integer into pointers and vice-verse is not safe. Utmost care has to be taken, as there very much chances of precision loss. In most of the cases an integer will be short of space to store a pointer, resulting in loss of value.

    0 讨论(0)
  • 2020-11-28 12:25

    If the issue is that you want to do normal math on it, probably the safest thing to do would be to cast it to a pointer to char (or better yet, * uint8_t), do your math, and then cast it back.

    0 讨论(0)
  • 2020-11-28 12:34

    Yes, if... (or "Yes, but...") and no otherwise.

    The standard specifies (3.7.4.3) the following:

    • A pointer value is a safely-derived pointer [...] if it is the result of a well-defined pointer conversion or reinterpret_cast of a safely-derived pointer value [or] the result of a reinterpret_cast of an integer representation of a safely-derived pointer value
    • An integer value is an integer representation of a safely-derived pointer [...] if its type is at least as large as std::intptr_t and [...] the result of a reinterpret_cast of a safely-derived pointer value [or] the result of a valid conversion of an integer representation of a safely-derived pointer value [or] the result of an additive or bitwise operation, one of whose operands is an integer representation of a safely-derived pointer value
    • A traceable pointer object is [...] an object of an integral type that is at least as large as std::intptr_t

    The standard further states that implementations may be relaxed or may be strict about enforcing safely-derived pointers. Which means it is unspecified whether using or dereferencing a not-safely-derived pointer invokes undefined behavior (that's a funny thing to say!)

    Which alltogether means no more and no less than "something different might work anyway, but the only safe thing is as specified above".

    Therefore, if you either use std::intptr_t in the first place (the preferrable thing to do!) or if you know that the storage size of whatever integer type you use (say, long) is at least the size of std::intptr_t, then it is allowable and well-defined (i.e. "safe") to cast to your integer type and back. The standard guarantees that.

    If that's not the case, the conversion from pointer to integer representation will probably (or at least possibly) lose some information, and the conversion back will not give a valid pointer. Or, it might by accident, but this is not guaranteed.

    An interesting anecdote is that the C++ standard does not directly define std::intptr_t at all; it merely says "the same as 7.18 in the C standard".

    The C standard, on the other hand, states "designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer".
    Which means, without the rather complicated definitions above (in particular the last bit of the first bullet point), it wouldn't be allowable to convert to/from anything but void*.

    0 讨论(0)
  • 2020-11-28 12:36

    Yes and no.

    The language specification explicitly states that it is safe (meaning that in the end you will get the original pointer value) as long as the size of the integral type is sufficient to store the [implementation-dependent] integral representation of the pointer.

    So, in general case it is not "safe", since in general case int can easily turn out to be too small. In your specific case it though it might be safe, since your int might be sufficiently large to store your pointer.

    Normally, when you need to do something like that, you should use the intptr_t/uintptr_t types, which are specifically introduced for that purpose. Unfortunately, intptr_t/uintptr_t are not the part of the current C++ standard (they are standard C99 types), but many implementations provide them nevertheless. You can always define these types yourself, of course.

    0 讨论(0)
  • 2020-11-28 12:36

    In general, no; pointers may be larger than int, in which case there's no way to reconstruct the value.

    If an integer type is known to be large enough, then you can; according to the Standard (5.2.10/5):

    A pointer converted to an integer of sufficient size ... and back to the same pointer type will have its original value

    However, in C++03, there's no standard way to tell which integer types are large enough. C++11 and C99 (and hence in practice most C++03 implementations), and also Boost.Integer, define intptr_t and uintptr_t for this purpose. Or you could define your own type and assert (preferably at compile time) that it's large enough; or, if you don't have some special reason for it to be an integer type, use void*.

    0 讨论(0)
提交回复
热议问题