The C standard specifies two forms of definition for main
for a
hosted implementation:
int main(void) { /* ... */ }
and
int main(int argc, char *argv[]) { /* ... */ }
It may be defined in ways that are "equivalent" to the above (for
example, you can change the parameter names, replace int
by a typedef
name defined as int
, or write char *argv[]
as char **argv
).
It may also be defined "in some other implementation-defined manner"
-- which means that things like int main(int argc, char *argv[],
char *envp)
are valid if the implementation documents them.
The "in some other implementation-defined manner" clause was not in
the 1989/1990 standard; it was added by the 1999 standard (but the
earlier standard permitted extensions, so an implementation could
still permit other forms).
My question is this: Given the current (2011) ISO C standard, is a
definition of the form
int main() { /* ... */ }
valid and portable for all hosted implementations?
(Note that I am not addressing either void main
or the use of
int main()
without parentheses in C++. This is just about the
distinction between int main(void)
and int main()
in ISO C.)
No.
According to the normative wording of the standard, a definition
using empty parentheses without the void
keyword is not one of the
forms that must be accepted, and strictly speaking the behavior of
such a program is undefined.
Reference:
N1570
section 5.1.2.2.1. (The published 2011 ISO C standard, which is not
freely available, has the same wording as the N1570 draft.)
Paragraph 1 says:
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.
The use of the word "shall" outside a constraint means that any
program that violates it has undefined behavior. So if, for example, I write:
double main(unsigned long ocelots) { return ocelots / 3.14159; }
a conforming compiler isn't required to print a diagnostic, but it's
also not required either to compile the program or, if it does compile
it, to have it behave in any particular manner.
If int main()
were equivalent to int main(void)
, then it
would be valid and portable to any conforming hosted implementation.
But it's not equivalent.
int main(void) { }
provides both a declaration (in this case, a prototype) and a
definition. The declaration, by using the void
keyword, specifies that the function has no parameters. The definition specifies the same thing.
If I instead write:
int main() { }
then I'm using an old-style declaration and definition. (Such
declarations and definitions are obsolescent, but they're still
part of the language definition, and all conforming compilers must
still support them.)
As a declaration, it doesn't specify the number or type(s) of arguments
expected by the function. As a definition, it defines no parameters,
but compilers needn't use that information to diagnose incorrect calls.
DR #317 includes the C standard committee's 2006 ruling that a definition with ()
does not provide a prototype equivalent to one with (void)
(thanks to hvd for finding that reference).
C allows main
to be called recursively. Suppose I write:
int main(void) {
if (0) {
main(42);
}
}
The visible prototype int main(void)
specifies that main
takes
no arguments. A call that attempts to pass one or more arguments
violates a constraint, requiring a compile-time diagnostic.
Or suppose I write:
int main() {
if (0) {
main(42);
}
}
If the call main(42)
were executed, it would have undefined behavior
-- but it doesn't violate a constraint, and no diagnostic is required.
Since it's protected by if (0)
, the call never happens, and
the undefined behavior never actually occurs. If we assume that
int main()
is valid, then this program must be accepted by any
conforming compiler. But because of that, it demonstrates that
int main()
is not equivalent to int main(void)
, and therefore
is not covered by 5.1.2.2.1.
Conclusion: Following the wording of the standard, an
implementation is permitted to document that int main() { }
is
permitted. If it doesn't document it, it's still permitted to accept
it without complaint. But a conforming compiler may also reject
int main() { }
, because it is not one of the forms permitted by
the standard, and its behavior is therefore undefined.
But there's still an open question: Was that the intent of the authors
of the standard?
Prior to the publication of the 1989 ANSI C standard, the void
keyword did not exist. Pre-ANSI (K&R) C programs would define main
either as
main()
or as
int main()
A major goal of the ANSI standard was to add new features (including
prototypes) without breaking existing pre-ANSI code. Stating that
int main()
is no longer valid would have violated that goal.
My suspicion is that the authors of the C standard did not intend
to make int main()
invalid. But the standard as written does not
reflect that intent; it at least permits a conforming C compiler
to reject int main()
.
Practically speaking, you can almost certainly get away with it.
Every C compiler I've ever tried will accept
int main() { return 0; }
without complaint, with behavior equivalent to
int main(void) { return 0; }
But for a variety of reasons:
- Following both the letter and the intent of the standard;
- Avoiding the use of an obsolescent feature (a future standard could remove old-style function definitions);
- Maintaining good coding habits (the difference between
()
and (void)
is important for functions other than main
that are actually called by other functions
I recommend always writing int main(void)
rather than int main()
.
It states the intent more clearly, and you can be 100% sure that your
compiler will accept it, rather than 99.9%.
A strong indication that int main()
is meant to be valid, regardless of whether the standard accurately gives the wording to make it valid, is the fact that int main()
is occasionally used in the standard without anyone raising any objection. While examples are not normative, they do indicate intent.
6.5.3.4 The sizeof and _Alignof operators
8 EXAMPLE 3 In this example, the size of a variable length array is computed and returned from a function:
#include <stddef.h>
size_t fsize3(int n)
{
char b[n+3]; // variable length array
return sizeof b; // execution time sizeof
}
int main()
{
size_t size;
size = fsize3(10); // fsize3 returns 13
return 0;
}
6.7.6.3 Function declarators (including prototypes)
20 EXAMPLE 4 The following prototype has a variably modified parameter.
void addscalar(int n, int m,
double a[n][n*m+300], double x);
int main()
{
double b[4][308];
addscalar(4, 2, b, 2.17);
return 0;
}
void addscalar(int n, int m,
double a[n][n*m+300], double x)
{
for (int i = 0; i < n; i++)
for (int j = 0, k = n*m+300; j < k; j++)
// a is a pointer to a VLA with n*m+300 elements
a[i][j] += x;
}
As for the actual normative text of the standard, I think too much is being read into "equivalent". It should be pretty clear that
int main (int argc, char *argv[]) {
(void) argc; (void) argv;
return 0;
}
is valid, and that
int main (int x, char *y[]) {
(void) argc; (void) argv;
return 0;
}
is invalid. Nonetheless, the standard explicitly states in the normative text that any names may be used, meaning that int main (int argc, char *argv[])
and int main (int x, char *y[])
count as equivalent for the purposes of 5.1.2.2.1. The strict English meaning of the word "equivalent" is not how it is meant to be read.
A somewhat looser interpretation of the word is what Keith Thompson suggests in his answer.
An equally valid even looser interpretation of the word does allow int main()
: both int main(void)
and int main()
define main
as a function returning int
and taking no parameters.
Neither the standard nor any official DRs currently answer the question of which interpretation is intended, so the question is unanswerable, but the examples strongly suggest that last interpretation.
Yes.
int main() { /* ... */ }
is equivalent to
int main(void) { /* ... */ }
N1570 5.1.2.2.1/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; or in some other implementation-defined manner.
6.7.6.3/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.
(emphasis mine)
As is clearly stated by the standard, the definition int main() { /* ... */ }
does specify that the funtion main
has no parameters. And it is clear to all of us, that this function definition does specify that the return type of the function main
is int
. And, since 5.1.2.2.1 does not require the declaration of main
to have a prototype, we can safely affirm that the definition int main() { /* ... */ }
satisfies all the requirement imposed by the standard (It [the main funtion] shall be defined with a return type of int and with no parameters, or [some other forms] .
).
Nonetheless you should never use int main() {}
in your code, because "The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature." (6.11.6), and because this form of definition does not include a function prototype declarator, the compiler won't check whether the number and types of arguments are correct.
N1570 6.5.2.2/8
No other conversions are performed implicitly; in particular, the number and types of
arguments are not compared with those of the parameters in a function definition that
does not include a function prototype declarator.
(emphasis mine)