-1

How to safely convert unsigned integral value (e.g. uint32_t, uint64_t) to a pointer of particular type?

For instance I have:

using my_uint32_t = unsigned int;
using my_uint64_t = unsigned long long;

struct Sample {
    my_uint32_t address;
    my_uint64_t address_64;
};

Sample sample; /* Just for reference here */

struct TargetStruct {
   /* Some member variables goes here */
   ...
};

The goal is to access the object of TargetStruct type existing at Sample.address or Sample.address64 respectively.

I tried to use:

TargetStruct * target = reinterpret_cast<TargetStruct *>( sample );

It works but is that safe? I know the rules say reinterpret_cast shall be avoided as unsafe but is there any other way to make it safely?

I cannot go over my "inputs" - the Sample struct and it's content - it is simply given but I have to use the data stored in there in such usafe way...

I am using C++20 so I am about to avoid C-style casts...

Martin Kopecký
  • 912
  • 5
  • 16

1 Answers1

1

How to safely convert unsigned integral value (e.g. uint32_t, uint64_t) to a pointer of particular type?

Firstly convert the integer to std::uintptr_t. This integer type is guaranteed to be sufficiently large to represent object pointers. std::uintptr_t can then be reinterpreted as a pointer.

Note that having gotten the integer (of sufficient size) originally by reinterpreting a valid pointer is the only case in which the standard specifies the resulting value of the conversion from integer to pointer. That is: The original pointer value is reproduced.

A safe example:

int x;         // some object
int* ptr = &x; // pointer to that object
std::uintptr_t reinterpreted = reinterpret_cast<std::uintptr_t>(ptr);
// following sanity check might fail in theory on systems
// where pointer is larger than 64 bits
assert(reinterpreted <= std::numeric_limits<my_uint64_t>::max());
my_uint64_t reinterpreted64 = reinterpreted;
// now we have a pointer in my_uint64_t

// conversion back
std::uintptr_t reinterpreted2 = reinterpreted64;
int* ptr_again = reinterpret_cast<int*>(reinterpreted2);
assert(ptr == ptr_again); // this is guaranteed to pass
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • convert with `reinterpret_cast`, or `static_cast`, or...? – Mooing Duck Jul 24 '20 at 17:17
  • @MooingDuck If you mean integer to `std::uintptr_t`, then either implicit conversion, or `static_cast` if you want to be explicit. Other conversions (between integer and pointer) are reinterpret casts. – eerorika Jul 24 '20 at 17:18
  • So that would mean to reinterpret twice? `uintptr_t temp = reinterpret_cast( sample.address );` and then `TargetStruct * = reinterpret_cast( temp );` ? – Martin Kopecký Jul 24 '20 at 17:24
  • @MartinKopecký: You shouldn't need the first one. `sample.address` should already be a `std::uintptr_t`. There's no guarantee that a pointer can be stored in any other integer type. What if pointers are 80 bits? – Mooing Duck Jul 24 '20 at 17:28
  • @MartinKopecký No. Only one reinterpret. I added the extra steps of converting to `my_uint64_t` to show how. That said, it might make sense to use `std::uintptr_t` throughout instead of converting to `my_uint64_t` and back which in theory may risk losing the correct value. – eerorika Jul 24 '20 at 17:28
  • But the address given IS stored within the 32bit unsigned intergral value (`using my_uint32_t = unsigned int;`) and exacty this is the part I cannot change to using `uintptr_t` which is proposed. My goal is to safely convert such raw 32bit number into an address... – Martin Kopecký Jul 24 '20 at 17:35
  • @MartinKopecký If the target system has 32 bit pointers, then there is no problem other than lack of portability. If the target system has larger (typically 64 bit) pointers, then they cannot fit in 32 bits. – eerorika Jul 24 '20 at 17:37