1

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?

nebuch
  • 6,475
  • 4
  • 20
  • 39

3 Answers3

2

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.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
1

Any function declarator can have empty parentheses (unless it's a function declaration where there is already a non-void prototype in scope). This isn't deprecated, although it is "obsolescent".

In a function pointer, it means the pointer can point to a function with any argument list.

Note that when actually calling a function through the pointer, the arguments must be of correct type and number according to the function definition, otherwise the behaviour is undefined.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Although you could supply “any argument list” when using a pointer to a function without a prototype, that could mislead people into think you could call a function with any parameter list. This is not true because an expression denoting a function without a prototype cannot be used to call a function with `char` or `float` parameters, since the default argument promotions are performed on the arguments, with the result that they are not compatible with `char` or `float` parameters. – Eric Postpischil Mar 25 '18 at 11:11
  • 1
    Old-style function declarations *and* definitions (with empty parentheses) are both "obsolescent". I'd say that's close enough to "deprecated". "... their use in ... new programs ... is discouraged" -- C11, Introduction, paragraph 2. – Keith Thompson Mar 25 '18 at 22:34
0

Although C allows you to declare a function (or pointer to function) with an empty parameter list, that does not change the fact that the function must be defined with a precise of parameters, each with a precise type. [Note 1]

If the parameter declaration is not visible at a call site, the compiler will obviously not be able to perform appropriate conversions to the provided arguments. It is, therefore, the programmer's responsibility to ensure that there are a correct number of arguments, all of them with the correct type. For some parameter types, this will not be possible because the compiler will apply the default argument promotions. [Note 2]

Calling a function with an incorrect number of arguments or with an argument whose type is not compatible with the corresponding parameter type is Undefined Behaviour.

The fact that the visible declaration has an empty parameter list does not change the way the function is called. It just puts more burden on the programmer to ensure that the call is well-defined.

This is equally true of pointer to function declarations.

In short, the sample code in the question is Undefined Behaviour. It happens to "work" on certain platforms, but it is neither portable nor is it guaranteed to keep working if you recompile. So the only possible advice is "Don't do that."

If you want to create a function which can accept extra arguments, use a varargs declaration. (See open for an example.) But be aware of the limitations: the called function must have some way of knowing the precise number and types of the provided arguments.


Notes

  1. With the the exception of varargs functions, whose prototypes end with .... But a declaration with an empty parameter list cannot be used to call a varargs function.

  2. Integer types narrower than int are converted to int and float values to double.

Community
  • 1
  • 1
rici
  • 234,347
  • 28
  • 237
  • 341
  • The empty parameter list changes more than whether the compiler can verify the arguments match the function declaration. When calling a function expression without a prototype, the integer promotions are performed on the arguments and `float` arguments are promoted to `double`. This does not happen with functions with a prototype; conversion as if by assignment is performed. Additionally, the rules for whether the argument types match the prototypes vary. (Without a prototype, the value of an argument can affect whether the call is legal in a way different from with a prototype.) – Eric Postpischil Mar 25 '18 at 11:03
  • @eric: the called function does not know it has been called through a declaration without a prototype, so it cannot interpret supplied arguments differently depending on whether or not the caller used a prototype. The lack of a prototype affects how the arguments are prepared and that certainly has consequences; one of them is that it is impossible to call functions whose defined parameters have certain types. I probably should be clearer about that. Thanks for the feedback. – rici Mar 25 '18 at 14:01