Are int main() and int main(void) equivalent prototypes in C23?

2.8k views Asked by At

C23 introduced new semantics in function declarators:

6.7.6.3 Function declarators

[...]

13   For a function declarator without a parameter type list: the effect is as if it were declared with a parameter type list consisting of the keyword void. A function declarator provides a prototype for the function.

This seems to imply that a function definition with an empty parameter list can be written equivalently with () or (void).

Yet this equivalence does not seem guaranteed for the main function:

5.1.2.2.1 Program startup

The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

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

or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

int main(int argc, char *argv[]) { /* ... */ }

or equivalent or in some other implementation-defined manner.

This does not seem to guarantee that int main() { /* ... */ } is a valid definition for main, or does equivalent cover this variant?


It troubles me that the 2 examples in C17 that use the syntax int main() (in 6.5.3.4 and 6.7.6.3) have been changed to use int main(void) in the latest C23 draft.

5

There are 5 answers

0
chux - Reinstate Monica On

Are int main() and int main(void) equivalent prototypes in C23?

Yes, for function declarations.

C23 draft N1570 has the revised specification:

For a function declarator without a parameter type list: the effect is as if it were declared with a parameter type list consisting of the keyword void. A function declarator provides a prototype for the function. C23 § 6.7.6.3 13

I understand this to apply to all functions including main().

The specifications about main do not restrict this.

7
Jonathan Leffler On

In C17 and earlier versions of the standard, int main() { … } does not provide a prototype for main() but is otherwise equivalent to int main(void) { … }.

In C23, int main() { … } does provide a prototype for main() and is fully equivalent, except for spelling, to int main(void) { … }.

The difference only matters if you call main() recursively — something that is allowed in C and disallowed in C++. With int main() in C17 or earlier, a recursive call like main(23, "elephants"); is allowed because there is no prototype specified for main() (assuming that the definition of main() is visible before the recursive call). With int main(void), that is not allowed because there is a prototype in scope that says "no arguments".

Note what is said in What should main() return in C and C++? That has extensive discussions, including that the C17 and earlier standards use both int main() and int main(void) in their (non-normative) examples. It also points out what Microsoft specifies for Windows systems and what Annex J "Common Extensions" mentions (both recognize int main(int argc, char **argv, char **envp)). Apple even has a fourth optional argument to main()int main(int argc, char **argv, char **envp, char **apple) that behaves like argv or envp. I need to update my answer for C23 sometime soon.

16
Vlad from Moscow On

Let's at first consider how the function main shall be declared according to the C17 Standard. The C17 Standard, section 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.

That is empty parentheses can be used in a function definition when the function has an empty identifier list. But relative to the function main the Standard requires that it will be declared with a parameter type list.

From the C grammar:

5 If, in the declaration "T D1", D1 has the form

D ( parameter-type-list )

or

D ( identifier-listopt )

and the type specified for ident in the declaration "T D" is "derived-declarator-type-list T", then the type specified for ident is "derived-declarator-type-list function returning the unqualified version of T"

As it is seen the parameter type list may not be optional. It is the identifier list that may be optional.

Further (p. #10):

10 The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters.

That is the function main without parameters shall be declared with a parameter list that contains unnamed parameter of type void.

From the C17 Standard (5.1.2.2.1 Program startup):

1 The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters:

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

or with two parameters (referred to here as argc and argv, though any names may be used, as they are local to the function in which they are declared):

int main(int argc, char *argv[]) { /* ... */ }

or equivalent;10) or in some other implementation-defined manner.

Now let's read the footnote 10 that elaborates what means the word equivalent:

10)Thus, int can be replaced by a typedef name defined as int, or the type of argv can be written as char ** argv, and so on.

There is nothing said that the function declaration of the function main with an empty parameter list may be rewritten with a function declaration with an empty identifier list.

That is the Standard requires that the function main shall be declared with the function prototype. And examples of such declarations of main are presented in the quote provided above.

From the C17 Standard (6.9.1 Function definitions)

Semantics

7 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,166) 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.

Pay attention to that the verb shall in the C standard means the following

  1. Conformance

1 In this document, "shall" is to be interpreted as a requirement on an implementation or on a program; conversely, "shall not" is to be interpreted as a prohibition.

