69

Considering the following code (and the fact that VirtualAlloc() returns a void*):

BYTE* pbNext = reinterpret_cast<BYTE*>(
    VirtualAlloc(NULL, cbAlloc, MEM_COMMIT, PAGE_READWRITE));

why is reinterpret_cast chosen instead of static_cast?

I used to think that reinterpret_cast is OK for e.g. casting pointers to and from integer types (like e.g. DWORD_PTR), but to cast from a void* to a BYTE*, isn't static_cast OK?

Are there any (subtle?) differences in this particular case, or are they just both valid pointer casts?

Does the C++ standard have a preference for this case, suggesting a way instead of the other?

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • 12
    In first place, I wouldn't assume that Microsoft developers follow "de facto good practices". `static_cast` is just fine here. –  Mar 22 '13 at 20:05
  • 3
    `static_cast` should be preferred, but some people prefer `reinterpret_cast` because the name shows what you are doing (you are reinterpreting the bit pattern). – Jesse Good Mar 22 '13 at 20:17
  • 2
    (Anyway, +1, enjoy your `nice question` badge!) –  Mar 22 '13 at 20:29

3 Answers3

59

For convertible pointers to fundamental types both casts have the same meaning; so you are correct that static_cast is okay.

When converting between some pointer types, it's possible that the specific memory address held in the pointer needs to change.

That's where the two casts differ. static_cast will make the appropriate adjustment. reinterpret_cast will not.

For that reason, it's a good general rule to static_cast between pointer types unless you know that reinterpret_cast is desired.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
32

You should static_cast. Use static_cast in cases where you're undoing an implicit conversion.

In this particular case, however, there is no difference because you're converting from void*. But in general, reinterpret_casting between two object pointers is defined to be (§5.2.10/7):

An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast<cv T2*>(static_cast<cv void*>(v)) if both T1 and T2 are standard-layout types and the alignment requirements of T2 are no stricter than those of T1, or if either type is void. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

Emphasis mine. Since T1 for you is already void*, the cast to void* in reinterpret_cast does nothing. This is not true in general, which is what Drew Dormann is saying:

#include <iostream>

template <typename T>
void print_pointer(const volatile T* ptr)
{
    // this is needed by oversight in the standard
    std::cout << static_cast<void*>(const_cast<T*>(ptr)) << std::endl;
}

struct base_a {};
struct base_b {};
struct derived : base_a, base_b {};

int main()
{
    derived d;

    base_b* b = &d; // implicit cast

    // undo implicit cast with static_cast
    derived* x = static_cast<derived*>(b);

    // reinterpret the value with reinterpret_cast
    derived* y = reinterpret_cast<derived*>(b);

    print_pointer(&d);
    print_pointer(x);
    print_pointer(y);
}

Output:

00CBFD5B
00CBFD5B
00CBFD5C

(Note that because y doesn't actually point to a derived, using it is undefined behavior.)

Here, reinterpret_cast comes up with a different value because it goes through void*. This is why you should use static_cast when you can, and reinterpret_cast when you have to.

Community
  • 1
  • 1
GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Nice illustration! Regarding `void*` conversion - I have this nagging recollection that *specifically* for conversions to/from `void*`, the standard allows `reinterpret_cast` to change the value. And that only a cyclical conversion `X*` to `void*` to `X*` is guaranteed to preserve the address. Is that incorrect? – Drew Dormann Mar 22 '13 at 20:25
  • @DrewDormann: I'm not sure I follow, which line are you referring to? For `reinterpret_cast`, all cases with `void*` (either as source type or destination type) go through the clause I quoted, so the only thing relevant from this point is `static_cast` w.r.t. `void*`. Hope that clarifies. – GManNickG Mar 22 '13 at 20:40
  • @GManNickG may I know your opinion on [this](https://stackoverflow.com/questions/3725975/simple-c-pointer-casting) question? based on the answers here I think using 2 static here is safer but could you correct me if I am wrong. – Ali Mar 16 '18 at 16:54
  • @GManNickG Can you explain the "..needed by oversight in the standard" part in `print_pointer`? Why not simply `cout` the `ptr`? – User 10482 Jun 29 '23 at 17:10
  • @User10482 I have long since forgotten, but if I were to guess it is/was probably missing a overload for `volatile`-qualified things. Totally unnecessary for this example to includes this, of course. – GManNickG Jun 29 '23 at 21:25
8

Using static_cast to cast a pointer to and from void* is guaranteed to preserve the address.

reinterpret_cast on the other hand guarantees that if you cast the pointer from one type to other, and back to the original type, the address is preserved.

Although with most implementations, you would see the same results in using either of these, static_cast should be preferred.

And with C++11 I remember that, using reinterpret_cast for void* has a well defined behavior. Before that this behavior was prohibited.

It is not permitted to use reinterpret_cast to convert between pointers to object type and pointers to void.

Proposed resolution (August, 2010):

Change 5.2.10 [expr.reinterpret.cast] paragraph 7 as follows:

An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of type “pointer to T1” is converted to the type “pointer to cv T2”, the result is static_cast(static_cast(v)) if both T1 and T2 are standard-layout types (3.9 [basic.types]) and the alignment requirements of T2 are no stricter than those of T1, or if either type is void.

Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value. The result of any other such pointer conversion is unspecified.

More info here.

Thanks to Jesse Good for the link.

Community
  • 1
  • 1
Tuxdude
  • 47,485
  • 15
  • 109
  • 110
  • 2
    Here is a [link](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1120) about reinterpret cast, it was actually prohibited before C++11. – Jesse Good Mar 22 '13 at 20:12