1

I have a short program with which I am going to test the “infinite recursion”. However, I meet another tough question associated to function pointer...

the program is as follow:

#include <stdio.h>

int repeat(char (*func)(int, int), int a) {
  func(a, a+1);
  return repeat(func, a+1);
}

char f (int a , int b){
  printf("%d\n", a);
  //return (double)(a + b);
  return 'c';
}

int main(int argc, char const *argv[])
{
  repeat(&f, 1);
  return 0;
}

As for me, I think the 'f' in “repeat(&f, 1)” has the type of “char *” and the 'func' in “int repeat(char (*func)(int, int), int a)” has the type of “char *”,too.

However, it seems strange. If I change “(char (*func)(int, int), int a) ” to “(char func(int, int), int a) ”, the compiler(testing in gcc and vc6) do not give any warnings and the running result is the same.

If I change “(char (*func)(int, int), int a) ” to “(double func(int, int), int a) ”, the compiler throws:

warning: incompatible pointer types passing 'char ()(int, int)' to parameter of type 'double ()(int, int)' [-Wincompatible-pointer-types] repeat(&f, 1);

It seems that the compiler treat “double (int, int)” and “double (*)(int, int)” the same stuff. In other words, If I change “(char (*func)(int, int), int a)” to “double (*func)(int, int)”, the compiler will throw the same message (I have tested it):

warning: incompatible pointer types passing 'char ()(int, int)' to parameter of type 'double ()(int, int)' [-Wincompatible-pointer-types] repeat(&f, 1);

God... Well, it is not the end. After that, I change the “repeat(&f, 1)to “repeat(f, 1)” with other parts unchanged. That is, the whole program is:

#include <stdio.h>

int repeat(char (*func)(int, int), int a) {
  func(a, a+1);
  return repeat(func, a+1);
}

char f (int a , int b){
  printf("%d\n", a);
  //return (double)(a + b);
  return 'c';
}

int main(int argc, char const *argv[])
{
  repeat(f, 1);
  return 0;
}

the compiler do not give any warnings and the running results are the same.

So I am confused that all of the following paris yields no warnings and correct results.

    1   repeat(&f, 1)   int repeat(char (*func)(int, int), int a)
    2   repeat(&f, 1)   int repeat(char func(int, int), int a) 
    3   repeat(f, 1)    int repeat(char (*func)(int, int), int a) 
    4   repeat(f, 1)    int repeat(char func(int, int), int a) 

Of course the 'f' in “repeat(f, 1)” has the type of “char (int, int)”, and the 'func' in "int repeat(double (func)(int, int), int a)" has the type of “char ()(int, int)”.

You can infer this by changing “int repeat(char (*func)(int, int), int a) ” to “int repeat(double (*func)(int, int), int a) ” and check the warning message:

warning: incompatible pointer types passing 'char (int, int)' to parameter of type 'double ()(int, int)' [-Wincompatible-pointer-types] repeat(f, 1);*

Who can give some comments? I totally think that only the first of the four is correct regarding the type.

Zirun
  • 21
  • 4

2 Answers2

3

When used as a function parameter, both of the syntax

foo(char bar(int, int), int a)  

and

foo(char (*bar)(int, int), int a)

are identical from the compiler's standpoint.

Also note that when you call function foo, the name of the function passed to it is treated as pointer to that function. These both calls are valid

foo(&f, 1)
foo(f, 1)

As for me, I think the f in repeat(&f, 1) has the type of char * and the func in int repeat(char (*func)(int, int), int a) has the type of char *,too.

No . f and func both are of type char (*)(int, int), i.e. they are pointer to a function that takes two int as argument and returns char

haccks
  • 104,019
  • 25
  • 176
  • 264
  • Thanks! I got the answer when I returned home from workplace... In fact, should they be the same! (The answer from Christophe seems more complete, so I vote him, you already have the reputation of 33.2k : ) you geek.) – Zirun Nov 09 '14 at 13:40
  • “f and func both are of type char (*)(int, int), i.e. they are pointer to a function that takes two int as argument and returns char”. Thank you again. The type is char (*)(int, int) indeed. I made a silly mistake. – Zirun Nov 09 '14 at 13:54
1

Type of the parameter:

