4

Can someone explain this to me:

char* a;
unsigned char* b;

b = a;
// error: invalid conversion from ‘char*’ to ‘unsigned char*’

b = static_cast<unsigned char*>(a);
// error: invalid static_cast from type ‘char*’ to type ‘unsigned char*’

b = static_cast<unsigned char*>(static_cast<void*>(a));
// everything is fine

What makes the difference between cast 2 and 3? And are there any pitfalls if the approach from 3 is used for other (more complex) types?

[edit] As some mentioned bad design, etc...

This simple example comes from an image library which gives me the pointer to the image data as char*. Clearly image intensities are always positive so I need to interpret it as unsigned char data.

tauran
  • 7,986
  • 6
  • 41
  • 48
  • I don't agree with you edit statement. If you are given an array of char then it is an array of char. Types are strong, really in C++, and you are doing C acrobatics. And most of the most of the most of the time when the compiler tells you are wrong, then you are wrong. – Stephane Rolland Sep 16 '10 at 11:31
  • if at some moment you really need unsigned char, you can simply do this but at the very precise moment you really need : `int i = 65; unsigned int j = i;` this will compile because it is legal, you can always legitimately store a char in a unsigned char. – Stephane Rolland Sep 16 '10 at 11:46
  • Well, what if another library requires the correct `unsigned char*` :D . Would you write a loop and copy all the values to a new array? – tauran Sep 16 '10 at 12:04
  • Yep, in this case I understand, and I agree with you that it's better to do reinterpret_cast<>. But I disagree with your term "correct". If the intensity value are between 0 and 127, as it is obviously the case, then both char and unsigned char types are correct. – Stephane Rolland Sep 16 '10 at 15:26

7 Answers7

6

static_cast<void*> annihilate the purpose of type checking as you say that now it points on "something you don't know the type of". Then the compiler have to trust you and when you say static_cast<unsigned char*> on your new void* then he'll just try to do his job as you ask explicitely.

You'd better use reinterpret_cast<> if you really must use a cast here (as it's obvioulsy showing a design problem here).

