26

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

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631

3 Answers3

27

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

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • 3
    I wanted to add something so badly; having grown up in the days of less-than-compliant compilers and tutorials written for them; but, really you've covered this ground fully :) – Edwin Buck Mar 22 '15 at 04:32
  • 1
    This is a really good answer! Thank you for sharing the example as well! – Anirudh Ramanathan Mar 22 '15 at 04:40
  • 1
    It should be noted that any violations in this case are relatively meaningless as long as you don't use the parameters passed in and declare a "normal" (ie register-sized) return. Any stack mismatch only happens after `main` returns, at which point your program has already finished. – Blindy Mar 22 '15 at 04:45
  • 5
    I'd be happer going with the interpretation that `int main()` is permitted , and the standard is defective – M.M Mar 22 '15 at 05:33
  • 1
    @Keith Thompson If I am not mistaken it was you who argued with me early that function main may be declared like int main().:) – Vlad from Moscow Mar 22 '15 at 05:47
  • @VladfromMoscow: Do you have a link? – Keith Thompson Mar 22 '15 at 05:49
  • @Keith Thompson It is very difficult to find now the link because the discussion was several months ago. :) – Vlad from Moscow Mar 22 '15 at 05:51
  • 2
    The [C99 rationale](http://www.open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf) may clarify the intent a bit. Program execution says: "`main` is the only function that may portably be declared either with zero or two arguments. (The number of other functions’ arguments must match exactly between invocation and definition.) This special case simply recognizes the widespread practice of leaving off the arguments to main when the program does not access the program argument strings. [continued]" –  Mar 22 '15 at 10:52
  • 1
    While many implementations support more than two arguments to main, such practice is neither blessed nor forbidden by the Standard; a program that defines `main` with three arguments is not strictly conforming (see §J.5.1.)." Furthermore, 6.2.5 Types: "Finally, a function prototype list that has no arguments is written as `f(void)`, because `f()` retains its old meaning that nothing is said about the arguments." –  Mar 22 '15 at 10:53
  • 5
    Also, I am *extremely* skeptical of your "Avoiding the use of an obsolescent feature (a future standard could remove old-style function definitions);" argument. Even if a future standard removes old-style function definitions, surely that future standard will make `()` equivalent to `(void)` just like C++ does. Arguing that a future standard might not do so is like arguing that a future standard might make all integer literals base 11. Sure, technically we can't rule it out, but come on, we all know that's not going to happen. –  Mar 22 '15 at 10:54
  • 8
    Re-phrasing my earlier comment: the argument that `int main()` is not equivalent to `int main(void)` as a declaration (as your answer shows), but is nonetheless equivalent to it as a definition, is really not proved or disproved by your answer. Yes, `int main() { if (0) { main(42); } }` doesn't require a diagnostic, but that is because the declaration is different from `int main(void)`, and there is still a potentially legitimate argument that the definition is equivalent. –  Mar 22 '15 at 11:00
  • @VladfromMoscow Fwiw (I've been on a SEDE kick lately), I couldn't find the conversation you were referring to, at least [in posts that both you and Keith had comments on](http://data.stackexchange.com/stackoverflow/query/291600). Either it was deleted or it happened in a chat room or on another site. This is not criticism; I was just curious and couldn't find it, mostly it seemed like an interesting query to make. – Jason C Mar 22 '15 at 17:08
  • @Jason C I remember such a discussion in comments to some post but it will be difficult to me to find that post because it was several months ago. – Vlad from Moscow Mar 22 '15 at 17:32
  • Well, [have fun](http://data.stackexchange.com/stackoverflow/query/291600/comment-conversation?Username1=Vlad+from+Moscow&Username2=Keith+Thompson) (modified to take user names, for easier searching on other sites)! That query might be useful in the future as well. – Jason C Mar 22 '15 at 17:44
  • 2
    The text "It shall be defined with a return type of int and with no parameters: `int main(void) { /* ... */ }`" is clearly defective. It's unclear whether the code sample is the only permitted form, or if it is just an example of permitted forms; in which case `int main() {}` also satisfies the criteria in the text. – M.M Mar 23 '15 at 05:54
  • @MattMcNabb: I don't think that's a problem. Paragraph 8 of the Foreword specifies which parts of the standard are normative and which are "for information only". Non-normative examples are clearly marked with the word "EXAMPLE". The code snippets in 5.1.2.2.1 are not marked as examples (nor are they notes or footnotes), so they're normative. – Keith Thompson Mar 28 '15 at 18:49
  • The question was about what the normative wording of the standard says about how `main` can be portably defined, and I'm going to accept my own answer on that basis. Nevertheless, hvd's answer makes an excellent argument that the committee *intended* to permit `int main(){}`. – Keith Thompson Mar 28 '15 at 18:54
  • The link to DR #317 is informative, and I haven't seen that DR before. It seems like I have seen more instances of `int main() {}` lately on SO than ever before; the only excuse for this is ignorance coming from C++, or lazy typing. Someone once told me that MSVC defined `int main() {}`, but [the MSVC docs suggest otherwise](https://learn.microsoft.com/en-us/cpp/c-language/arguments-to-main?view=vs-2019). – ad absurdum Jan 26 '20 at 16:57
  • @exnihilo Visual Studio emphasizes C++ over C. In a very quick test, I didn't see a way to ask it to create a C source file. – Keith Thompson Jan 26 '20 at 20:55
  • @KeithThompson -- what about this: ["By default, the MSVC compiler treats all files that end in .c as C source code...."](https://learn.microsoft.com/en-us/cpp/build/walkthrough-compile-a-c-program-on-the-command-line?view=vs-2019)? – ad absurdum Jan 26 '20 at 21:16
  • @exnihilo: MSVC will accept a C source file with either `int main(void)` or `int main()` (which is permitted by the standard). When it creates a C++ source file, it uses `int main()`. My question is whether there's a way to have it create a C source file, and if so how it defines `main`. – Keith Thompson Jan 26 '20 at 22:26
  • @KeithThompson -- isn't the point of your answer that `int main()` is _not_ permitted unless specifically allowed as a compiler extension? The [first link that I gave](https://docs.microsoft.com/en-us/cpp/c-language/arguments-to-main?view=vs-2019) shows the two signatures sanctioned in the Standard, and additionally `int main( int argc, char *argv[], char *envp[] )`. It does not include `int main()`. DR#317 which you linked to seems to say that `int main()` does not provide a prototype, and that calling `main()` would lead to UB (though I don't see how 6.5.2.2p6 says this). What have I missed? – ad absurdum Jan 26 '20 at 23:04
  • Note that I have always bought the obsolescent feature argument as allowing compilers to accept `int main()`, but DR #317 seems to disallow this; or I am misreading something there. – ad absurdum Jan 26 '20 at 23:05
  • 1
    @exnihilo: `int main()` (given a strict reading of the standard) has undefined behavior. It doesn't violate a constraint or syntax rule, so no diagnostic is required. A conforming compiler can accept it without complaint, and most or all do. Another conforming compiler could reject it. – Keith Thompson Jan 26 '20 at 23:09
  • It can't be undefined behavior, if the standard uses this form itself. You are arguing about how the sentence actually stands there and that it would be not permitted and invoke undefined behavior to use *any* form which is not *equivalent* to `int main(void)`, such as in fact is `int main()` but not what its actual content/meaning is. The quote states "*It shall be defined with a return type of int and with no parameters*" - this is fulfilled with `int main()` as 6.7.6.3/14 describes "*An empty list in a function declarator that is part of a definition*... – RobertS supports Monica Cellio Jun 25 '20 at 15:43
  • *...of that function specifies that the function has no parameters.*" - `int main()` specifies that `main` has no parameters and is what the above quote requires. That the sentence points at `int main(void) {...}` is unfortunate and the thing we all struggling here about, but that you say `int main()` would be not valid and not portable, plus the behavior is undefined, is IMHO not correct. The standard uses this form itself. If it would be invalid and non-portable, why would they use this form then in the (generic) standard? – RobertS supports Monica Cellio Jun 25 '20 at 16:20
  • @M.M [Ref](https://stackoverflow.com/questions/29190986/is-int-main-without-void-valid-and-portable-in-iso-c#comment46619324_29190987) If it wouldn't be permitted, they wouldn't use it in the standard. Implicitly, by its use, it fulfills/satisfies the criteria. – RobertS supports Monica Cellio Jun 25 '20 at 16:21
  • @RobertSsupportsMonicaCellio I have little doubt that the authors of the standard *intended* to permit `int main() { /* ... */ }. You're right that the standard uses `int main()` in examples. Examples are explicitly non-normative. The `int main(void) { /* ... */ }` in N1570 5.1.2.2.1 is not a non-normative example; it's normative text. `int main()` is not entirely equivalent to `int main(void)`; the latter makes a call like `main(42)` a constraint violation. Again, I agree that `int main()` was *meant* to be supported (though obsolescent, see 6.11.6). But the standard fails to say so. – Keith Thompson Jun 26 '20 at 02:06
  • @KeithThompson Are the examples really non-normative? IMHO since the examples are part of the standard, they shall and do conform to the norms and since they do so they can also be seen as normative. --- We can roll of the question from another side: The wording is "*with no parameters*" which also includes the case of the empty parameter list. The wording is not "*with a parameter list of type `void`*". Maybe indeed, because they intended to say implicitly that they wanted to permit `int main()`, too, but since it is not explicitly stated otherwise, `int main()` can't really be seen... – RobertS supports Monica Cellio Jun 26 '20 at 07:51
  • ...as invalid and non-portable. That it is a use of an obsolescent feature, doesn't change anything at all that it is valid *yet*. I guess, we all agree that this is something which urgently requires clarity. We would really need to talk to one of the committee's members to get this. Unfortunately I don't know to ask one of those (if possible) and if the suggestion for a Technical Corrigendum is something which no longer is supported. Do you know how to start something like this? You are very long "in business" about C. – RobertS supports Monica Cellio Jun 26 '20 at 07:57
  • I've found a summary of Clarification Requests for C11, [here](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2256.htm), but I don't see where we can submit CRs for C18 currently. Plus it obviously also requires a Technical Corrigendum that this ambiguity should be no longer existing. – RobertS supports Monica Cellio Jun 26 '20 at 09:45
  • Yes, examples are non-normative. Paragraph 8 of the Foreword says so explicitly. (The Foreword is itself non-normative, but it refers to ISO/IEC directives.) The fact that `int main()` is obsolescent is not relevant to the question of whether it has defined behavior. Both the wording "with no parameters" and the code `int main(void) { /* ... */ }` are normative. I'd like this to be clarified, but I don't see it as urgent. As far as I know, all existing C compilers accept `int main()` (as the standard permits). – Keith Thompson Jun 26 '20 at 18:57
  • And the next C standard is likely to make this moot. See the the [N2454 draft](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf); [N2455](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2455.pdf) is the same with changes marked. See 6.7.6.3p13: "For a function declarator without a parameter type list: if it is part of a definition of that functionthe function has no parameters and the effect is as if it were declared with a parameter type list consisting of the keyword **`void`**; otherwise it specifies that no information about the number or types of the parameters is supplied." – Keith Thompson Jun 26 '20 at 19:00
  • @RobertSsupportsMonicaCellio So as of the next standard, `int main() { /* ... */ }` will be well defined (and will have subtly different semantics than it has in C11 and preceding). – Keith Thompson Jun 26 '20 at 19:01
15

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.

Community
  • 1
  • 1
  • The examples are an excellent argument, and tends to confirm that the *intent* was to permit `int main(){}` (more precisely to require conforming implementations to accept `int main(){}`). I still think that the normative wording of the standard makes `int main(){}` not strictly conforming, but that hinges in the interpretation of "equivalent". – Keith Thompson Mar 22 '15 at 18:58
  • What's invalid about your last example, other than references to undefined values? – cat Oct 21 '16 at 20:39
  • 1
    @cat It's exactly that `(void) argc; (void) argv;` is invalid because `argc` and `argv` haven't been declared. That's the point: in an overly-strict sense, `int main(int x, char *y[])` is not equivalent to `int main(int argc, char *argv[])`. –  Oct 21 '16 at 20:54
8

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)

cpplearner
  • 13,776
  • 2
  • 47
  • 72
  • 1
    You should add that 5.1.2.2.1 contains the words “or equivalent;”. This allows for instance `int main(int c, char **v) {…`, and if you take the words “or equivalent” to apply to both cases that precede them, then you have a strong argument (together with 6.7.6.3:14 that you cited). – Pascal Cuoq Mar 22 '15 at 13:08
  • 1
    This answer is *definitely* missing important details. Keith Thompson's answer already showed a concrete difference between `int main()` and `int main(void)`. –  Mar 22 '15 at 13:13
  • 3
    @hvd Actually, I think that this answer makes a strong point that the sentence “If we assume that int main() is valid, then this program must be accepted by any conforming compiler.” as used in Keith Thompson's answer is wrong. Because “an empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters”. – Pascal Cuoq Mar 22 '15 at 13:26
  • 3
    @PascalCuoq Keith Thompson is right about that, although I'll agree the standard could be clearer. See [DR #317](http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_317.htm) for the official interpretation. –  Mar 22 '15 at 13:28
  • @PascalCuoq: It specifies that the function has no parameters, but not that the function takes no arguments. More precisely, it doesn't require the compiler to diagnose calls that pass one or more arguments. (This is the weakness of non-prototype declarations and the reason ANSI added prototypes to the language.) For example, gcc doesn't complain about `void foo() { foo(42); }`; should it? IMHO it *should* warn, but the standard doesn't require a diagnostic. – Keith Thompson Mar 22 '15 at 18:46
  • 1
    @KeithThompson Yes, this is convincingly explained by DR317 that hvd dug out. I am still not convinced that just because `int main()` allows one to write invalid recursive calls, it should be considered forbidden, but hvd has written a comment to that effect under your answer that I have nothing to add to. – Pascal Cuoq Mar 22 '15 at 18:50
  • @hvd: Thanks for finding that reference; I've added it to my answer. – Keith Thompson Mar 22 '15 at 18:51
  • @PascalCuoq There's already too much debate on the meaning of "or equivalent". Let's find something more unambiguous (e.g. "It shall be defined with a return type of int and with no parameters."). – cpplearner Mar 23 '15 at 05:12
  • There is a defect report against that "... specifies that the function has no parameters", which I can't find at the moment, unfortunately. The committee agreed that the wording is ambiguous, but didn't want to touch anything about old-style declarations. – mafso Mar 23 '15 at 11:37
  • @mafso Uhhh, That reference would help so much. Can you at least give some more information about the DR? From when more or less? – RobertS supports Monica Cellio Jun 26 '20 at 08:32
  • I think you all talking around each other in the comments. `int main()` is in fact not equivalent to `int main (void)` that is what hvd and Keith are arguing about. But what PascalCuoq and cpplearner means is actual different: `int main()` satisfies the rule of "*It shall be defined with a return type of int and with no parameters.*" - It's different. – RobertS supports Monica Cellio Jun 26 '20 at 08:39
  • @cpplearner `int main (void)` is in fact *not* equivalent to `int main()`. That is what hvd meant, Keith would concern in his answer and what Keith also repeated himself. To state they would be equivalent is incorrect. Please change that. – RobertS supports Monica Cellio Jun 26 '20 at 08:42