Why does an invalid use of C function compile fine without any warnings?
Asked Answered
K

4

6

I'm more like C++ than C, but this simple example of code is big surprise for me:

int foo() {
    return 3;
}

int main() {
    int x;
    foo(&x);
    return x;
}

https://godbolt.org/z/4ov9nTzjM

No warnings, no linking issues still this code is invalid.

I've stumbled on this when some beginner filed a question with code having this strange issue. Question was asked on some other site (none English one) and I'd like to provide him better explanation why it compiles (and learn something about C). His original code if some one needs to see it.

I'm suspecting that this sis somehow related to implicit function declaration, but I'm not fully sure. Why compiler doesn't complain that foo is called with arguments?

Update:

Here is an assembly:

foo:
        push    rbp
        mov     rbp, rsp
        mov     eax, 3
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        lea     rax, [rbp-4]
        mov     rdi, rax
        mov     eax, 0
        call    foo
        mov     eax, DWORD PTR [rbp-4]
        leave
        ret

as you can see x remains uninitialized.

Koral answered 17/1, 2022 at 12:29 Comment(1)
Related, Function declaration vs. definition C - Stack OverflowAlabaster
E
3

The compiler should issue a message for this program

int foo() {
    return 3;
}

int main() {
    int x;
    foo(&x);
    return x;
}

because the function foo called with an argument though its identifier list is empty. So the program has undefined behavior.

According to the C Standard *6.7.6.3 Function declarators (including prototypes) )

14 An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

So the program is invalid.

You could make it a valid program the following way

int foo();

int main() {
    int x;
    foo(&x);
    return x;
}

int foo( int *p ) {
    return 3;
}

Though the compiler can issue a warning that the parameter p is not used.

In this case the function declaration that is not its definition means that there is no information about the number and types of parameters.

Opposite to C in C++ this declaration

int foo();

is equivalent to

int foo( void );
Ergot answered 17/1, 2022 at 12:44 Comment(7)
So it looks like a bug in GCC.Deafanddumb
@IanAbbott If the compiler does not issue a message then it seems it is a bug.Ergot
Apparently, it is not a bug after all (although the program has UB). See Bug 64526 and DR#314. A warning would be nice, though!Deafanddumb
The C standard does not require a compiler to issue a diagnostic for this. 6.7.6.3 14 does say the definition specifies the function has no parameters, but this is not a rule that is violated by the code in the question. The call has undefined behavior because 6.5.2.2 6 says “If the number of arguments does not equal the number of parameters, the behavior is undefined.” However, that is not in a Constraints section, so violating it does not require a diagnostic. It is a run-time error. A compiler would be allowed to define the call to execute normally.Waldron
@IanAbbott Thanks. A good references.Ergot
@EricPostpischil Should issue and shall issue have different meanings.Ergot
@VladfromMoscow: Nothing in my comment says otherwise or is predicated upon the contrary.Waldron
P
3

This function is 100% correct.

