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 ofint
and with no parameters:int main(void) { /* ... */ }
or with two parameters (referred to here as
argc
andargv
, 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.
6
5 Answers
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.
7
-
Hmm, As I read "An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters." (C11 §6.7.6.3 14) says to me that
foo() { .... }
, as a function definition that a laterfoo(23, "elephants");
is an invalid call. I would expectint main() { ... }
and a later call likemain(23, "elephants");
to also be invalid.– chux – Reinstate Monica22 hours ago
-
1
Side note: Why can you call
main
recursively in C but not in C++? Although not super useful, I could devise a C program:#include <stdio.h> int main(int argc,char **argv) { printf("%s: %sn",argv[0],argv[argc - 1]); if (argc > 2) main(argc - 1,argv); return 0; }
Build it and run it with:./program hello world goodbye galaxy
and get:./program: galaxy ./program: goodbye ./program: world ./program: hello
Why won't this work with C++? It seems to be some arbitrary rule. What's the rationale for this rule?– Craig Estey21 hours ago
-
2
@CraigEstey Say rather an obsolete rule. Cfront-era C++ implementations injected code at the beginning of
main
to run global constructors, and that code was not idempotent, so recursively callingmain
would run the global constructors again and hilarity would ensue. I imagine the rule persists today largely because nobody in WG21 thinks it's important enough to bother changing.– zwol21 hours ago
-
1
@CraigEstey: MinGW puts a call to a
__main
init function at the top ofmain:
in the asm even in C mode, I think instead of having their libc init stuff run from DLL on-load hooks. Presumably it's idempotent at least in C mode, perhaps with an already-initialized check of a static flag. What library has the __main function reference in gcc assembly output / How does GCC implement __attribute__((constructor)) on MinGW? (GCC supports static-constructor functions even in C mode, so maybe not libc init)– Peter Cordes5 hours ago
-
1
@PeterCordes: They can't put their libc stuff in DLL on-load hooks because of
-static
. There's no such thing as an EXE on-load hook. Now why they didn't put that call incrt0.o
(or its moral equivalent) I have no idea.– Joshua2 hours ago
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.
11
-
Re “All code fragments in the standard are considered to be examples”: Code in examples are non-normative because examples are non-normative. There are fragments of code not in examples, used in exposition, and they are integral to the normative part of the standard. We cannot say the
int main(void) { /* ... */ }
in 5.1.2.2.1 is example code and not expositive code because then theint main(int argc, char *argv[]) { /* ... */ }
would also be non-normative and then there is nothing in the normative standard that says the first parameter has typeint
and the second has typechar *[]
.– Eric Postpischil13 hours ago
-
Notably the parts you quote from 5.1.2.2.1 are for hosted systems – you are quoting the hosted system sub-chapter, which is only normative for strictly conforming hosted programs. Whereas 6.7.6.3 is normative for all C programs. For example a C23 freestanding implementation might declare main as
void main ()
and that will be equivalent tovoid main (void)
.– Lundin10 hours ago
-
@EricPostpischil No, really, all code fragments are non-normative examples. It does not actually say that anywhere in the text of the standard but this has been the consistent interpretation of the committee since before C1999. (It follows that the case you cite is a defect — the requirement for the first parameter of
main
to beint
and the second to bechar *[]
needs to be lifted to normative text.)– zwol10 hours ago
-
@Lundin That seems unrelated to the point made by my answer.
– zwol10 hours ago
-
No it isn't, because the text you claim is normative isn't normative to C but normative to the sub category of hosted C programs. Though I realize you inherited this from the question itself.
– Lundin9 hours ago
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
- 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.
16
-
Foot note 10 is not certainly an elaboration of "what means the word equivalent:" It is a least an example of equivalent. Other than that most of the answer is good.
– chux – Reinstate Monica13 hours ago
-
@chux-ReinstateMonica In any case the equivalence does not mean in C17 declaring main with an empty identifier list. It shall be declared with a parameter list including the empty parameter list.
– Vlad from Moscow13 hours ago
-
Re “But relative to the function main the Standard requires that it will be declared with a parameter type list”: No such assertion appears in C 2017.
– Eric Postpischil13 hours ago
-
Re “As it is seen the parameter type list may not be optional. It is the identifier list that may be optional”: The grammar makes no such distinction. These two options for a direct-declarator establish that a direct-declarator may be “direct-declarator
(
parameter-type-list)
, “direct-declarator(
identifier-list)
”, or “direct-declarator(
)
”. No distinction between whether(
)
means the parameter-type-list is missing or the identifier-list is missing is made either by the grammar rules or by the text of the standard.– Eric Postpischil12 hours ago
-
1
Re “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 typevoid
.”: That passage from paragraph 10 says if the declarator has the formD(void)
, then it specifies the function has no parameters, not that if the function has no parameters, its declarator must have the formD(void)
, and it says nothing about what formmain
must have.– Eric Postpischil12 hours ago
Are
int main()
andint 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.
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, thenint main ()
andint 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[])
orint32_t main (void)
in a system whereint
==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)
vsint 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);
1
-
Even if examples aren't normative, their existence would strongly suggest to me imply an intention and expectation that implementations will behave in the indicated fashion any time there isn't a very strong and compelling reason for doing otherwise.
– supercat7 hours ago
On many traditional Unix systems, you can also declare
main
asint main(int argc, char **argv, char **envp)
, in which caseenvp
is aNULL
-terminated array of environment variable definitions. I just tried this on my Linux system, and it still works. Not sure what ANSI C has to say about it. It's probably considered non-standard.yesterday
@TomKarzes: this is covered by or in some other implementation-defined manner
yesterday
The old wording in C11 §6.7.6.3 p14 was "An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters." So a definition of
main
asint main() { /*code here*/ }
has been equivalent toint main(void) { /*code here*/ }
at least as far back as C11.yesterday
@user3386109: That's not how GCC or clang treats it with
-std=c11
. godbolt.org/z/5zf1dr1dK shows GCC acceptingmain(123)
after anint main(){}
definition with no warnings even at-Wall -Wextra -pedantic
. Clang warns about deprecation but doesn't error, with the message saying it's only an error in C2x. GCC-std=c23
does error. It that item in the standard just talking about there being no named vars you can access to read any parameters that got passed? Otherwise GCC and Clang would be deviating from the C11 standard, perhaps intentionally, but then what's C23 changing?6 hours ago
@PeterCordes You've misconstrued my comment. I was addressing the question that the OP asked. The question specifically asks whether "
int main() {/*...*/}
is a valid definition formain
" Emphasis on definition added. Notice also the emphasis on definition in my comment. Your comment is about whether a definition with an empty argument list acts as a prototype. The answer to that question in C11 is "No". In C23, the answer is "Yes".6 hours ago