Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
480 views
in Technique[技术] by (71.8m points)

language lawyer - Is int main() { } (without "void") valid and portable in ISO C?

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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%.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...