Casting void pointers
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.
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, 2022Comments
-
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'svoid *
? Is it because older C compilers didn't support void pointers andmalloc()
used to returnchar *
instead? -
Blagovest Buyukliev over 13 yearsI 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 over 13 yearsA
void*
will be automatically converted to the correspondingtype_t*
on assignment -
James Curran over 13 yearsYour 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 over 13 yearsI 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 over 13 yearsPrior to C89, malloc() returned
char *
. Thevoid *
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 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 over 13 yearsMy 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 over 13 yearsI 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 over 13 yearsDoesn't x = c; and x = (int *) c; produce absolutely the same machine code?
-
abelenky over 13 yearsI stand corrected. I was never aware that malloc returned char* once upon a time. Good to know.
-
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 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 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 over 13 yearsI 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?