I don't get why does this code compile?
#include <stdio.h>
void foo() {
printf("Hello\n");
}
int main() {
const char *str = "bar";
foo(str);
return 0;
}
gcc doesn't even throw a warning that I am passing too many arguments to foo(). Is this expected behavior?
For legacy reasons, declaring a function with
()
for a parameter list essentially means “figure out the parameters when the function is called”. To specify that a function has no parameters, use(void)
.Edit: I feel like I am racking up reputation in this problem for being old. Just so you kids know what programming used to be like, here is my first program. (Not C; it shows you what we had to work with before that.)
Both the C99 Standard (6.7.5.3) and the C11 Standard (6.7.6.3) state:
Since the declaration of foo is part of a definition, the declaration specifies that foo takes 0 arguments, so the call foo(str) is at least morally wrong. But as described below, there are different degrees of "wrong" in C, and compilers may differ in how they deal with certain kinds of "wrong".
To take a slightly simpler example, consider the following program:
If I compile the above using Clang:
If I compile with gcc 4.8 I don't get any errors or warnings, even with -Wall. A previous answer suggested using -Wstrict-prototypes, which correctly reports that the definition of f is not in prototype form, but this is really not the point. The C Standard(s) allow a function definition in a non-prototype form such as the one above and the Standards clearly state that this definition specifies that the function takes 0 arguments.
Now there is a constraint (C11 Sec. 6.5.2.2):
However, this constraint does not apply in this case, since the type of the function does not include a prototype. But here is a subsequent statement in the semantics section (not a "constraint"), that does apply:
Hence the function call does result in undefined behavior (i.e., the program is not "strictly conforming"). However, the Standard only requires an implementation to report a diagnostic message when an actual constraint is violated, and in this case, there is no violation of a constraint. Hence gcc is not required to report an error or warning in order to be a "conforming implementation".
So I think the answer to the question, why does gcc allow it?, is that gcc is not required to report anything, since this is not a constraint violation. Moreover gcc does not claim to report every kind of undefined behavior, even with -Wall or -Wpedantic. It is undefined behavior, which means the implementation can choose how to deal with it, and gcc has chosen to compile it without warnings (and apparently it just ignores the argument).
In C, a function declared with an empty parameter list accepts an arbitrary number of arguments when being called, which are subject to the usual arithmetic promotions. It is the responsibility of the caller to ensure that the arguments supplied are appropriate for the definition of the function.
To declare a function taking zero arguments, you need to write
void foo(void);
.This is for historic reasons; originally, C functions didn't have prototypes, as C evolved from B, a typeless language. When prototypes were added, the original typeless declarations were left in the language for backwards compatibility.
To get gcc to warn about empty parameter lists, use
-Wstrict-prototypes
:in C, this code does not violates a constraint (it would if it was defined in its prototype-form with
void foo(void) {/*...*/}
) and as there is no constraint violation, the compiler is not required to issue a diagnostic.But this program has undefined behavior according to the following C rules:
From:
the
foo
function does not provide a prototype.From:
the
foo(str)
function call is undefined behavior.C does not mandate the implementation to issue a diagnostic for a program that invokes undefined behavior but your program is still an erroneous program.