Search code examples
cfunction-pointersc99c11c89

What are the semantics of function pointers with empty parentheses in each C standard?


Answers to this and this question say that function pointers of the form return-type (*pointer)() are pointers to a function which takes any number of arguments, though the latter says they obsolesced in C11.

On an i386 system with GCC, “extra” arguments passed in a call to an empty-parentheses-type’d function pointer are ignored, because of how stack frames work; e.g.,

/* test.c */
#include <stdio.h>

int foo(int arg) { return arg; }

int main(void)
{
    int (*fp)() = foo;
    printf("%d\n", fp(267239151, 42, (struct { int x, y, z; }){ 1, 2, 3 }));
    return 0;
}

$ gcc -o test test.c && ./test
267239151
$ 

In which C standards are empty-parentheses’d function pointers allowed? and wherever so, what are they specified to mean?


Solution

  • N1570 6.11.6:

    The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

    This same wording appears in the 1990, 1999, and 2011 editions of the ISO C standard. There has been no change. The word obsolescent says that the feature may be removed in a future edition of the Standard, but so far the committee has not done so. (Function pointer declarations are just one of several contexts where function declarators can appear.)

    The Introduction section of the C standard explains what obsolescent means:

    Certain features are obsolescent, which means that they may be considered for withdrawal in future revisions of this International Standard. They are retained because of their widespread use, but their use in new implementations (for implementation features) or new programs (for language [6.11] or library features [7.31]) is discouraged.

    A call to a function declared with an old-style declarator is still required to pass the correct number and type(s) of arguments (after promotion) as defined by the function's actual definition. A call with incorrect arguments has undefined behavior, which means that the compiler is not required to diagnose the error; the burden is entirely on the programmer.

    This is why prototypes were introduced, so that the compiler could check correctness of arguments.

    On an i386 system with GCC, “extra” arguments passed in a call to an empty-parentheses-type’d function pointer are ignored, because of how stack frames work ...

    Yes, that's well within the bounds of undefined behavior. The worst symptom of undefined behavior is having the program work exactly as you expect it to. It means that you have a bug that hasn't exhibited itself yet, and it will be difficult to track it down.

    You should not depend on that unless you have a very good reason to do so.

    If you change

    int (*fp)() = foo;
    

    to

    int (*fp)(int) = foo;
    

    the compiler will diagnose the incorrect call.