What is changed in the C23 Standard relative to the declaration of main? Nothing! If you will read the section 5.1.2.2.1 Program startup of the C23 Standard Draft you will see that there are used the same text relative to the declaration of main without parameters.

Yes, now in the C23 Standard a function declaration with empty parentheses in the function declarator has another meaning. The parameter type list can be optional. The identifier list is excluded from function declarators. But the declaration of main satisfies the same requirements as in the C17 Standard. It shall be declared as written in the C23 Standard like

int main( void )

and even the footnote stays the same as in the C17 Standard. There is no a word that the function main may be declared like

int main()

in the section 5.1.2.2.1 Program startup of the C23 Standard.

The form of the declaration of main with empty list of parameters was preserved in the C23 Standard in the same way as in the C17 Standard.

And @zwol mistakenly claims relative to the declaration of main that "All code fragments in the standard are considered to be examples, and, therefore, non-normative." The section 5.1.2.2.1 Program startup of the both Standards, the C17 Standard and the C23 Standard, has normative description of how the function main shall be declared.

You should pay attention to that it will be a serious change in the normative description of main from preceding C Standards and the C23 Standard. And the C23 Standard should reflect that in its description "what documents, for all intents and purposes, have been applied to this draft (C23)". However in this list of documents nothing is said about changes in the requirements for declaration of the function main. I found only the following document that has some relation "N2432 Remove support for function definitions with identifier lists".

If to adopt that I am mistaken then as a consequence of that it may be concluded that the C23 Standard has a defect because this section was transferred from the C17 Standard without any changes but its meaning was changed. At least in the C23 Standard there should be added a note or a footnote in this section that clarifies changes relative to the previous C Standard.

18
zwol On

All code fragments in the standard are considered to be examples, and, therefore, non-normative.

The normative requirement is what the text says: "[main] shall be defined with a return type of int and with no parameters[, or ...]". In C2023, int main() { ... } defines main with a return type of int and with no parameters, so it meets the requirement.

As pointed out in the comments on the question, C2011 had language that meant a definition int main() { ... } would have, under that standard, defined main with a return type of int and with no parameters, although a declaration int main(); would not have declared main with no parameters. I cannot conveniently check C1999 or earlier from this computer.

1
Lundin On

What is normative in the C standard:

  • 6.7.6.3 is normative for all function declarators.
  • The entry point for a C program is always a function in all C systems (see 5.1.2).
  • If main is the entry point for a C program, then int main () and int main (void) are equivalent as per C23 6.7.6.3.

Notably:

  • Examples, including code examples are not normative. Neither are notes or foot notes. These are all examples of informative parts. For details check: Are notes and examples in the core language specification of the C++ Standard non-normative?

  • Chapter 5.1.2.2.1 is only normative for strictly conforming hosted system programs. It bears no relevance for a general C program.

  • As noted in the answer by @zwol, the parts in 5.1.2.2.1 that are normative (for hosted system programs) is the text "It shall be defined with a return type of int and with no parameters" and not the example.

  • Furthermore, 5.1.2.2.1 has the normative text "or equivalent". This was always meant for covering cases such as int main (int foo, char* bar[]) or int32_t main (void) in a system where int == int32_t, and so on. Equivalent examples where only coding style or naming changes.

    As it happens, in C23 this "or equivalent" part will also apply to int main (void) vs int main () since they are equivalent forms.

  • 5.1.2.2.1 also has the text "or in some other implementation-defined manner".
    Similarly 5.1.2.1 for freestanding systems: "the name and type of the function called at program startup are implementation-defined".

    This means that as long as the function obeys the rules for how functions are declared, it can be named anything and declared in any manner, as long as this is documented by the implementation (compiler). However, a program relying on such will no longer be strictly conforming (C17 or C23 4 §5) since it relies on implementation-defined means; a freestanding program can therefore never be strictly conforming.

    It is however possible to in a conforming program (hosted/freestanding) to create a function with any name, return type or parameters as the entry point of the program, as long as the compiler documents and supports such.

    A program entry function with a format int function_name() still has to obey 6.7.6.3 however.

Common example of a conforming hosted system function (impl.defined):

int WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);

Common example of a conforming freestanding system function (impl.defined):

void main (void);