Warm tip: This article is reproduced from serverfault.com, please click

Calling method without typeless argument

发布于 2020-12-03 02:43:41

I have noticed some behavior in C that I do not quite understand. If I define a function with an argument that I do not specify a type for and call it without any argument, the code compiles but gives a warning that the type of the parameter defaults to int.

void f(i) {
    printf("%d", i);
}
int main(void) {
    f();
}

If I run this on godbolt, I get the expected compiler warning, and execution prints 1 to stdout.

However, if I define the same method, but explicitly declare the argument as an int, the compiler throws an error for the function being called with too few arguments.

void f(int i) {
    printf("%d", i);
}
int main(void) {
    f();
}

Compiling this on godbolt produces error: too few arguments to function 'f'.

This behavior is consistent across all modern versions of gcc and clang. Why does specifying the type of the argument make this a compiler error? Also, is the behavior of the first example defined? The fact that gcc and clang both always make i 1 would make me think that this is intentional. If so, why?

Questioner
bisen2
Viewed
0
dbush 2020-12-03 11:30:33

The second piece of code is relatively simple. The function is defined to accept one argument, and the function call in main passed no arguments. That's a clear error.

The first piece of code is more interesting. This goes back to old style function definitions where the parameter list contains only the names of the parameters and the types appear between the closing parenthesis terminating the argument list and the opening brace before the start of the function body. Such a function properly defined would look like this:

void f(i) 
  int i;
{
    printf("%d", i);
}

This style of specifying function arguments dates back to the original K&R C but is considered deprecated now. This variant of C also had the property that the type of a variable could be omitted in which cast it defaults to int.

However, even if you do specify the type after the argument list as in the above example, it's still not an error. So why is this?

The key parts comes from section 6.5.2.2 of the C standard regarding function calls:

2 If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.

...

8 No other conversions are performed implicitly; in particular, the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator.

And section 6.9.1p7 regarding function definitions:

The declarator in a function definition specifies the name of the function being defined and the identifiers of its parameters. If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit. If the declarator includes an identifier list, the types of the parameters shall be declared in a following declaration list. In either case, the type of each parameter is adjusted as described in 6.7.6.3 for a parameter type list; the resulting type shall be a complete object type

In the case of a K&R style function definition, the parameters are specified as an identifier list as opposed to a parameter type list. This means that the function definition does not also specify a function prototype, meaning the number and types of the parameters are not checked when the function is called.