16

Compiling with gcc8:

#include <stdio.h>
void some_func(void f1(void), void (*f2)(void))
{
    printf("%d\n", f1);
    printf("%d\n", f2);
}

Gives (only) the following warnings:

<source>:11:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'void (*)(void)' [-Wformat=]
     printf("%d\n", f1);
<source>:12:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'void (*)(void)' [-Wformat=]
     printf("%d\n", f2);

Why is the type of f1 the same as f2? Only f2 is declared as a function pointer. I would expect f1 not to compile at all, as it names a function type, not a function pointer. What is the rule that says, that a function type inside a function parameter list changes to a pointer to that function type?

Boann
  • 48,794
  • 16
  • 117
  • 146
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 2
    Answered this on Quora once: https://www.quora.com/In-C-is-it-possible-to-pass-a-function-to-another-function/answer/Petr-Skocik – Petr Skocik Oct 25 '18 at 19:24
  • 1
    Regarding the warnings, please use `%p` format specifier when targeting pointers. – paddy Oct 25 '18 at 19:27
  • 4
    @paddy I know, and not really, `%p` is _only_ for `void*`, so I would need to cast it. The warnings serve only to show to me the deduced by the compiler variable types, not to print the pointers addresses. – KamilCuk Oct 25 '18 at 19:34
  • As a note, just stick to one style, be constant, I prefer the second one but, there is not a clear rule about that. – Stargateur Oct 25 '18 at 19:41
  • Why? Because the language standard says so. – AnT stands with Russia Oct 25 '18 at 20:21
  • @paddy `"%p"` is for `void *`, not necessarily function pointers. Casting a function pointer to `void*` has its limitations too. No great way to print function pointers - `printf("%jX\n", (uintmax_t) f1);` is a reasonable mostly portable alternative. – chux - Reinstate Monica Oct 25 '18 at 20:30

2 Answers2

24

Because the standard (6.7.6.3p8) says that

A declaration of a parameter as ''function returning type'' shall be adjusted to ''pointer to function returning type'', as in 6.3.2.1.

It's similar to how arrays parameters are adjusted to pointers (6.7.63.p7) , if you think about it.

void some_func(void (void));
void some_func(void (*)(void));

are compatible declarations, just like:

void other_func(char string[]);
void other_func(char *string);

are.


Note that the adjustment doesn't make void some_func(void (*)(void) compatible with void some_other_func(void (**)(void) or void yet_another_func(void (*****)(void) and that as far as functions are concerned, declarations don't really reflect use anymore, (despite that being the intention of the language's original author). In standardized C, due to how function identifiers decay to pointers and due to how it doesn't matter whether you use a function type or a function pointer type to make a call, you can call any function with arbitrarily many *:

#include <stdio.h>
int main()
{
    (*puts)("hello world");
    (******puts)("hello world");
    (***&*&*puts)("hello world"); //& cancels a * as per 6.5.3.2p3

    int (*p)(char const*) = puts;
    int (**pp)(char const*) = &p;
    int (***ppp)(char const*) = &pp;

    (**ppp)("hello world"); //at least two asterisks required here
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
2

Because in C, in that case, the function name itself is a function pointer. See this answer: Why is using the function name as a function pointer equivalent to applying the address-of operator to the function name?

ad3angel1s
  • 484
  • 1
  • 5
  • 17
  • Then why does `void some_func(void (*****f3)(void))` here `f3` names a different type then `f1` and `f2`. – KamilCuk Oct 25 '18 at 19:31
  • I don't see why it should be the same type as `f1` or `f2`. There is an historical and practical reason why the function name is a pointer to the function itself, like explained in that answer, and I don't see these reasons can be applied to any arbitrary pointer to pointer (...) to function. – ad3angel1s Oct 25 '18 at 19:35
  • 7
    In C, a function name itself is **not** a function pointer. The function name is an identifier. When used as an expression, it has function type. Per C 6.3.2.1 4, an expression that has function type is called a *function designator*. When it is not used as the operand of `sizeof` or unary `&`, **then** a function designator is converted to a pointer (again by 6.3.2.1 4). I understand you may think of this conversion as so automatic and ubiquitous that one can think of a function name as a pointer, but we are creating a permanent record of technical answers explaining the C standard here. – Eric Postpischil Oct 25 '18 at 19:49
  • 1
    @EricPostpischil does this mean that you can only call only function pointers and not functions? Because `()` operator is not `sizeof` or `&`. – Ajay Brahmakshatriya Oct 25 '18 at 20:19
  • 2
    @AjayBrahmakshatriya: In a function call expression, such as `SomeExpression()`, if the `SomeExpression` is a function designator (any expression that has function type), then it is automatically converted to a pointer to the function. In effect, a “proper” way to write a function call such as `f1(3)` is `(&f1)(3)`. When you write `f1(3)`, the compiler automatically converts it to `(&f1)(3)` for you. – Eric Postpischil Oct 25 '18 at 20:35
  • @EricPostpischil I agree, that is what I meant. You never truly call a function, you always call a function pointer. – Ajay Brahmakshatriya Oct 25 '18 at 20:45