views:

623

answers:

3

I'm trying to understand the differences between C and C++ with regards to void pointers. the following compiles in C but not C++ (all compilations done with gcc/g++ -ansi -pedantic -Wall):

int* p = malloc(sizeof(int));

Because malloc returns void*, which C++ doesn't allow to assign to int* while C does.

However, here:

void foo(void* vptr)
{
}


int main()
{
    int* p = (int*) malloc(sizeof(int));
    foo(p);
    return 0;
}

Both C++ and C compile it with no complain. Why?

K&R2 say:

Any pointer to an object may be converted to type void * without loss of information. If the result is converted back to the original pointer type, the original pointer is recovered.

And this pretty sums all there is about void* conversions in C. What does C++ standard dictate?

+15  A: 

In C, pointer conversions to and from void* were always implicit.

In C++, conversions from T* to void* are implicit, but void* to anything else requires a cast.

GMan
can you give a source for your 2nd statement? is it written in C++ standard?
zaharpopov
There isn't much to quote, really. It's in section 4.10.2. It pretty much says a `T*` can be converted to a `void*`. It leaves out the reverse, implying you must use a cast.
GMan
@zaharpopov: look up reinterpret_cast<> or static_cast<>
Martin York
@GMan: By explicitly not stating the conversion from void* means it is not possible to do it (ie converting from void* to any other pointer type is not allowed (implicitly)). Then by looking at the definition for the cast operators you can see what these operators are allowed to do with objects. And they are very clear on the definition of casting void* to other types.
Martin York
@GMan: If by "In C, pointer conversions were always implicit" you mean conversions to and from `void *`, you are right. Otherwise, when it comes to pointer conversions, C is about as strict as C++. For example, there's no implict conversion from `char *` to `int *` in C. Historically, it has been customary to report pointer conversion errors as "warnings" in C compilers, which lead to a myth about C allegedly allowing all and any implicit pointer conversions. Nevertheless, it is a mere myth.
AndreyT
Yea, I meant what you said, I didn't realize how unclear that was. In regards to York's comments, I'm gonna try to add some info on that tomorrow, but for now I must sleep.
GMan
+6  A: 

C++ is more strongly-typed than C. Many conversions, specially those that imply a different interpretation of the value, require an explicit conversion. The new operator in C++ is a type-safe way to allocate memory on heap, without an explicit cast.

Vijay Mathew
+1  A: 

It is useful to understand that pointer type conversions don't really require execution of extra CPU instructions. They are analysed during the compile time to understand the intensions of the developer. void * is an opaque pointer. All it says that the type of pointed object is unknown. C is weakly typed. It allows direct conversion between (void *) and any (T*) implicitly. C++ is strongly typed. A conversion from (void *) to (T*) wouldn't really make good case for a strongly typed language. But C++ had to stay backwards compatible with C, hence it had to allow such conversions. The guiding principle then is: explicit is better than implicit. Hence if you wish to convert an (void*) to some specific (T*) pointer, you need to explicitly write that in code. Conversion from (T*) to (void*) doesn't require explicit conversion since there is nothing much one can do on a (void*) pointer directly (one can call free() though). Hence (T*) to (void*) conversion is pretty much safe.

Shailesh Kumar
Whether pointer conversions require CPU instructions or not is an implementation detail. There's absolutely nothing in C and C++ languages that would require this conversions to be purely conceptual (i.e. require no CPU instructions). On the contrary, both languages are specifically formulated to allow different representations for different pointer types. While "understanding" that in typical case the conversion really does nothing at the CPU level might indeed be useful, writing C or C++ code that relies on that assumption is a serious error.
AndreyT
... And I don't understand the point about "staying backward compatible". C++ requires an explicit cast to convert from `void *`. That is already *not* backward compatible with C.
AndreyT
C++ is not fully backward compatible with C. It just provides ways to make the migration as easy as possible wherever necessary. Shifting from being weakly typed to strongly typed naturally leads to compatibility issues. C++ just tries to provide simple ways (like doing an explicit type casting on a void pointer) to ease migration.
Shailesh Kumar
@AndreyT: And just to re-enforce your point, conversions between derived and base classes (especially with multiple inheritance) may require CPU in order to move the pointer to the appropriate part of the object in memory.
Richard Corden