Felix Kater said:
I wonder if I really could achieve what I need (calling different
functions via the same function pointer) by simply inserting a cast like
this -- which would be fantastic:
[snip]
No, that still invokes undefined behavior. The cast, as casts often
do, act as a promise to the compiler that you really know what you're
doing, and it shouldn't bother you with warning messages. In this
case, you're lying to the compiler.
On many systems, the calling conventions are consistent enough that
you can get away with this kind of thing. In pre-ANSI (K&R) C, there
were no prototypes; the only declaration you might have for printf(),
for example, is:
int printf();
Since the compiler had no idea when it saw a call that printf() is
variadic, or even that it expects any arguments at all, it would have
to generate code for the call based purely on the actual arguments.
Since this was the case for all function calls, all functions had to
have the same calling conventions.
The introduction of prototypes in ANSI C89 made it possible for the
compiler to know when it sees a call exactly what the function
expects, and therefore allowed for more sophisticated calling
conventions. The generated code for printf("%d", n) and
some_other_func("%d", n) might be very different, based on the
functions' prototypes -- which is why calling printf() with no visible
prototype to provide that information to the compiler invokes
undefined behavior.
But some implementations have kept the older calling conventions to
avoid breaking older code, or just out of inertia (there might not be
any reason to change the conventions if the older ones worked well
enough).
Which means that if you make the mistake of calling a function without
properly telling the compiler about the function's prototype, (a)
you'll most likely invoke undefined behavior, and (b) it will *appear*
to work on many implementations, so you won't detect the error until
the most embarrassing possible moment. So don't do that.
Here's what the standard says (C99 6.3.2.3p8):
A pointer to a function of one type may be converted to a pointer
to a function of another type and back again; the result shall
compare equal to the original pointer. If a converted pointer is
used to call a function whose type is not compatible with the
pointed-to type, the behavior is undefined
So you can use a single "generic" pointer-to-function type to store
pointers to arbitrary functions, but you must keep track of the actual
type of each function and explicitly convert the pointer back to the
proper type before calling it through the pointer. Which means you
can only have a fixed number of function types that you can handle
this way -- but you're only going to have a fixed number of functions
in your program anyway. (There's no defined generic
pointer-to-function type, like void* for pointer-to-object types, but
you can pick one arbitrarily, like void(*)(void).)