Casting void pointers

27,385

Solution 1

Your own explanation is the right one. Pre-ANSI C ('K&R' C) did not have a void * type with implicit conversion. char * doubled as a pseudo void * type, but you needed the explicit conversion of a type cast.

In modern C the casting is frowned upon because it can suppress compiler warnings for a missing prototype of malloc. In C++, the casting is needed (but there you should be using new instead of malloc most of the time).

Update

My comments below that try to explain why the cast is required were a bit unclear, I'll try to explain it better here. You might think that even when malloc returns char *, the cast is not needed because it is similar to:

int  *a;
char *b = a;

But in this example a cast is also needed. The second line is a constraint violation for the simple assignment operator (C99 6.5.1.6.1). Both pointer operands need to be of compatible type. When you change this to:

int  *a;
char *b = (char *) a;

the constraint violation disappears (both operands now have type char *) and the result is well-defined (for converting to a char pointer). In the 'reverse situation':

char *c;
int  *d = (int *) c;

the same argument hold for the cast, but when int * has stricter alignment requirements than char *, the result is implementation defined.

Conclusion: In the pre-ANSI days the type cast was necessary because malloc returned char * and not casting results is a constraint violation for the '=' operator.

Solution 2

The problem here is not compatibility with any dialect of C. The problem is C++. In C++, a void pointer cannot be automatically converted to any other pointer type. So, without an explicit cast, this code would not compile with a C++ compiler.

Solution 3

I'm not aware that malloc ever returned a char*.

But implicit casting from void* to type_t* (or any other type) hasn't always been allowed. Hence, the need to explicitly cast to the proper type.

Share:
27,385
Blagovest Buyukliev
Author by

Blagovest Buyukliev

Author of Quaint, an experimental statically-typed imperative language with first-class resumable functions. Dedicated to the pursuit of clean, elegant and no-nonsense code.

Updated on August 23, 2022

Comments

  • Blagovest Buyukliev
    Blagovest Buyukliev over 1 year

    I've seen a lot of the following in older C code:

    type_t *x = (type_t *) malloc(...);
    

    What's the point of casting the pointer returned from malloc() since it's void *? Is it because older C compilers didn't support void pointers and malloc() used to return char * instead?

  • Blagovest Buyukliev
    Blagovest Buyukliev over 13 years
    I am not attempting to dereference a void pointer without casting it. The point here is that the lvalue is a typed pointer and you still have to cast the rvalue before assignment.
  • sth
    sth over 13 years
    A void* will be automatically converted to the corresponding type_t* on assignment
  • James Curran
    James Curran over 13 years
    Your argument only says why x (in the OP) must be type_t*, not why there must be a cast to assign the value there.
  • Blagovest Buyukliev
    Blagovest Buyukliev over 13 years
    I also think this argument is the most plausible one. Yet even if malloc() returns char *, such a cast is technically not necessary because the type of the lvalue cannot be changed anyway.
  • John Bode
    John Bode over 13 years
    Prior to C89, malloc() returned char *. The void * type was introduced with C89 precisely because of the need for a "generic" pointer type that could be implicitly converted to any other pointer type. As of C89, there is no reason to explicitly cast the result of malloc().
  • schot
    schot over 13 years
    @Blagovest K&R2 (p142) says the cast is necessary, but the errata list (cm.bell-labs.com/cm/cs/cbook/2ediffs.html) retracts this and adds: "On the other hand, pre-ANSI, the cast was necessary, and it is in C++ also."
  • Blagovest Buyukliev
    Blagovest Buyukliev over 13 years
    My remark tried to explain that char *c; int *x; x = c; is technically OK apart from the warning you would get from the compiler.
  • schot
    schot over 13 years
    I think your wrong there, because of alignment issues: From C99 6.3.2.3 (7): "A pointer to an object or incomplete type may be converted to a pointer to a different object or incomplete type. If the resulting pointer is not correctly aligned for the pointed-to type, the behavior is undefined."
  • Blagovest Buyukliev
    Blagovest Buyukliev over 13 years
    Doesn't x = c; and x = (int *) c; produce absolutely the same machine code?
  • abelenky
    abelenky over 13 years
    I stand corrected. I was never aware that malloc returned char* once upon a time. Good to know.
  • R.. GitHub STOP HELPING ICE
    R.. GitHub STOP HELPING ICE over 13 years
    -1 for incorrect usage of the word "cast", which refers to a (type) operator in C. You do need a conversion before the pointer can be used, but it will happen implicitly.
  • caf
    caf over 13 years
    @Blagovest Buyukliev: x = c isn't required to compile at all - it violates a "shall" clause in the standard. It is legitimate for the compiler to reject it as an error rather than just issue a warning.
  • Blagovest Buyukliev
    Blagovest Buyukliev over 13 years
    @schot: Thank you for the insightful explanation. I now understand that this is a violation against the language specifications, but I couldn't get the stuff about alignment issues since we are not talking about dereferencing pointers. On the machine level, doesn't char *, int *, void *, or any other pointer look exactly the same and have the same alignment?
  • Blagovest Buyukliev
    Blagovest Buyukliev over 13 years
    I think I actually understood it... You mean that by casting the pointer the actual address can be "rounded" to match the alignment of the target type?