You're right that the first is a pointer to a function and the second is a function declaration.
That means that the compiler is correct about the warnings, too.
The first call (f3(&f1)
) passes the address of the function pointer, which is convertible to void *
(contrary to a previous comment of mine). So, there's no error required. (It's a pointer to a function pointer, and therefore a data object, not a function pointer. Omit the &
and you get the same error as with the second call.)
The second call (f3(&f2)
) passes a pointer to a function, and function pointers and void
pointers are not inter-convertible. The &
in front of the function name is superfluous — and mildly misleading in context. You can add an arbitrary number of *
, or a single &
(or omit them all) from a function name and it is treated the same — one of the weirder aspects of standard C. (See also Why do function pointer definitions work with any number of ampersands '&' or asterisks '*'?)
I note that I had to use -pedantic
to get GCC to complain about it at all. That's a consequence of the standard documenting a common extension in Annex J:
J.5.7 Function pointer casts
¶1 A pointer to an object or to void
may be cast to a pointer to a function, allowing data to
be invoked as a function (6.5.4).
¶2 A pointer to a function may be cast to a pointer to an object or to void
, allowing a
function to be inspected or modified (for example, by a debugger) (6.5.4).
You ask what void *(*)()
means. It is the 'cast' form of a pointer to a function that takes an indeterminate argument list (but not a variadic one with ellipsis ...
at the end) and returns a pointer to a function.
haccks asked:
Could you please add a reference from C standard about function pointers and void pointers are not inter-convertible?
Yes — that's easy:
6.2.5 Types
¶1 … Types are partitioned into object types (types that describe objects) and function types (types that describe functions).
6.3 Conversions
6.3.2.3 Pointers
¶1 A pointer to void
may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void
and back again; the result shall compare equal to the original pointer.
¶7 A pointer to an object type may be converted to a pointer to a different object type. If the
resulting pointer is not correctly aligned68) for the referenced type, the behavior is
undefined. Otherwise, when converted back again, the result shall compare equal to the
original pointer. When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
¶8 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 referenced type,
the behavior is undefined.
Those are the only defined conversions for between pointer types. There's nothing there about converting between pointers to objects and pointers to functions, so it isn't allowed (but a diagnostic is not necessarily required; it isn't in a 'constraints' section of the standard).
Tested code
Variant 1 (pf19.c
):
void (*f1)(void);
void *f2(void);
void f3(void *p_func);
int main(void)
{
f3(&f1);
f3(&f2);
}
Compilation warnings (errors because of -Werror
and -pedantic
):
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -pedantic -c pf19.c
pf19.c: In function ‘main’:
pf19.c:9:12: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
f3(&f2); //<----------Compiler error
^
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void * (*)(void)’
void f3(void *p_func);
^~
cc1: all warnings being treated as errors
Variant 2 (also pf19.c
):
void (*f1)(void);
void *f2(void);
void f3(void *p_func);
int main(void)
{
f3(f1);
f3(&f2);
}
Compilation messages:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition -pedantic -c pf19.c
pf19.c: In function ‘main’:
pf19.c:8:8: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
f3(f1);
^~
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void (*)(void)’
void f3(void *p_func);
^~
pf19.c:9:8: error: ISO C forbids passing argument 1 of ‘f3’ between function pointer and ‘void *’ [-Werror=pedantic]
f3(&f2);
^
pf19.c:4:6: note: expected ‘void *’ but argument is of type ‘void * (*)(void)’
void f3(void *p_func);
^~
cc1: all warnings being treated as errors
$
The wording of the message is different from a C compiler compared with a C++ compiler, but the intent is the same (pf17.cc
is a simple copy of pf19.c
):
$ g++ -O3 -g -I./inc -std=c++11 -Wall -Wextra -Werror -c pf17.cc
pf17.cc: In function ‘int main()’:
pf17.cc:8:10: error: invalid conversion from ‘void (*)()’ to ‘void*’ [-fpermissive]
f3(f1);
^
pf17.cc:4:6: note: initializing argument 1 of ‘void f3(void*)’
void f3(void *p_func);
^~
pf17.cc:9:8: error: invalid conversion from ‘void* (*)()’ to ‘void*’ [-fpermissive]
f3(&f2);
^~~
pf17.cc:4:6: note: initializing argument 1 of ‘void f3(void*)’
void f3(void *p_func);
^~
$
Testing: GCC 6.2.0 on Mac OS X 10.11.6 El Capitan.
Thanks to Dmitri for noting that §6.3.2.3 ¶1 was relevant, and Annex J.5.7.