Reputation: 149
I am trying to understand why these two lines produce the same result (test and test2).
customClass* test= (customClass*)*(uintptr_t*)(someAddr);
customClass* test2 = *(customClass**)(someAddr);
From what I gathered it's either double dereference or pointer to pointer (in this case).
someAddr refers to pointer to pointer to customClass class in memory
Upvotes: 0
Views: 494
Reputation: 67713
If you can't read complex statements, break them up into lots of simple statements.
(uintptr_t*)(someAddr)
casts someAddr
(which I assume is some form of pointer) to a pointer-to-uintptr_t
.
So, replace this with the statement
uintptr_t* A = (uintptr_t*)(someAddr);
uintptr_t
is defined as an unsigned integer type large enough to hold a pointer*(uintptr_t*)(someAddr)
is now just *A
which is a reference to uintptr_t
So, replace this with
uintptr_t& B = *A;
(customClass*)*(uintptr_t*)(someAddr)
is now just (customClass*)B
, which means we're converting the integer-large-enough-to-hold-a-pointer back into a pointer.
See the second and third notes on reinterpret_cast
, and replace this code with
customClass* test = reinterpret_cast<customClass*>(B);
So the first line ends up as
uintptr_t* A = (uintptr_t*)(someAddr);
uintptr_t& B = *A;
customClass* test = reinterpret_cast<customClass*>(B);
Notice that since uintptr_t
is being used to hold a pointer, we're really treating the original address as a pointer-to-pointer. That's how we de-reference one pointer and still end up with a pointer.
Now look again at
customClass* test2 = *(customClass**)(someAddr);
and see it's doing roughly the same thing.
The problem is that although uintptr_t
and customClass*
are losslessly-convertible, that doesn't mean it's OK to either alias them or to assume they're layout-compatible. You still need to know what type of object you currently have, in order to correctly convert it to the other.
Which type you currently have depends on what was stored there originally, so only one of the statements can be correct.
Upvotes: 0
Reputation: 62586
I am trying to understand why these two lines produce the same result (test and test2).
Because undefined behaviour means anything is allowed, even things that don't make sense1.
The value in someAddr
is a pointer value, but that doesn't mean you can interpret it as whatever pointer type you like. What it points to is either a someClass *
, or a uintptr_t
, or something else, it can't be both at the same time.
Therefore one of those lines is breaking the strict aliasing rule, and thus has undefined behaviour. I would presume that it really is a someClass **
value, so use an appropriate cast for that, i.e. std::reinterpret_cast<someClass **>(someAddr)
someAddr
as a pointer-sized number, then reinterpreting that as a someClass *
matches how a someClass **
is implemented. It would be ok to haveuintptr_t otherAddr;
memcpy(&otherAddr, reinterpret_cast<void*>(someAddr), sizeof(uintptr_t));
customClass* test3 = reinterpret_cast<customClass*>(otherAddr);
Upvotes: 1