In the C language int foo() { means: define function foo returning int and taking unspecified number of parameters.

Your confusion comes from C++ plus where int foo(void) and int foo() mean exactly the same.

In the C language to define function which does not take any parameters you need to define it as:

int foo(void) {
Polymorphism answered 17/1, 2022 at 12:33 Comment(1)
int foo() { does not define the function to take an unspecified number of parameters. In a declaration that is not a definition, it declares the function to take an unknown number of parameters. In a definition, it defines the function to take no parameters. Then the call in main has undefined behavior because C 2018 6.5.2.2 6 says “If the number of arguments does not equal the number of parameters, the behavior is undefined.”Waldron
E
3

The compiler should issue a message for this program

int foo() {
    return 3;
}

int main() {
    int x;
    foo(&x);
    return x;
}

because the function foo called with an argument though its identifier list is empty. So the program has undefined behavior.

According to the C Standard *6.7.6.3 Function declarators (including prototypes) )

14 An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

So the program is invalid.

You could make it a valid program the following way

int foo();

int main() {
    int x;
    foo(&x);
    return x;
}

int foo( int *p ) {
    return 3;
}

Though the compiler can issue a warning that the parameter p is not used.

In this case the function declaration that is not its definition means that there is no information about the number and types of parameters.

Opposite to C in C++ this declaration

int foo();

is equivalent to

int foo( void );
Ergot answered 17/1, 2022 at 12:44 Comment(7)
So it looks like a bug in GCC.Deafanddumb
@IanAbbott If the compiler does not issue a message then it seems it is a bug.Ergot
Apparently, it is not a bug after all (although the program has UB). See Bug 64526 and DR#314. A warning would be nice, though!Deafanddumb
The C standard does not require a compiler to issue a diagnostic for this. 6.7.6.3 14 does say the definition specifies the function has no parameters, but this is not a rule that is violated by the code in the question. The call has undefined behavior because 6.5.2.2 6 says “If the number of arguments does not equal the number of parameters, the behavior is undefined.” However, that is not in a Constraints section, so violating it does not require a diagnostic. It is a run-time error. A compiler would be allowed to define the call to execute normally.Waldron
@IanAbbott Thanks. A good references.Ergot
@EricPostpischil Should issue and shall issue have different meanings.Ergot
@VladfromMoscow: Nothing in my comment says otherwise or is predicated upon the contrary.Waldron
W
0

Empty parentheses are part of an old C grammar for function declarations that did not provide type checking. Prior to the creation of parameter lists with types, function definitions had a form like this:

int foo(a, b, c)
char  a;
int  *b;
float c;
{
    // body of function
}

Then function declarations that were not definitions would just use (), as in int foo();. So a function definition would show what arguments a function expected, including the number of them and their types, but a declaration would not.

Of course, function definitions are often not visible when compiling a source file, as the definitions are in other files, although of course a declaration may be provided, often in header files. So, when a function is called, the compiler would not generally see the definition and would not generally know what arguments the function required. So it could not check the arguments. Thus, there is no requirement that the compiler provide a diagnostic message for a mismatch with functions declared with this old grammar.

The programmer is responsible for making the arguments match. The function definition does specify how many parameters the function has and does specify their types, and the C standard does not define the behavior if there is a mismatch. But that is a run-time issue; it is not something the compiler is required to diagnose, and it is not something the compiler can diagnose if the function definition is not visible to it.

When the definition is visible, the compiler could issue a diagnostic, especially if the definition appears prior to the call. Although GCC does not diagnose this, Clang does, if the definition appears first. So this is a missed opportunity for the GCC developers.

When the grammar for function prototypes (function declarations with types for the parameters) was created, () could not be used to indicate the function had no parameters, since it was already in use for the old grammar. So the notation (void) was invented for this; to declare, outside of a definition, that a function takes no parameters, you muse use (void), as in int foo(void);. This may also be used in a function definition, int foo(void) { … }.

When a function declaration has this new form, the compiler is required to check the number and types of arguments in a call to the function and to issue a diagnostic if there is a mismatch.

Waldron answered 17/1, 2022 at 13:46 Comment(0)
D
0

The function

int foo() {
    return 3;
}

with an empty parameter list means in C a function with an undefined list of parameters. It's a legacy syntax from the times of pre-ANSI. Today it is considered an incomplete function definition (as you can later provide a more precise definition).

The compiler gives no warning because this construction was the only available when there were no function prototypes to define the interface of a function and the compiler didn't check the parameters passed to a function.

It is old, legacy use of C syntax, like the following (actually still accepted today, and surprises many language experts when they discover it is still accepted):

/* no function return type, defaults to int */
main(argc, argv)
/* no definition for argc, defaults to int */
char *argv[]; /* parameters are defined between the parameter list
               * and the function body block */
{
    /* ... */
}

which was a reminiscence of fortran.

The correct way of defining a function with no parameters is:

int foo(void)
{
    /* ... */
}

which makes the compiler to emit an error (and not a warning) if you try to call it with parameters.

Disharmony answered 17/1, 2022 at 20:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.