tinycc-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Tinycc-devel] Clarification about log message in commit 48df89e10e


From: Michael Matz
Subject: Re: [Tinycc-devel] Clarification about log message in commit 48df89e10e
Date: Mon, 19 Apr 2021 17:54:21 +0200 (CEST)
User-agent: Alpine 2.21 (LSU 202 2017-01-01)

Hello,

On Sat, 17 Apr 2021, Stefanos wrote:

No particular reason most probably (except in the case when specifically
the standard mandated behaviour of 'int main()' is tested).  If you prefer
the void form for new tests use it.  (please don't change existing tests
except for good reasons)

The reason I asked this question is because I was reading the C standard and
found the following parts at 5.1.2.2.1 [1] and at 6.7.5.3/14 [2]:

   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; 9) or in some other implementation-defined manner.

and

   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.

In other words, all the tests that use `int main()` are open to accept
any number of parameters, correct?

This is all a bit academic, but anyway:

The difference is in "that is part of a definition" and "that is not part of a definition". So:

  int main();

declares main as function but not anything about parameters (main has no prototype then). While:

  int main() { ...; return 0; }

declares (and defines) main to be a function having no parameters. Even the first variant (i.e. missing prototype without parameter info) is no license to make main (or any other function) accept any number of parameters. This is because "any number of params" would be a stdarg function, which must be declared and defined with a parameter type list that includes an ellipsis at the end. You could call such function with arbitrary number and types of arguments, and the compiler wouldn't reject this, but at runtime it won't work. The prototype is what is checked, so without one no checks are done. But even without prototype the calls need to match the implementation of the function at run time (otherwise: undefined behaviour).

Additionally the "or in some other implementation-defined manner" gives us leeway to accept more declarations than strictly required, which makes us okay to accept:

  void main(void);

(i.e. without return type). That is good, because in the wild this usage of main() happens, so we better accept it as well.

Was this implemented intentionally or just happened?

I'm not sure what "this" refers to here, but we basically need to accept any of these forms of decls and defs of main:

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

We need to accept these in such way that the user has to be able to write a prototype for main, and has to be able to _not_ have to write a prototype for it before definition. If he chooses to write a prototype it must be compatible with the given definition as usual. We need not accept other forms of main. E.g. GCC warns with other forms, but TCC does not. Sometimes it's convenient (when you know something about the C libraries implementation) to define main like so:

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

or even add another one (auxv) on some libc's, so adding more checks for validity would have to be done carefully to not disallow valid uses in the wild.

The reason why main() is so special-cased in the standard is historic.

I'm not sure if the above answers your question(s), if not, try asking more ;-)


Ciao,
Michael.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]