printf("%p") and casting to (void *)
The %p
conversion specifier requires an argument of type void *
. If you don't pass an argument of type void *
, the function call invokes undefined behavior.
From the C Standard (C11, 7.21.6.1p8 Formatted input/output functions):
"p - The argument shall be a pointer to void."
Pointer types in C are not required to have the same size or the same representation.
An example of an implementation with different pointer types representation is Cray PVP where the representation of pointer types is 64-bit for void *
and char *
but 32-bit for the other pointer types.
See "Cray C/C++ Reference Manual", Table 3. in "9.1.2.2" http://docs.cray.com/books/004-2179-003/004-2179-003-manual.pdf
In C language all pointer types potentially differ in their representations. So, yes, int *
is different from void *
. A real-life platform that would illustrate this difference might be difficult (or impossible) to find, but at the conceptual level the difference is still there.
In other words, in general case different pointer types have different representations. int *
is different from void *
and different from double *
. The fact that your platform uses the same representation for void *
and int *
is nothing more than a coincidence, as far as C language is concerned.
The language states that some pointer types are required to have identical representations, which includes void *
vs. char *
, pointers to different struct types or, say, int *
and const int *
. But these are just exceptions from the general rule.
Other people have adequately addressed the case of passing an int *
to a prototyped function with a fixed number of arguments that expects a different pointer type.
printf
is not such a function. It is a variadic function, so the default argument promotions are used for its anonymous arguments (i.e. everything after the format string) and if the promoted type of each argument does not exactly match the type expected by the format effector, the behavior is undefined. In particular, even if int *
and void *
have identical representation,
int a;
printf("%p\n", &a);
has undefined behavior.
This is because the layout of the call frame may depend on the exact concrete type of each argument. ABIs that specify different argument areas for pointer and non-pointer types have occurred in real life (e.g. the Motorola 68000 would like you to keep pointers in the address registers and non-pointers in the data registers to the maximum extent possible). I'm not aware of any real-world ABI that segregates distinct pointer types, but it's allowed and it would not surprise me to hear of one.