No. ...
Function pointers cannot portably be converted either to void *, to any
other object pointer type, or to intptr_t; not directly, and not
indirectly. They _can_ all be converted, losslessly, to one another, but
that isn't helpful if you want to print one.
Right.
Fundamentally, this allows implementations to use "fatter" pointers
for "pointer to <function ...>" types than for "pointer to <data
type>" types. The IBM AS/400, at least, makes use of this property.
(Intel x86 compilers used to use it too, back in the days of "near"
and "far" pointers, in various models.)
Note that one can always store the function pointers in data objects
of type "pointer to <function ...>". That is, given some function
T F(arglist), we just declare an actual object:
T (*fp)(arglist); /* e.g., int (*fp)(int, char **) */
and store the pointer to that function in that data object. Now we
take the data object's address:
T (**pfp)(arglist) = &fp;
and we have successfully "smuggled" the function-pointer value inside
a data object, whose address we can print. (Of course, it may be the
*contents* of that address that are in some manner "interesting", but
the address is printable, and is specific to "pfp".)
Now suppose that, for every function F that exists in some given
C program P, we were able to -- in some mechanical manner -- invent
a static-storage-duration pointer of the appropriate type, and set
it to point to F. For instance, if program P contains, among other
things:
int foo(int, char **);
void bar(double);
we would have to create pointers p_foo and p_bar:
int (*p_foo)(int, char **) = foo;
void (*p_bar)(double) = bar;
If we could do this, we would have a data pointer corresponding to
each function pointer, and could substitute the data-pointer values
for every instance of the function pointer value, at the cost of
having to indirect through that data pointer to find the function.
That is, we could rewrite the call:
result = foo(3, &x);
as:
result = (*p_foo)(3, &x);
The question is, can a C compiler do this "mechanically"? The
answer is obviously "yes": every time it encounters an actual
function definition, it emits a definition of the corresponding
pointer -- using the implementation's name space, so something like
__func_foo instead of p_foo -- initialized to point to that function.
Every time it encounters a declaration of a function, it emits a
corresponding declaration (with "extern") for that same pointer.
Every time it encounters a call to a function F, it loads the
pointer from __func_F.
Having done this, the compiler has, in effect, converted all
functions to data objects, which are (in this case) all named
__func_<whatever> instead of just <whatever>. If we were to collect
up all these objects in some manner (say, at link time), and label
them "function descriptors", we could put them in a "function
descriptor table".
Using this technique, a C compiler that was *not* allowed to use
"fat" pointers to point to functions could put the "real" function
pointers -- which could in fact be enormously fat -- into a function
descriptor table, and make the "function pointer" that the programmer
sees be an ordinary data pointer, pointing into the descriptor
table. This is, in fact, exactly what some (but not all) C compilers
do, for architectures on which the underlying machine-code level
function pointers are "fatter" than ordinary data pointers. A C
standard that *required* this would be no more restrictive, in
terms of computing power at least, than the current C standards.