Why can't I static_cast between char * and unsigned char *?

45,643

They are completely different types see standard:

3.9.1 Fundamental types [basic.fundamental]

1 Objects declared as characters char) shall be large enough to store any member of the implementation's basic character set. If a character from this set is stored in a character object, the integral value of that character object is equal to the value of the single character literal form of that character. It is implementation-defined whether a char object can hold negative values. Characters can be explicitly declared unsigned or
signed. Plain char, signed char, and unsigned char are three distinct types. A char, a signed char, and an unsigned char occupy the same amount of storage and have the same alignment requirements (basic.types); that is, they have the same object representation. For character types, all bits of the object
representation participate in the value representation. For unsigned character types, all possible bit patterns of the value representation represent numbers. These requirements do not hold for other types. In any particular implementation, a plain char object can take on either the same values as a signed char or an unsigned char; which one is implementation-defined.

So analogous to this is also why the following fails:

unsigned int* a = new unsigned int(10);
int* b = static_cast<int*>(a); // error different types

a and b are completely different types, really what you are questioning is why is static_cast so restrictive when it can perform the following without problem

unsigned int a = new unsigned int(10);
int b = static_cast<int>(a); // OK but may result in loss of precision

and why can it not deduce that the target types are the same bit-field width and can be represented? It can do this for scalar types but for pointers, unless the target is derived from the source and you wish to perform a downcast then casting between pointers is not going to work.

Bjarne Stroustrop states why static_cast's are useful in this link: http://www.stroustrup.com/bs_faq2.html#static-cast but in abbreviated form it is for the user to state clearly what their intentions are and to give the compiler the opportunity to check that what you are intending can be achieved, since static_cast does not support casting between different pointer types then the compiler can catch this error to alert the user and if they really want to do this conversion they then should use reinterpret_cast.

Share:
45,643
Nick
Author by

Nick

I was a dual-degree Math and CS major at Penn, earning a Bachelor's and Master's in both. I graduated in 2013.

Updated on May 01, 2020

Comments

  • Nick
    Nick about 4 years

    Apparently the compiler considers them to be unrelated types and hence reinterpret_cast is required. Why is this the rule?

  • Tobias Langner
    Tobias Langner about 12 years
    thx for stating the standard here. I don't have it available.
  • Nick
    Nick about 12 years
    So if they're distinct types, why does the compiler allow the cast unsigned char a = 255; char b = static_cast<char>(a); ?
  • EdChum
    EdChum about 12 years
    the same reason you can static_cast from doubles to ints and the other way, what you can't do is static_cast double* to int*, the pointer types are different but you can convert from one value to another with the caveat that there may be a loss in precision
  • Nick
    Nick about 12 years
    hmm, seems like a silly rule. As I mentioned in my response to Tobias, it seems like the only time a static_cast should be allowed on array/pointer types should be for same-width primitives, otherwise you could accidentally shoot yourself in the foot (as in the example I gave). At least with same-width primitives, nothing can go wrong.
  • EdChum
    EdChum about 12 years
    @Nick It can surprise people when they learn that you cannot static_cast between unsigned char* and char* but it is fundamentally because they are different types, we are not surprised that you can static cast between related types like floats to ints, nor in fact unsigned char to char but static_cast does what it does the clear advantage over c-style casts is that you get compile time errors if you try to convert between different types like in your case, there is a related SO post: stackoverflow.com/questions/2473628/…
  • Nick
    Nick about 12 years
    in that case it's obviously wrong though, since int and double have different widths (and aren't even represented the same!), so if you cast to double * you could accidentally trample some memory if you do *d = 3.14. My use of "silly" applies only to same-width primitives.
  • EdChum
    EdChum about 12 years
    @Nick well it may be silly for you but it as another example static_cast is not possible between unsigned int* and int* which is analogous to your question, the storage and width is the same for both but static_cast detects at compile time that they are both different types
  • Nicol Bolas
    Nicol Bolas about 12 years
    @Nick: Even with int and float. They may have the same size, but if you have an int, then try to read it like a float, then what you get depends on exactly how float is stored in memory. And since the specification does not say how float is stored in memory, the specification cannot define what the int looks like. Remember: The C++ standard exists to provide guarantees about what you get. In order to define this behavior, the standard would have to detail how float is laid out in memory, as well as how int is laid out in memory.
  • Nicol Bolas
    Nicol Bolas about 12 years
    @Nick: In short: you're confusing the concept of "what happens on real machines" with "what the specification requires."
  • Cemafor
    Cemafor about 9 years
    The specification does not say that the most significant bit has to be the sign bit, so an implementation could have it be the least significant bit. If that were the case, when the compiler was casting an unsigned char with value 1 to a signed char, it would know it needed to shift the bits left 1 to account for the sign bit. But with a unsigned char* and char*, it's not the cast's job to adjust the values at the pointer's location. It wouldn't know how many chars to adjust anyway.
  • allyourcode
    allyourcode over 8 years
    The part of the standard quoted in this answer reminds me of git man page generators.
  • Euri Pinhollow
    Euri Pinhollow almost 6 years
    static_cast is a bitch though, allowing for unconditional upcast in the derivation tree.
  • Osman-pasha
    Osman-pasha about 2 years
    is it better to do a reinterpret_cast or do a static_cast to void* and then to required type?