views:

147

answers:

7

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.

+5  A: 

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
+2  A: 

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
+1  A: 

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.

DeadMG
+1  A: 

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 Nice and short. In addition, it's worth mentioning that `static_cast` can't cast const away.
sellibitze
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
A: 

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
+3  A: 

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
+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
reinterpret_cast is very dangerous and it's non crossplatform.
zaynyatyi
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
A: 

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
Well but that's exactly the purpose of the cast :), see my edit
tauran
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
You write as if being a beginner was a bad thing ...
tauran
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
I have read you edit. I commented below it, because I totally disagree with you.
Stephane Rolland
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