The order of precendence imposed by () in the syntax of :

    int repeat(char (*func)(int, int), int a)

means that (*func)(int,int) is a function taking two int arguments and returning a char. The * means that func a pointer to a function returning char.

The following more modern syntax would have worked as well :

    int repeat(char func(int, int), int a)

ATTENTION, the following looks close to the first one, but would be totally different, the function argument would be a pointer to a function returning a char *:

    int repeat(char *func(int, int), int a)

And yes, just for completeness, int repeat((char *)func(int, int), int a) would be a syntax error, as (char*) would be understood as a cast operator whereas a type is expected.

Use of the f and &f syntax:

The C11 standard section 6.5.3.2 states that "The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’":

  • f being a function taking two int as argument and returning a char
  • So &f is a pointer to a function taking two int as argument and returning a char.

But the standard in section 6.3.2.1/4 also states that "A function designator is an expression that has function type. (...) a function designator with type ‘‘function returning type’’ is converted to an expression that has type ‘‘pointer to function returning type’’":

  • f being a function taking two int as argument and returning a char
  • if provided in an expression (of course not in the syntax of a function call), f is also understood as pointer to a function taking two int as argument and returning a char.

Nowadays, you'll find both syntaxes.

Historically speaking, in former times the use of * in the (*func)() syntax was mandatory to call the function pointed to (see original K&R).

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Thank you very much, it is surprising that you have known so much even about the C standard/C11 standard. really cool. So, they are different in the past, but the same nowadays? – Zirun Nov 09 '14 at 13:32
  • Maybe that's the way mainstream languages evolve ? New syntaxes emerge, but old ones are kept because of the huge code base. Then it's a matter of taste: some will prefer `extern char rpt(char(*)(int, int));` and some `extern char rpt(char(int, int));` – Christophe Nov 09 '14 at 14:16
  • Mostly a nice answer, but your last sentence is inaccurate. I have my copy of K&R 1st Edition, and it covers pointers to functions on pp114-117. Not one of the function names used as a function pointer is preceded by an `&`; it was not legal at the time (but was made legal by C89, still over a decade into the future when the book was written). Also, in K&R 1, the only way to invoke a pointer to function as a function was `(*pointer_to_func)(arg1, arg2)`; again, standard C allowed the shorthand `pointer_to_func(arg1, arg2)`. – Jonathan Leffler Nov 09 '14 at 18:22
  • Note how the use-mimics-type applies in `(*func)(a1, a2)`, which matches a declaration such as `int (*func)(int a1, int a2)` which tells the compiler that the return type of `(*func)(a1, a2)` is `int`. The new notation has some merits, but you lose the certainty about whether `some_name(a1, a2)` is an invocation of a function such as `int some_name(int a1, int a2)` or function pointer (perhaps from a function argument list) `int (*some_name)(int a1, int a2)`. With the old system, it was crystal clear; with the new, not so much. – Jonathan Leffler Nov 09 '14 at 18:26
  • @JonathanLeffler Indeed ! In K&R1 p.115 the example is without & : `sort(lineptr, nlines, strcmp,swap)` the two last being functions. Nevertheless it states just below that "*& is not **necessary***". My appologies. I was mistaken by another difference: the `*` was mandatory in the `(*fptr)(...)` calling syntax which is no longer the case. – Christophe Nov 09 '14 at 18:47
  • @JonathanLeffler, @Christophe. So the old usage is: when passing a function pointer as an argument, we do not use `&`; when invoking a pointer to function as a function we use `*`. And it is much more clear than the new usage. I am not sure whether I understand it well. (My native language is not English...) – Zirun Nov 10 '14 at 01:24
  • 1
    @ZirunZhu: whether it is clearer depends on your perspective. When I learned C, the `&` on the function name wasn't an option AFAICR, and you didn't ever write `&` in front of an array name because different C compilers interpreted it differently. This was a murky area that the standard fixed. A lot of C works on 'type mimics use'. When you write `int *ip;`, it means that if you subsequently use `*ip`, the type is `int`. Similarly with function pointers: if you declare `int (*func)(int a1, int a2)`, then `(*func)(1, 2)` produces an `int`. These days, you can write `func(1, 2)` as well. – Jonathan Leffler Nov 10 '14 at 06:24