1

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?

bisen2
  • 486
  • 1
  • 4
  • 10
  • 1
    I bet it's got something to do with ancient K&R C (which would look something like `void f(i) int i { ... }`) – ikegami Dec 03 '20 at 03:01

2 Answers2

1

There are two types of function prototypes:

  1. The original, known as "K&R" [from Kernighan & Ritchie, the creators of C].
  2. The modern, known as "ANSI" [from the ANSI standardization committee].

At first, we only had K&R:

#include <stdio.h>

void
f(i)
int i;
{

    printf("%d\n",i);
}

int
main(void)
{

    f();

    return 0;
}

When the C language was standardized, an alternate form of function prototype was added:

#include <stdio.h>

void
f(int i)
{

    printf("%d\n",i);
}

int
main(void)
{

    f();

    return 0;
}

These are how the functions are defined. But, if we put f in a separate file, we'd be left with the prototype declarations:

Here is K&R:

#include <stdio.h>

void f();

int
main(void)
{

    f();

    return 0;
}

Here is ANSI:

#include <stdio.h>

void f(int i);

int
main(void)
{

    f();

    return 0;
}

If you look closely at the K&R prototype, there is no way to discern the type for i.

That blemish was rectified with ANSI. If you look at that prototype, the type is for i is explicitly specified as int.


So, to answer your question, in your examples, the syntax without the int is a K&R style prototype/definition. When we add int, it is an ANSI prototype/definition.

K&R hasn't been widely used in new code since sometime in the 1990's (IIRC). But, the compilers understand it for backward compatibility with code written pre-ANSI.

Side note: I started programming in C in 1981, so I was using K&R for at least a decade (A lot of this post comes from [fading] memory). After I got a compiler that supported ANSI, after a few months trying out the ANSI prototypes on new code, I converted all my old code to use ANSI.


For more information ...

See: Function declaration: K&R vs ANSI

And, Alternative (K&R) C syntax for function declaration versus prototypes

Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • The question asks about a compiler complaint about the number of arguments, and this answer does not speak to that at all. – Eric Postpischil Dec 03 '20 at 11:18
  • Interesting! I've never actually run across any K&R style definitions before. I'm going to give the accepted answer to dbush simply because they mentioned the lack of prototype in K&R style, but +1 for a very interesting answer. – bisen2 Dec 03 '20 at 21:44
1

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.

dbush
  • 205,898
  • 23
  • 218
  • 273