cfunction-declarationfunction-prototypes

Why is it legal to change function prototype after its definition?


int test();
int test(){
 return 5;
}
int test(int in);

int main() {
   return test();
}

I know it's ok to have multiple (and similar) declarations of a function, and only 1 single definition.

My question is related to a declaration after its own definition. Does the compiler pick the last prototype it has encountered as the true prototype? even if it comes father its full definition?

This doesn't compile with the error saying: error: too few arguments to function 'test'


Solution

  • There are three declarations of test in your sample code:

    1. int test(); is a declaration that is not a prototype1 or a definition2.
    2. int test() { return 5; } is a declaration that is a definition of the function.
    3. int test(int in); is a declaration that is a prototype for the function.

    The plain declaration, number 1, is incomplete.3 It leaves it unstated whether there are any parameters or what their types are. This declaration can be completed by either declaration 2 or declaration 3, but not by both. If both 2 and 3 are present, a compiler should give you a warning or error message.4

    Specifically, to answer your question “Why is it legal to change function prototype after its definition?”: After an incomplete declaration, you can change what is known about the function type by adding more information, using either an old-style definition or a modern prototype. You cannot make changes other than adding more information.5 (After a prototype or a definition, the function type is complete, and you cannot make any changes.)

    Declaration 2 matches an old style of function definition in which the parameters were defined after the closing ) and before the opening {:

    return-type name-of-function(names-of-parameters)
    declarations of parameters
    {
        body-of-function
    }
    

    For example:

    int test(n, x)
    int n;
    double x;
    {
        …
    }
    

    When there are no parameters in this style of definition, it means the function takes no parameters. Note the difference:

    As you can see, these are compatible: Not stating how many parameters there are is compatible with saying there are no parameters.6

    Declaration 3 is a modern declaration that says there is one parameter, with type int. As with 2, declarations 1 and 3 are compatible: Not stating how many parameters there are or what types they might have is compatible with saying there is one parameter of type int.

    However, declarations 2 and 3 are incompatible:

    Footnotes

    1 A function prototype declares the types of its parameters. Since () is empty, there are no parameter types declared. (Due to C history, () in a function declaration means nothing is said about the parameters, and (void) is a prototype saying there are no parameters.)

    2 A function definition includes the body of the function, the brace-enclosed compound-statement containing the code the function executes.

    3 “Incomplete” is used here in the ordinary English sense, not the sense in which the C standard applies to object types.

    4 This is required by the constraint in C 2018 6.7 4: “All declarations in the same scope that refer to the same object or function shall specify compatible types.”

    5 There may be some minor changes possible. For example, qualifiers such as const can be added to or removed from parameters, although this does not change the function type for purposes of compatibility.

    6 Specific rules for function type compatibility are in C 2018 6.7.6.3 15.