Klaim
  • 67,274
  • 36
  • 133
  • 188
  • based on this [link](https://stackoverflow.com/questions/15578935/proper-way-of-casting-pointer-types) is not better to use static casting twice? – Ali Mar 16 '18 at 16:49
  • 1
    @Ali: The `reinterpret_cast` is defined as two `static_cast`s, so safety is roughly equivalent. The problem with two static_casts is that you introduce a point in the middle where it can do the wrong thing. Using reinterpret_cast directly expresses _only_ the two types in question, so it's both more concise and harder to break. – GManNickG Mar 16 '18 at 19:18
5

Your third approach works because C++ allows a void pointer to be casted to T* via static_cast (and back again) but is more restrictive with other pointer types for safety reasons. char and unsigned char are two distinct types. This calls for a reinterpret_cast.

sellibitze
  • 27,611
  • 3
  • 75
  • 95
  • 2
    +1 Also note that while the compiler will accept the code and in all scenarios I can think of it will work, the standard allows `static_cast` to `void*` and back to the same type, which is not what you are doing. This answer is correct: you should use `reinterpret_cast` explcitly. – David Rodríguez - dribeas Sep 16 '10 at 11:21
  • reinterpret_cast is very dangerous and it's non crossplatform. – zaynyatyi Sep 16 '10 at 11:23
  • 2
    reinterpret cast is entirely appropriate here... the (edited) question states this is a workaround for a library that's actually providing unsigned char values, but mistakenly calling them char in its interface. There's billions of lines of C code littered with casts that work perfectly well, you just need a clear understanding of the bitwise arrangements of the two and from data types, and what it is you're asking the compiler to do. – Tony Delroy Sep 16 '10 at 11:36
2

C++ tries to be a little bit more restrictive to type casting than C, so it doesn't let you convert chars to unsigned chars using static_cast (note that you will lose sign information). However, the type void* is special, as C++ cannot make any assumption for it, and has to rely on the compiler telling it the exact type (hence the third cast works).

As for your second question, of course there are a lot of pitfals on using void*. Usually, you don't have to use it, as the C++ type system, templates, etc. is rich enough to not to have to rely in that "unknown type". Also, if you really need to use it, you have to be very careful with casts to and from void* controlling that types inserted and obtained are really the same (for example, not pointer to subclasses, etc.)

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
2

static_cast between pointers works correct only if one of pointers is void or that's casting between objects of classes, where one class is inherited by another.

zaynyatyi
  • 1,116
  • 6
  • 18
  • +1 Nice and short. In addition, it's worth mentioning that `static_cast` can't cast const away. – sellibitze Sep 16 '10 at 11:12
  • 1
    The standard allows you to cast from `T*` to `void*` and back to `T*` with `static_cast`. The result of casting from `void*` to a different type than the original pointer was is undefined in the standard. – David Rodríguez - dribeas Sep 16 '10 at 11:24
1

The difference between 2 and 3 is that in 3, you're explicitly telling the compiler to stop checking you by casting to void*. If the approach from 3 is used for, pretty much anything that isn't a direct primitive integral type, you will invoke undefined behaviour. You may well invoke undefined behaviour in #3 anyway. If it doesn't cast implicitly, it's almost certainly a bad idea unless you really know what's going on, and if you cast a void* back to something that wasn't it's original type, you will get undefined behaviour.

Puppy
  • 144,682
  • 38
  • 256
  • 465
1

Casts between pointers require reinterpret_cast, with the exception of void*:

Casts from any pointer to to void* are implicit, so you don't need to explicitly cast:

char* pch;
void* p = pch;

Casts from void* to any other pointer only require a static_cast:

unsigned char* pi = static_cast<unsigned char*>(p);
sbi
  • 219,715
  • 46
  • 258
  • 445
0

beware, when you cast to void* you lose any type information.

what you are trying to do is incorrect, and false, and error prone and misleading. that's why the compilator returned a compilation error :-)

a simple example

char* pChar = NULL; // you should always initalize your variable when you declare them
unsigned char* pUnsignedChar = NULL; // you should always initalize your variable when you declare them

char aChar = -128;
pChar = &aChar;
pUnsignedChar = static_cast<unsigned char*>(static_cast<void*>(pChar));

then, though pUnsignedChar == pChar nonethless we have *pUnsignedChar == 255 and *pChar == -128.

i do believe this is bad joke, thus bad code.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • Well but that's exactly the purpose of the cast :), see my edit – tauran Sep 16 '10 at 11:18
  • No. The purpose of `reinterpret_cast`, which is equivalent to what you did, is to be used when you KNOW for real the type behind an object. In your example you obviously share information. Since you ask this type of question then you are obviously a beginner in C++, otherwise you should already know that `reinterpret_cast` is only to be used in the extrem limit when really knowing what you do. – Stephane Rolland Sep 16 '10 at 11:22
  • no, I write knowing you have to learn, so I took some time to answer you differently than others. There is always something to learn in C++, and I learn almost every day... I can assure you that I still feel myself more "intermediate" than "expert" in C++ and it's been more than 10 years I'm in it... thus a kind of almost beginner always learning. – Stephane Rolland Sep 16 '10 at 11:26
  • I have read you edit. I commented below it, because I totally disagree with you. – Stephane Rolland Sep 16 '10 at 11:32
  • remark that in your question you have asked for pitfalls, so I gave you a concrete example. But as reinterpret_cast is often bad practice, you'll find (sooner that you think) someone warning you or even asking you to change your code. Just know this: it is a basic C++ knowledge. – Stephane Rolland Sep 16 '10 at 11:42
  • @StephaneRolland considering that you have to do convert int8_t* to uint8_t* which method do you use, reinterpret_cast or 2 static_cast? – Ali Mar 16 '18 at 16:59
  • @Ali `static_cast` will most probably complain at compile time that you are not able to cast `int8_t*` into `uint8_t*`. indeed you used the word "convert", and not "cast". Somebody is wrong: either the producer which produces int8_t or the consumer which awaits uint_8. It depends on the behaviour you want if your pointer int8_t points to the value -1 for example... is it ok to be seen as 256 as an uint or do you want it to be 0 ? if it is ok to be seen as 256 then use a reinterpret_cast. If it is not ok, then you have to manually convert the negative value, because uint just cannot be negative – Stephane Rolland Mar 16 '18 at 17:23
  • You can static cast to void* and then from void* to anything* = 2 static cast. Actually in my case I am working on an embedded system in which some libraries use char and some int8_t as char. Also it does not really matter which is used, because the data ends up being transmitted in bytes and the receiving ends looks at it in whatever way they want. My focus right now is to do the safest possible cast. From what I understood so for with static cast to void I can make sure the address does not change after the conversions but with reinterpret_cast things can end up ugly specially in older C++ – Ali Mar 16 '18 at 17:32
  • I may be wrong but I have always considered (void *) and reinterpret_cast to be the same. I do not see what you mean by reinterprest_cast changing addresses. What do you mean ? You have an example where this happens, because I'm not reminded of any case rigth now. I'd be glad to know. – Stephane Rolland Mar 16 '18 at 17:36
  • check the answer and comments by James Kanze in [this](https://stackoverflow.com/questions/6594395/c-when-should-we-prefer-to-use-a-two-chained-static-cast-over-reinterpret-cast). Also have a look at GManNickG answer in [this](https://stackoverflow.com/questions/15578935/proper-way-of-casting-pointer-types), not saying that my example has anything to do with this but I found it interesting. – Ali Mar 16 '18 at 17:51
  • The answer from James Kanze says that the difference is the intent you transmit to the user, but there won't be differences at execution. But in the answer by GManNickG, you mistook a little bit, it is the static_cast that does the address shift, on the contrary the reinterpret_cast will look at the value blindly without changing the address. The address will change with static_cast so as to match the memory model of class in C++, the address of the base_a and base_b are different: this is why static cast will shift the address depending on the cast either to base_a or base_b. – Stephane Rolland Mar 16 '18 at 17:59
  • Beware that the memory model of C++ classes is platform dependent. Base class adresses and virtual table addresses will probably not be the same under windows and linux. The C++ class memory model has not been unified. – Stephane Rolland Mar 16 '18 at 18:06