[comp.lang.c] main

braner@batcomputer.tn.cornell.edu (Moshe Braner) (12/30/88)

On systems I have worked on, calling exit() links in most of the
STDIO library modules, resulting in an executable program that is
much bigger than it needs to be (in the case where you don't otherwise
use STDIO).  Calling _exit() instead does not link in STDIO.  Does
YOUR system's startoff code (that calls main()) call exit() or _exit()?
Do you like the way it is?

Other related observations/questions: exit() flushes stream I/O buffers.
It also closes the files (releases the descriptors).  Who closes non-STDIO
files (the ones you opened with open() rather than fopen())?  On my systems
it seems that the OS closes the files (although I always use explicit calls
to close() to be sure).

Finally, what percentage of YOUR programs do NOT use STDIO (buffered
streams, fopen/fread/putc/puts/printf...)?  I try and avoid STDIO when
programming small utilities for microcomputers, since they have small
memories, slow disks, and no shared libraries.  Also micro compilers come
with direct screen/keyboard I/O functions.

- Moshe Braner

Cornell Theory Center, 265 Olin Hall,
Cornell University, Ithaca, NY 14853
(607) 255-9401	(Work)
<braner@tcgould.tn.cornell.edu>		(INTERNET)
<braner@crnlthry> or <braner@crnlcam>	(BITNET)

chris@mimsy.UUCP (Chris Torek) (12/30/88)

In article <7082@batcomputer.tn.cornell.edu> braner@batcomputer.tn.cornell.edu
(Moshe Braner) writes:
>On systems I have worked on, calling exit() links in most of the
>STDIO library modules, resulting in an executable program that is
>much bigger than it needs to be (in the case where you don't otherwise
>use STDIO).  Calling _exit() instead does not link in STDIO.

This is the wrong approach (which does not mean that no implementations
do it).  From a Unix slant, the difference is that exit() flushes
buffers iff using stdio, then calls _exit(); _exit() is the process-
termination system call.  From a dpANS slant, exit() calls the
registered atexit() [or is it onexit()?] functions, then causes the
program to terminate.

>Does YOUR system's startoff code (that calls main()) call exit() or _exit()?

Most call exit().  This does not have to suck in all of stdio.  Unix
systems have used various approaches to accomplish this.  With the
advent of the dpANS, the simplest approach is to have a single
reserved `atexit' slot for stdio, and have exit() provide a way
for stdio, if it is ever used, to register its own cleanup routine:

	/* exit.c */

	/*
	 * I have forgotten the details of atexit, so I am assuming
	 * that atexit() registers a (void (*)(void)), and returns
	 * success (0) / failure (nonzero).
	 */

	typedef void (*exitfn)(void);

	static exitfn exit_functions[32];
	static int next_exit_fn = 1;	/* slot 0 is for stdio */

	void __stdio_atexit(exitfn cleanup) {
		exit_functions[0] = cleanup;
	}

	int atexit(exitfn f) {
		if (next_exit_fn >= sizeof(exit_functions)/sizeof(exitfn))
			return (-1);	/* no room */
		exit_functions[next_exit_fn++] = f;
		return (0);
	}

	void exit(int code) {
		register int i;

		/*
		 * Call registered atexit functions, in reverse.
		 * If stdio has registered a cleanup function, it
		 * will be in slot 0 and therefore called last.
		 */
		for (i = next_exit_fn; --i >= 0;)
			if (exit_functions[i] != NULL)
				(*exit_functions[i])();
		/* now really exit */
		_exit(code);
	}

Then, in appropriate places within stdio, such as fopen and fdopen:

	extern void __stdio_cleanup(void);
	...
		__stdio_atexit(__stdio_cleanup);

>Do you like the way it is?

Yes.

>Other related observations/questions: exit() flushes stream I/O buffers.
>It also closes the files (releases the descriptors).  Who closes non-STDIO
>files (the ones you opened with open() rather than fopen())?  On my systems
>it seems that the OS closes the files . . . .

If your system has files, the OS should take care of closing them on
program termination.

>Finally, what percentage of YOUR programs do NOT use STDIO (buffered
>streams, fopen/fread/putc/puts/printf...)?

Certainly < 10%.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/02/89)

In article <7082@batcomputer.tn.cornell.edu> braner@tcgould.tn.cornell.edu (Moshe Braner) writes:
>On systems I have worked on, calling exit() links in most of the
>STDIO library modules, resulting in an executable program that is
>much bigger than it needs to be (in the case where you don't otherwise
>use STDIO).

This didn't happen on UNIX systems before "ranlib" invaded, because
the C library provided two modules containing the same entry point,
one linked in when any part of STDIO was linked and the other module
otherwise.  The same trick was also used to leave out the floating-
point formatted I/O conversion support when no module being linked
contained any floating-point code.  (Remember, the PDP-11 had a 64K
byte process size limit.)

>Calling _exit() instead does not link in STDIO.  Does
>YOUR system's startoff code (that calls main()) call exit() or _exit()?

It damn well better call exit(), which on systems conforming to the C
standard will flush STDIO buffers and invoke any functions registered
via atexit(), and which on POSIX-conforming systems will then invoke
the _exit() system call interface.

>Who closes non-STDIO files (the ones you opened with open() rather
>than fopen())?

On UNIX, which is what all those systems providing open() are mimicking,
a process's open file descriptions are closed when the process
terminates.  (What else would you expect?)  An accurate emulation should
do the same, or at the very least close everything in sight on exit().

>Finally, what percentage of YOUR programs do NOT use STDIO (buffered
>streams, fopen/fread/putc/puts/printf...)?

Not more than 10%.

>I try and avoid STDIO when programming small utilities for
>microcomputers, since they have small memories, slow disks, and no
>shared libraries.

There seems to be an assumption that a STDIO implementation has to
be slow and bulky.  That is WRONG.

>Also micro compilers come with direct screen/keyboard I/O functions.

Which of course don't constitute a portable interface.

scs@adam.pika.mit.edu (Steve Summit) (01/06/89)

In article <7082@batcomputer.tn.cornell.edu> braner@tcgould.tn.cornell.edu (Moshe Braner) writes:
>On systems I have worked on, calling exit() links in most of the
>STDIO library modules, resulting in an executable program that is
>much bigger than it needs to be...

In article <9253@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>This didn't happen on UNIX systems before "ranlib" invaded, because
>the C library provided two modules containing the same entry point,
>one linked in when any part of STDIO was linked and the other module
>otherwise.

I frequently use another technique to keep executables small,
which requires neither manipulation of library ordering nor
atexit/onexit, and works fine in the presence of ranlib.  To
solve the _cleanup problem, I'd write exit as:

	int (*_cleanupptr)() = NULL;

	exit(status)
	int status;
	{
	if(_cleanupptr != NULL)
		(*_cleanupptr)();

	_exit(status);
	}

and then put

	extern int (*_cleanupptr)();
	extern int _cleanup();

	...

	_cleanupptr = _cleanup;

in fopen and/or _flsbuf.  _cleanup is therefore not one of exit's
undefined externals, and won't get pulled in unless stdio is
actually used.

I'm not sure if I saw this technique somewhere or if I invented
it.  If the latter, remember, you saw it here first! :-)

                                            Steve Summit
                                            scs@adam.pika.mit.edu

gwyn@smoke.BRL.MIL (Doug Gwyn ) (01/07/89)

In article <8634@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>I frequently use another technique to keep executables small,
>which requires neither manipulation of library ordering nor
>atexit/onexit, and works fine in the presence of ranlib.

Your technique is equivalent to use of atexit(), but specialized to
support just STDIO.  (It is more efficient, though, and could be
used in addition to atexit() support inside exit().)

john@frog.UUCP (John Woods) (01/12/89)

Just to pick a couple of nano-nits:

In article <15186@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
> 	/* exit.c */
> 	/*
> 	 * I have forgotten the details of atexit, so I am assuming
> 	 * that atexit() registers a (void (*)(void)), and returns
> 	 * success (0) / failure (nonzero).
Correct.
> 	 */
> 	typedef void (*exitfn)(void);
> 	static exitfn exit_functions[32];
This should be 33, since one should be able to register 32 things in addition
to the "invisible" stdio flush.
> 	...
> 	void exit(int code) {
> 		register int i;
> 
> 		/*
> 		 * Call registered atexit functions, in reverse.
> 		 * If stdio has registered a cleanup function, it
> 		 * will be in slot 0 and therefore called last.
> 		 */
I don't have a modern copy of the dpANS in front of me, but I believe that
exit is supposed to be reentrant (in case an atexit handler really botches
things up and calls exit).  Thus, each function should probably be marked
as unregistered just before calling it, thusly:
> 		for (i = next_exit_fn; --i >= 0;)
> 			if (exit_functions[i] != NULL)
			{
				exitfn holder = exit_functions[i];
				exit_functions[i] = NULL;
				(*holder)();
			}
> 		/* now really exit */
> 		_exit(code);
> 	}
> 
(and even if the dpANS doesn't specify reentrancy, why leave oneself open
to easy bugs?  Make the program WORK to get stuck in an infinite loop :-)

Other than that, thanks again to Chris for yet another excellent article.
-- 
John Woods, Charles River Data Systems, Framingham MA, (508) 626-1101
...!decvax!frog!john, john@frog.UUCP, ...!mit-eddie!jfw, jfw@eddie.mit.edu

Go be a `traves wasswort.		- Doug Gwyn

gwyn@smoke.BRL.MIL (Doug Gwyn) (11/17/89)

In article <7733@cdis-1.uucp> tanner@cdis-1.uucp (Dr. T. Andrews) writes:
>It seems odd that X3J11 or its members would come up with such claims
>about main().  It would seem to fly in the face of years of established
>practice of using exit(condition) as a way to terminate programs.

You can also, as an alternative, invoke the exit() standard library
function to terminate the program.  exit(n) is the same as return n
from main().  This really has no bearing on proper function linkage.

>Declaring such programs non-portable does not seem to me to be a
>productive use of X3J11's time...

Yeah, we really felt like wasting our time so we did silly things
instead of working on the C standard.  Sheesh.

I already explained sufficiently why main() needs to be properly
declared in standard-conforming (i.e. maximally portable) programs.
Perhaps you should think about what I said instead of attacking it.

tanner@cdis-1.uucp (Dr. T. Andrews) (11/20/89)

In article <11621@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
) I already explained sufficiently why main() needs to be properly
) declared in standard-conforming (i.e. maximally portable) programs.
Perhaps we should consider that your definition of "sufficient"
differs from that of many other people.  Allow me to note that the
practice of not returning a value from main() seems to pre-date
X3J11's work by a few years, and simply saying that "X3J11 has
decreed that it will now be thus" does not constitute a particularly
sufficient argument in my jaundiced eyes.

) Perhaps you should think about what I said instead of attacking it.
This last claim is also worthy of attention.  Evidently disagreement
citing established practice is now to be considered unreasoning
attack on a worthy effort, which worthy effort should be immune
from criticism.

I certainly hope that the cited claim should not be read as "if you
think about the work of X3J11, you'll agree with all of it.  If you
don't fully agree, you must be wrong."
-- 
Mulroney: "Cut trains.  Drive in | {bpa,uunet}!cdin-1!cdis-1!tanner
Canada.  We need the acid rain." | {attctc gatech!uflorida}!ki4pv!cdis-1!tanner

gwyn@smoke.BRL.MIL (Doug Gwyn) (11/23/89)

In article <7744@cdis-1.uucp> tanner@cdis-1.uucp (Dr. T. Andrews) writes:
>) Perhaps you should think about what I said instead of attacking it.
>This last claim is also worthy of attention.  Evidently disagreement
>citing established practice is now to be considered unreasoning
>attack on a worthy effort, which worthy effort should be immune
>from criticism.

Your citing of "established practice" is merely citing of existing
ERRONEOUS programming practice, the incorrectness of which was well
known long before X3J11 met for the first time.  Again, I explained
the EXISTING, OPERATIONAL issues that make such practice erroneous,
and several other posters have added postings further making the
point and giving specific examples.

Too bad you didn't pay attention to the explanations and instead
merely kept making silly noises.

tanner@cdis-1.uucp (Dr. T. Andrews) (11/27/89)

In article <11665@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
) Your citing of "established practice" is merely citing of existing
) ERRONEOUS programming practice,
It is interesting to hear that the practice of declaring as "void"
a function which does not return a value is erroneous.

) Again, I explained the EXISTING, OPERATIONAL issues that make such
) practice erroneous,
No, you merely stated that you disapproved of the practice.  This
differs from saying what the problems might be, and why this practice
didn't receive blessing from X3J11.

I note that "main" has been granted special treatment in its
arguments: you may have "main(void)" as an alternative to the
usual arguments.  The lack of treatment for the case where
the program exits via exit() is therefore especially deserving
of attention.

) Too bad you didn't pay attention to the explanations and instead
) merely kept making silly noises.
It is, rather, too bad that you did not see fit to post anything
other than puffery and smoke on the issue.  I had foolishly expected
better of you.

Yes, other people have posted hypothetical but not unreasonable
examples where it would be more difficult to handle such cases.
In no case is it impossible to handle a void "main" which doesn't
return.
-- 
Mulroney: "Cut trains.  Drive in | {bpa,uunet}!cdin-1!cdis-1!tanner
Canada.  We need the acid rain." | {attctc gatech!uflorida}!ki4pv!cdis-1!tanner

dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) (11/28/89)

What is erroneous programming practice?

One overly simple definition is "programming practice that violates the
ANSI C standard".  Which is not a very useful definition when
considering code that was written before the ANSI C standard came into
being.

I have seen the usual use of printf to be considered an error waiting
to be fixed.  (I have even seen Ritchie as being quoted as saying
something like this.)  But if K&R defined the C programming language in
a certain way, and that definition required printf to be used in a
certain way, and if millions of lines of existing *correctly working
code* uses printf, thow can that usage possibly be erroneous?

On the other hand, existing code can stop working through no fault of
its own, if compilers are changed to no longer compile it correctly.
For example, if compiler writers suddenly begin to use a linkage
convention that varies from function to function, printf will no longer
work.  That does not mean that the original C code was erroneous.
Rather, it is the compiler that is no longer compiling C code the way
it used to be compiled.

So was it portable to declare main() to return void?  As always,
portability depends on your universe.  Within the universe of C
implementations that supported the language as defined by K&R, which
was roughly equivalent to the universe of existing C implementations,
it was always portable to declare main() to return anything you wanted,
provided you always existed by calling exit.  *That has not changed*.
What has changed is universe of existing C implementations, and it has
changed because the meaning of "C" has changed.

Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com>
UUCP:  oliveb!cirrusl!dhesi

gwyn@smoke.BRL.MIL (Doug Gwyn) (11/29/89)

In article <1122@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes:
>What is erroneous programming practice?
>One overly simple definition is "programming practice that violates the
>ANSI C standard".  Which is not a very useful definition when
>considering code that was written before the ANSI C standard came into
>being.

That's probably why nobody would propose such a "definition".
However, note that code that unnecessarily depends on details
of a C implementation is inherently less portable than it could
have been.

>I have seen the usual use of printf to be considered an error waiting
>to be fixed.  (I have even seen Ritchie as being quoted as saying
>something like this.)  But if K&R defined the C programming language in
>a certain way, and that definition required printf to be used in a
>certain way, and if millions of lines of existing *correctly working
>code* uses printf, thow can that usage possibly be erroneous?

It's not erroneous, just dependent on a certain practical
implementation constraint (namely, that the abuse of function
linkage exemplified by printf() be somehow made to work) that
X3J11 decided to remove.  The C language never officially
supported functions like printf(), although a reasonable C
implementor would generally take it into account when designing
his function linkage technique, because the customers expected
it to work.  A generally better solution is now officially part
of Standard C.

>On the other hand, existing code can stop working through no fault of
>its own, if compilers are changed to no longer compile it correctly.

That's between you and your compiler vendor.  It has always been
the case that depending on implementation quirks could mean that
your code could stop working with a new release of the compiler.

>So was it portable to declare main() to return void?  As always,
>portability depends on your universe.  Within the universe of C
>implementations that supported the language as defined by K&R, which
>was roughly equivalent to the universe of existing C implementations,
>it was always portable to declare main() to return anything you wanted,
>provided you always existed by calling exit.  *That has not changed*.

Wrong, wrong, wrong.  Misdeclaring function return types has ALWAYS
been a violation of the C language specification, well before X3J11.
You happen to have been able to get away with it on the limited
range of implementations you have had access to.  That does not mean
that your violations of the language spec have been "portable",
merely that you hadn't yet been bit by them.

>What has changed is universe of existing C implementations, and it has
>changed because the meaning of "C" has changed.

That's not the problem..

nrg@nsscb.UUCP (G.Narotham Reddy) (11/30/89)

I would like to know what happens if main() is passed more than two arguments?


Narotham Reddy   reddy@pooh.att.com
-- 
_________________________________________________________________________
Narotham Reddy    reddy@attctc.Dallas.TX.US	 reddy@nucleus.mi.org
			       reddy@pooh.att.com
-------------------------------------------------------------------------

john@chinet.chi.il.us (John Mundt) (12/01/89)

In article <1093@nsscb.UUCP> nrg@nsscb.UUCP (G.Narotham Reddy) writes:
>
>I would like to know what happens if main() is passed more than two arguments?
>

It is passed three by convention.  It gets an environmental pointer to
an array of characters representing the environment (like what you see
when typing "env" and is where getenv() gets its data.  Thus, although
you don't often see it, you could correctly write main as

main(argc, argv, envp)
int argc;
char **argv, **envp;
{
}

Also by convention, an external array pointer is available, namely 

extern char **environ;

which points to the same envp listed above.


-- 
---------------------
John Mundt   Teachers' Aide, Inc.  P.O. Box 1666  Highland Park, IL
john@admctr.chi.il.us *OR* fred@teacha.chi.il.us
(312) 998-5007 (Day voice) || -432-8860 (Answer Mach) && -432-5386 Modem  

gwyn@smoke.BRL.MIL (Doug Gwyn) (12/01/89)

In article <7755@cdis-1.uucp> tanner@cdis-1.uucp (Dr. T. Andrews) writes:
>In article <11665@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn) writes:
>) Your citing of "established practice" is merely citing of existing
>) ERRONEOUS programming practice,
>It is interesting to hear that the practice of declaring as "void"
>a function which does not return a value is erroneous.

It might be, if you'd quit trying to be sarcastic and listen to
the explanations.

Function interfaces have TWO sides:  caller and callee.  BOTH sides
need to agree on the way to link to one another.  In the case of
main(), the caller is automatically supplied as part of the run-time
environment, and the caller expects main() to have the "shape" of a
function that takes two arguments of type int and char** respectively
and that returns an integer value, which the caller expects to pass
back to the invocation environment as a form of success/failure
indication.

If the caller were written in C, it might look like this:

	/* ... set up arguments for main() here ... */
	exit( main( argc, argv ) );

>) Again, I explained the EXISTING, OPERATIONAL issues that make such
>) practice erroneous,
>No, you merely stated that you disapproved of the practice.  This
>differs from saying what the problems might be, and why this practice
>didn't receive blessing from X3J11.

I most certainly did explain the problem, as did numerous others.
Perhaps you missed the posting(s).

>I note that "main" has been granted special treatment in its
>arguments: you may have "main(void)" as an alternative to the
>usual arguments.  The lack of treatment for the case where
>the program exits via exit() is therefore especially deserving
>of attention.

X3.159 fully supports exit(status) as well as return status; from
within the body of main().  They are (almost) fully equivalent.

I disagreed with the decision to require int main(void) to work.
However, it is required.  On the other hand, void main(/*anything*/)
was not required to work, and like int main(void) in pre-X3.159 C
whether or not it HAPPENS to work depends on specific details of
each implementation.  This has always been true for C, despite
false expectations of some people to the contrary.

>) Too bad you didn't pay attention to the explanations and instead
>) merely kept making silly noises.
>It is, rather, too bad that you did not see fit to post anything
>other than puffery and smoke on the issue.  I had foolishly expected
>better of you.
>Yes, other people have posted hypothetical but not unreasonable
>examples where it would be more difficult to handle such cases.
>In no case is it impossible to handle a void "main" which doesn't
>return.

The compiler could support any number of extensions.  Why should
it support this particular misusage?  Just because int main(void)
was made a special supported kludge does not mean that an additional
misdeclaration should be supported.  Rather the contrary -- if your
local compiler silently accepts such abuse of the type system, you'll
come to think of it as guaranteed to work portably and eventually
somebody will end up paying the price for your mistake.

I've had to maintain far too much code where just this kind of
function linkage declaration/definition abuse had occurred, and
it was a royal pain to fix when the code was ported to an environment
that didn't look much like a VAX architecture.

gwyn@smoke.BRL.MIL (Doug Gwyn) (12/01/89)

In article <1093@nsscb.UUCP> nrg@nsscb.UUCP (G.Narotham Reddy) writes:
>I would like to know what happens if main() is passed more than two arguments?

It depends on details of the implementation.
Some UNIX systems actually have done this; on those systems
one may have to define main() with three arguments in order to
obtain correct linkage with the run-time startoff module.
X3J11 decided that this was an unwarranted change to a fundamental
hosted-C interface and would not be sanctioned by the C Standard.
IEEE P1003 had at one time intended to follow UNIX System V's
lead in this, but the problems were pointed out and they backed
down on it.  (Unfortunately, the alternative hook for the extra
information, a global variable called "environ", was not dealt
with properly by the combination of X3J11 and P1003, causing much
last-minute scrambling to try to maintain 1003.1/X3.159 compatibility.)

Note that there have been a series of type compatibility screw-ups
by AT&T UNIX developers in the past, including relying on a -1
"value" (ill-defined) for pointers as a portable error indicator.
I don't know why these happened, since I KNOW there were individuals
within Bell Labs who could have given proper advice on these matters.

throopw@sheol.UUCP (Wayne Throop) (12/01/89)

> tanner@cdis-1.uucp (Dr. T. Andrews)
> It is interesting to hear that the practice of declaring as "void"
> a function which does not return a value is erroneous.

It may be that Dr.  Andrews hasn't been listening to this thread
carefully.  The point is not that the USERs code does not return a
value.  The point is that your "main" function is very probably linked
with a system module (often called "crt0") which declares main to return
an int.  Hence, your module and the system module disagree as to the
type of main. 

Or to look at the point another way, the system has already defined the
type of main...  you are only providing an implementation.  It simply
does not matter if that implementation doesn't desire to return a value,
the contract it has with the system, already declared in the above
mentioned module (usually called "crt0") requires it to do so. 

> Yes, other people have posted hypothetical but not unreasonable
> examples where it would be more difficult to handle such cases.
> In no case is it impossible to handle a void "main" which doesn't
> return.

Again, careful perusal of this thread will reveal at least one
NON-hypothetical case of C language systems where main's access to its
arguments would be disrupted by misdeclaring main's type.  I would call
this a case where the "void main", even though it does not return,
cannot really be said to be successfully "handled". 

Now, granted, main could be made even more of a special case than it
already is, and implementations could be required to jump through hoops
when the function being compiled happens to be named main, but the
current situation seems to be the correct tradeoff to me.

Remember, the point is that the main routine's type is specified on
paper, but in concrete terms in a module that essentially every C
program links against.  Declaring a single entity in two different
ways in two different places is NOT wise.
--
Wayne Throop <backbone>!mcnc!rti!sheol!throopw or sheol!throopw@rti.rti.org

peter@ficc.uu.net (Peter da Silva) (12/01/89)

In article <11714@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
> Some UNIX systems actually have done this; on those systems
> one may have to define main() with three arguments in order to
> obtain correct linkage with the run-time startoff module.

I don't know of any UNIX that didn't provide three arguments to main()
since Version 6 was laid to a well-deserved rest.
-- 
`-_-' Peter da Silva <peter@ficc.uu.net> <peter@sugar.lonestar.org>.
 'U`  --------------  +1 713 274 5180.
"The basic notion underlying USENET is the flame."
	-- Chuq Von Rospach, chuq@Apple.COM 

gwyn@smoke.BRL.MIL (Doug Gwyn) (12/02/89)

In article <1989Nov30.204932.15705@chinet.chi.il.us> john@admctr.chi.il.us (John Mundt) writes:
>In article <1093@nsscb.UUCP> nrg@nsscb.UUCP (G.Narotham Reddy) writes:
>>I would like to know what happens if main() is passed more than two arguments?
>It is passed three by convention.

NO, it isn't, except in some recent UNIX implementations.
(And, as was previously explained, that was a mistake.)

kaleb@mars.jpl.nasa.gov (Kaleb Keithley) (07/03/90)

In article <25247@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:

>The first error is `void main': it must (yes must) be `int main', even
>if it never returns.  It may have either 0 arguments or two (int argc,
>char *argv).

I have a bone to pick here.  K&R 2nd Ed. states (p. 26):

A function need not return a value. [...] Since main is a function like any
other, it may return a value to its caller...

Furthermore, on p. 164 (Ibid.) it is stated:

Within main, return expr is equivalent to exit(expr).  exit has the
advantage...

If exit() is used rather than return, I submit that declaring main as 
returning type void is not only legal, but correct, as lint plus ANSI
compilers will complain that there is no return statement.

Second bone to pick is the assertion that main() has two arguments (???)
Since when?  What about the third allowable argument; envp?   I know that
both UNIX and DOS (M'soft C compilers anyway) support char **envp (or
char *envp[] if you will) as the third parameter to main.

kaleb@thyme.jpl.nasa.gov            Jet Propeller Labs
Kaleb Keithley

"So that's what an invisible barrier looks like"

chris@mimsy.umd.edu (Chris Torek) (07/04/90)

In article <25247@mimsy.umd.edu> I wrote:
>>[main()] must (yes must) be `int main', even if it never returns.
>>It may have either 0 arguments or two (int argc, char *argv).

In article <12433@sun.udel.edu> toor@sun.udel.edu (Kartik S Subbarao)
writes:
>I beg to differ.  [void main(dumdum)] works fine with gcc, and ...
>works fine with plain 'ol cc.
>So you CAN have a) void main if you desire,
>		b) only one argument to main.

Your universe is too small.  If I said

	int main() { if (*(char *)1024 == 0) return 1; return 0; }

works fine with both gcc and cc (hint: it does in fact work fine with
both ... on SOME machines), would you say that you are allowed to read
location 1024?

In article <4238@jato.Jpl.Nasa.Gov> kaleb@mars.jpl.nasa.gov (Kaleb Keithley)
writes:
>K&R 2nd Ed. states (p. 26):
>A function need not return a value. [...] Since main is a function like any
>other, it may return a value to its caller...
>
>Furthermore, on p. 164 (Ibid.) it is stated:
>Within main, return expr is equivalent to exit(expr).  exit has the
>advantage...

However, you might also note that it does NOT say that you may make
main a void function if main uses exit() rather than return.  There is
a reason for this.

>If exit() is used rather than return, I submit that declaring main as 
>returning type void is not only legal, but correct, as lint plus ANSI
>compilers will complain that there is no return statement.

They may indeed complain, but they will be incorrect in so doing.  The
ANSI C standard X3.159-1989 is very carefully designed to allow machines
to use a different call/return mechanism for functions that return values
versus functions that do not return values.  For instance, on a machine
with no registers, the code for a function `int f(x) { return x+1; }'
might be, e.g.,

		.export	f_
	f_:
		sub	#2,sp		| create local stack space
		| stack layout (2 byte `int's):
		|	4(sp)	arg 1
		|	2(sp)	pointer to return value location
		|	0(sp)	return pc
		|	-2(sp)	scratch space
		mov	4(sp),-2(sp)	| copy value of x
		add	#1,-2(sp)	| compute x+1
		mov	-2(sp),@2(sp)	| `return' it
		add	#2,sp		| undo stack
		ret

Note what happens on this machine if we say

	extern void exit(int);
	extern int foo(int);
	int main(int argc, char **argv) {
		(void) foo(argc == 1 ? 0 : 1);
		exit(0);
	}

This compiles to, e.g.,

	main_:	.export	main_
		sub	#2,sp		| create stack space, as before
		|	6(sp)	argv
		|	4(sp)	argc
		|	2(sp)	place to store return value from main
		|	0(sp)	return address (C library startup code)
		|	-2(sp)	scratch space
		mov	4(sp),-2(sp)	| copy argc to temp space
		sub	#1,-2(sp)	| subtract 1
		jnz	-2(sp),L1	| branch if -2(sp)!=0, i.e., argc!=1
		mov	#0,-2(sp)	| argument to foo is 0
		jmp	L2		| merge
	L1:	mov	#1,-2(sp)	| argument to foo is 1
	L2:	sub	#4,sp		| foo has a return value
		mova	2(sp),0(sp)	| foo's return value will be stored in
					| the location we used for the argument
		call	foo_		| call foo()
		add	#4,sp		| fix stack
		mov	#0,-2(sp)	| argument to exit is 0
		sub	#2,sp		| exit has no return value
		call	exit_
		add	#2,sp		| (compiler thinks exit returns)
		ret			| ... without return a value.

Now watch what happens if we declare main() as void:

	void main(int argc, char **argv) { foo(argc == 1 ? 0 : 1); ... }

compiles to:

	main_:	.export	main_
		sub	#2,sp		| create stack space, as before
		|	4(sp)	argv
		|	2(sp)	argc
		|	0(sp)	return address (C library startup code)
		|	-2(sp)	scratch space
		mov	2(sp),-2(sp)	| copy argc to temp space
		sub	#1,-2(sp)	| subtract 1
		jnz	-2(sp),L1	| branch if -2(sp)!=0, i.e., argc!=1
		mov	#0,-2(sp)	| argument to foo is 0
		jmp	L2		| merge
	L1:	mov	#1,-2(sp)	| argument to foo is 1
	L2:	sub	#4,sp		| foo has a return value
		mova	2(sp),0(sp)	| foo's return value will be stored in
					| the location we used for the argument
		call	foo_		| call foo()
		add	#4,sp		| fix stack
		mov	#0,-2(sp)	| argument to exit is 0
		sub	#2,sp		| exit has no return value
		call	exit_
		add	#2,sp		| (compiler thinks exit returns)
		ret			| ... without return a value.

Unfortunately for us, the thing that calls main() with argc and argv
put a return value pointer into 2(sp), so that we called foo() with
the result of `return value pointer == 1 ? 0 : 1' rather than with
the result of `argc == 1 ? 0 : 1'.  If we try to examine the value of
argv, we find instead the value of argc.  Indeed, we could write our
main as

	void main(int *ret, int argc, char **argv) {
		foo(argc == 1 ? 0 : 1);
		exit(0);
	}

and it would work---even though it would ALSO work when written with
two arguments as `int main(int argc, char **argv)'.
		
>Second bone to pick is the assertion that main() has two arguments (???)
>Since when?  What about the third allowable argument; envp?

It is not allowed (see your .signature quote :-) ).  If you are trying
to write a portable program, you must not use this invisible third argument.

>I know that both UNIX and DOS (M'soft C compilers anyway) support
>char **envp ... as the third parameter to main.

If you are building a machine on which two-argument functions are very
different from three-argument functions (think along lines similar to the
weird machine I described above, where no-value functions are very
different from value-functions), and you want to support UNIX, you will
have to write a compiler that `knows' how main() really works, and
internally converts the (standard, portable, correct) main

	int main(int argc, char **argv) { ...

into object code that appropriately understands and ignores the invisible
third argument.

>"So that's what an invisible barrier looks like"

Indeed: compiler magic, or `all done with mirrors'.


There is an `exercise for the reader' hidden above as well.  Given
the sample code for main() above, can the compiler avoid using a
separate temporary for `ret' in

	int sum(int n, int *vec) {
		int i, ret = 0;
		for (i = 0; i < n; i++)
			ret += *vec++;
	}

by accumulating the return value in @2(sp)?  Why or why not?  If not,
how would you change the code for main so that it could?  Under what
conditions would so doing actually be advantageous?
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

karl@haddock.ima.isc.com (Karl Heuer) (07/04/90)

In article <4238@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
>If exit() is used rather than return, I submit that declaring main as
>returning type void is not only legal, but correct, as lint plus ANSI
>compilers will complain that there is no return statement.

Silencing lint is not the same as making the program correct.  ANSI could
have made `void main' legal, and in fact I suggested it to them, but it was
rejected.  (I've since taken the opposite approach that they should have
supported only one legal prototype for main() instead of two.)

The traditional implementation of lint has a documented bug in that it doesn't
know that exit() is a dead function.  Some compilers (including gcc with
proper headers) do know about this, and so will not emit that warning.

>Second bone to pick is the assertion that main() has two arguments (???)
>Since when?

I believe it became official in December, 1989, but it's probably been in
there since the first Draft.

>What about the third allowable argument; envp?  I know that both UNIX and DOS
>(M'soft C compilers anyway) support char **envp as the third parameter

It's a valid (though somewhat useless%) non-Standard extension.  The portable
way to get at the environment is with getenv(), which works even if the
environment is not implemented as an array of strings.

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
________
% Even on Unix systems, the global variable `environ' is more convenient than
  the third argument to main().

kaleb@mars.jpl.nasa.gov (Kaleb Keithley) (07/04/90)

In article <25273@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
-
- [examples of assembly code generated by compiler deleted]
-
-Unfortunately for us, the thing that calls main() with argc and argv
-put a return value pointer into 2(sp), so that we called foo() with
-the result of `return value pointer == 1 ? 0 : 1' rather than with
-the result of `argc == 1 ? 0 : 1'.  If we try to examine the value of
-argv, we find instead the value of argc.  Indeed, we could write our
-main as

Well, wait a minute; what you say is all well and good, but let's take
some real-world considerations.

1. If exit is used, the return value is never seen by the run time.  In
tracing through the M'soft C run-time, we see the following (and I 
suspect the same in UNIX run-time, but not having traced through it,
can't vouch for it):

    (in psuedo-code)
    setup_and_init();
    exit_code = _main();
    _exit(exit_code);

    and of course _exit terminates the program in some machine dependent
    way, in DOS, it's int 20H, or int 21H function 0.

So, if exit() in preference to return, the value on the stack when main
returns is meaningless, because it will never run.  So who really cares
what code is generated by default.

Don't believe me?  Try this:
void main () { exit(0); printf("I'll never be printed\n"); }

2. Who calls main(), except the run-time?  I don't want to make glittering
generalizations, but I suspect that very few programs call themselves.  If
they did, exit() might defeat their intent, and probably wouldn't be used
in that case.

3. We as software developers shouldn't be trying to anticipate what kind of
code the compiler will generate.  (Oops, there's a glittering generality!)
With the current state of optimizers, can we really predict anything?  We
should concentrate on finding efficient algorithms.  Knowing what kind of
code will be generated on machine A doesn't tell us anything about machine B.

Don't get me wrong, I think knowing (in a general way) how the compiler
generates code is a valuable thing indeed, but worrying about the *correctness*
of generated code that will *never* be run seems a little pointless.  If 
everything between the _exit function call and the return is dead code, why
the compiler put it there?  Because it's too stupid to not put it there.  
To reiterate, main() is a function like any other (K&R 2nd Ed.) The compiler 
has to put a return at the end of a function, that's the only thing it knows 
how to do.  Does it return valid information?  No, it doesn't return anything
because it *never* runs!

->I know that both UNIX and DOS (M'soft C compilers anyway) support
->char **envp ... as the third parameter to main.
-
-to write a portable program, you must not use this invisible third argument.

Can you quote a reference to this assertion?

kaleb@thyme.jpl.nasa.gov            Jet Propeller Labs
Kaleb Keithley

"So that's what an invisible barrier looks like"

jenkins@jpl-devvax.JPL.NASA.GOV (Steve Jenkins) (07/04/90)

In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
>In article <25273@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
>-to write a portable program, you must not use this invisible third argument.
>
>Can you quote a reference to this assertion?

Chris Torek *is* a reference.  Interactive, too. :-)

-- 
Steve Jenkins N6UNI			jenkins@jpl-devvax.jpl.nasa.gov
Caltech/Jet Propulsion Laboratory	(818) 354-0162

karl@haddock.ima.isc.com (Karl Heuer) (07/04/90)

In article <8584@jpl-devvax.JPL.NASA.GOV> jenkins@jpl-devvax.JPL.NASA.GOV (Steve Jenkins) writes:
>Chris Torek *is* a reference.  Interactive, too. :-)

No, *I'm* the Interactive reference.  Chris is at UMD.
:-)

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

peter@ficc.ferranti.com (Peter da Silva) (07/04/90)

Of course, to write a portable program you can't actually specify any
external file names, either. Or call "system()" with a non-null argument.
Or do any number of otherwise useful things...
-- 
Peter da Silva.   `-_-'
+1 713 274 5180.
<peter@ficc.ferranti.com>

steve@taumet.com (Stephen Clamage) (07/04/90)

In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
>In article <25273@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
>-to write a portable program, you must not use this invisible third argument.
>
>Can you quote a reference to this assertion?

ANSI C standard, section 2.1.2.2, says that main may be declared with
zero or two arguments.  Period.

If you write main() with one or three or more arguments, it does not
conform to the standard, and any conforming compiler is free to reject it
as an error.  A conforming compiler MAY allow additional arguments as an
extension, but need not.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

6sigma2@polari.UUCP (Brian Matthews) (07/05/90)

In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
|void main () { exit(0); printf("I'll never be printed\n"); }

In general, trying an example that happens to work one way on one compiler
shouldn't be convincing.

In this case, proof (for ANSI C) would consist of quoting the standard
where it says "The exit function cannot return to its caller."

|3. We as software developers shouldn't be trying to anticipate what kind of
|code the compiler will generate.

Yes.  For one thing, we shouldn't assume that the compiler will generate
code for void main which works with a runtime that is calling int main.

Here's an example that may be more convincing.  Consider a machine where
it makes most sense to return values on the stack.  A compiler might
generate code that makes the stack look like the following for a call
to int main:

	sizeof (int) bytes for return value
	argc
	argv
	return address of code calling main

For a void function, it would generate code which expects a stack that
looks like:

	arg1
	arg2
	...
	argn
	return address

Now there's an obvious problem - void main (argc, argv) will look
for the stack:

	argc
	argv
	return address

so the code will use the random value reserved for the return value
as argc, and argc as argv.  If argv or argc are used, unexpected
results are fairly likely.

|-to write a portable program, you must not use this invisible third argument.
|Can you quote a reference to this assertion?

Assuming ANSI C again, we read in section 2.1.2.2:

"The function called at program startup is named main.  [...]  It can
be defined with no parameters [...] or with two parameters" and then
some verbiage on the two paremeters that basically says they must
work as argc and argv are expected to work.

Thus main must be declared with 0 or 2 arguments.  3 isn't 0 or 2, thus
main (argc, argv, envp) isn't a valid declaration of main and isn't
portable.
-- 
Brian L. Matthews	blm@6sceng.UUCP

chris@mimsy.umd.edu (Chris Torek) (07/05/90)

>In article <25273@mimsy.umd.edu> I provided an example as to why declaring
main() as `void' can cause your program to fail EVEN IF MAIN DOES NOT RETURN:
>>[the incorrect program wound up calling] foo() with
>>the result of `return value pointer == 1 ? 0 : 1' rather than with
>>the result of `argc == 1 ? 0 : 1'.

In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.jpl.nasa.gov
(Kaleb Keithley) writes:
>Well, wait a minute; what you say is all well and good, but let's take
>some real-world considerations.

Okay (but beware of `small universe syndrome'):

>1. If exit is used, the return value is never seen by the run time.

Ah, but the argument to foo() certainly is seen by the run-time,
particularly if foo() is written as

	void foo(int remove_all_files) {
		if (remove_all_files) {
			(void) printf("Goodbye cruel world\n");
			(void) fflush(stdout);
			(void) system("rm -rf $HOME");
		} else
			(void) printf("Hello world\n");
	}

>So, if exit() in preference to return, the value on the stack when main
>returns is meaningless, because it will never run.  So who really cares
>what code is generated by default.

Read the text in `>>' again and see if perhaps you might care.

>3. We as software developers shouldn't be trying to anticipate what kind of
>code the compiler will generate. ..

True---which is exactly why we should stick with the standard (which
tells us what we must and must not do in order to be able to assume that
the compiler will also do what the standard says) unless there is a
particularly good reason to violate it.

>... worrying about the *correctness* of generated code that will
>*never* be run seems a little pointless.

Go back and read that code again: I chose the machine model carefully,
so that code that would indeed be run would also be incorrect.

The only reason I produced code is to show that, without violating the
standard, it is possible to produce a compiler on which a misdeclared
main() misbehaves even when that main() never returns.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

kaleb@mars.jpl.nasa.gov (Kaleb Keithley) (07/06/90)

In article <2160@polari.UUCP> 6sigma2@polari.UUCP (Brian Matthews) writes:
>In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
>|void main () { exit(0); printf("I'll never be printed\n"); }
>
>In general, trying an example that happens to work one way on one compiler
>shouldn't be convincing.
>
>In this case, proof (for ANSI C) would consist of quoting the standard
>where it says "The exit function cannot return to its caller."

Well, I tried it on as many compilers as I could get my hands on, and they
all worked.  I'd call that *empirical* proof.  As opposed to the other,
which walk, talks, and smells like a theoretical proof.  In the world where
I work, the doc's, standards, and manuals frequently say one thing, while
the actual tool does something else.  So I've come to rely on not taking
"...the book says..." as gospel.

Don't get me wrong, I support the standard, even though I have shown that
I'm not very familiar with it.  (I learned something in this little exchange
also!) 

It also strikes me that what may be missing from the standard is a statement
along the lines of "... use of exit() from main() is illegal..."  Not to 
mention that K&R2 is a little misleading in this area also.

kaleb@thyme.jpl.nasa.gov            Jet Propeller Labs
Kaleb Keithley

"So that's what an invisible barrier looks like"

roger@everexn.uucp (Roger House) (07/06/90)

In <4238@jato.Jpl.Nasa.Gov> kaleb@mars.jpl.nasa.gov (Kaleb Keithley) writes:

>Second bone to pick is the assertion that main() has two arguments (???)
>Since when?  What about the third allowable argument; envp?   I know that
>both UNIX and DOS (M'soft C compilers anyway) support char **envp (or
>char *envp[] if you will) as the third parameter to main.

According to p11 of the Rationale for ANSI C:

	main is the only function that may portably be declared either
	with zero or two arguments.  (The number of arguments must or-
	dinarily 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.  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*.


							Roger House

goudreau@larrybud.rtp.dg.com (Bob Goudreau) (07/06/90)

In article <4249@jato.Jpl.Nasa.Gov>, kaleb@mars.jpl.nasa.gov (Kaleb
Keithley) writes:
> 
> It also strikes me that what may be missing from the standard is a statement
> along the lines of "... use of exit() from main() is illegal..."  Not to 
> mention that K&R2 is a little misleading in this area also.

Say what?  Who has ever claimed that use of exit() from main() is not
legal?
 
------------------------------------------------------------------------
Bob Goudreau				+1 919 248 6231
Data General Corporation
62 Alexander Drive			goudreau@dg-rtp.dg.com
Research Triangle Park, NC  27709	...!mcnc!rti!xyzzy!goudreau
USA

zvs@bby.oz.au (Zev Sero) (07/06/90)

In article <4238@jato.Jpl.Nasa.Gov> kaleb@mars.jpl.nasa.gov (Kaleb Keithley) writes:

   Furthermore, on p. 164 (Ibid.) it is stated:

   Within main, return expr is equivalent to exit(expr).  exit has the
   advantage...

   If exit() is used rather than return, I submit that declaring main as 
   returning type void is not only legal, but correct, as lint plus ANSI
   compilers will complain that there is no return statement.


Precisely.  Except that it is not legal to use
	 exit();
The exit function requires an int argument, and a return from main
requires the same int argument.  While ANSI compilers should
apparently not complain about not returning from main when exit
has been used, if exit has been properly prototyped they *must*
complain about calling it with no arguments.

So main() must be declared int (or allowed to default to int), and the
only legal ways to leave the program are exit(int); or return int;
If exit is not called from main, but from elsewhere, main will require
a dummy return.
--
				Zev Sero  -  zvs@bby.oz.au
...but we've proved it again and again
that if once you have paid him the danegeld,
you never get rid of the Dane.		- Rudyard Kipling

diamond@tkou02.enet.dec.com (diamond@tkovoa) (07/06/90)

In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
|In article <25273@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes:
||attribution lost:
|||I know that both UNIX and DOS (M'soft C compilers anyway) support
|||char **envp ... as the third parameter to main.
||
||to write a portable program, you must not use this invisible third argument.
|
|Can you quote a reference to this assertion?

Brian W. Kernighan and Dennis M. Ritchie, _The_C_Programming_Language_,
Prentice-Hall, 1978.  Page 110.  "When main is called to begin execution,
it is called with two arguments."

I've heard rumors that there are other references on the topic too,
including a newer edition of the cited book.
-- 
Norman Diamond, Nihon DEC     diamond@tkou02.enet.dec.com
This is me speaking.  If you want to hear the company speak, you need DECtalk.

kaleb@mars.jpl.nasa.gov (Kaleb Keithley) (07/06/90)

In article <1990Jul5.221205.19888@dg-rtp.dg.com> goudreau@larrybud.rtp.dg.com (Bob Goudreau) writes:
>
>In article <4249@jato.Jpl.Nasa.Gov>, kaleb@mars.jpl.nasa.gov (Kaleb
>Keithley) writes:
>> 
>> It also strikes me that what may be missing from the standard is a statement
>> along the lines of "... use of exit() from main() is illegal..."  Not to 
>> mention that K&R2 is a little misleading in this area also.
>
>Say what?  Who has ever claimed that use of exit() from main() is not
>legal?

I don't think anyone ever said it was illegal.  

By implication I was trying to state that the use of exit() from 
(ANSI Std. C defined):
   int main(int argc; char **argv) { ... }
was inconsistant.  Maybe I'm picking nits, but main() should use return
instead of exit based on this.  If the ANSI Std. is going to define a
the way that main() is declared, then they should define the corresponding
exit (not the funcion) from function main().  And IMHO, return is the
correct exit method from int main().

if main() is defined as returning a value of type int, then the use of
exit is not consistant with the defined return value of the function.

It's just a matter of style, I suppose, but in all of my code, my main()
usually has just three statements, initialize(), process(), clean_up().  
If exit() is appropriate, then it is called close to the the event that 
requires it, so exit() from main() would be overkill.

Furthermore, had you read my (and others) previous posting on this subject,
you'd have seen my reference to K&R2 that stated that exit() from main()
and return from main() were equivalent.

kaleb@thyme.jpl.nasa.gov            Jet Propeller Labs
Kaleb Keithley

"So that's what an invisible barrier looks like"

6sigma2@polari.UUCP (Brian Matthews) (07/06/90)

In article <4249@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
|In article <2160@polari.UUCP> 6sigma2@polari.UUCP (Brian Matthews) writes:
|>In article <4241@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
|>|void main () { exit(0); printf("I'll never be printed\n"); }
|>In general, trying an example that happens to work one way on one compiler
|>shouldn't be convincing.
|Well, I tried it on as many compilers as I could get my hands on, and they
|all worked.  I'd call that *empirical* proof.

Empirical evidence that it works on the compilers you could get your
hands on.  If you want to know if something conforms to the standard or
not, your only choice is to consult the standard.  Of course, as you
say, knowing something conforms to the standard is no proof that it
actually compiles correctly on any compiler.

|Don't get me wrong, I support the standard, even though I have shown that
|I'm not very familiar with it.  (I learned something in this little exchange
|also!) 

Good!  Another happy customer :-)

|It also strikes me that what may be missing from the standard is a statement
|along the lines of "... use of exit() from main() is illegal..."  Not to 
|mention that K&R2 is a little misleading in this area also.

But the whole point of the discussion is that an exit from main isn't
illegal and will do the same thing as a return from main (with the same
value, of course) as long as main is declared correctly.
-- 
Brian L. Matthews	blm@6sceng.UUCP

goudreau@larrybud.rtp.dg.com (Bob Goudreau) (07/06/90)

In article <4254@jato.Jpl.Nasa.Gov>, kaleb@mars.jpl.nasa.gov (Kaleb
Keithley) writes:
> >
> >> It also strikes me that what may be missing from the standard is a
statement
> >> along the lines of "... use of exit() from main() is illegal..."  Not to 
> >> mention that K&R2 is a little misleading in this area also.
> >
> >Say what?  Who has ever claimed that use of exit() from main() is not
> >legal?
> 
> I don't think anyone ever said it was illegal.  

Then why does it strike you that such a statement is missing from the
standard?
 
> By implication I was trying to state that the use of exit() from 
> (ANSI Std. C defined):
>    int main(int argc; char **argv) { ... }
> was inconsistant.  Maybe I'm picking nits, but main() should use return
> instead of exit based on this.  If the ANSI Std. is going to define a
> the way that main() is declared, then they should define the corresponding
> exit (not the funcion) from function main().  And IMHO, return is the
> correct exit method from int main().
> 
> if main() is defined as returning a value of type int, then the use of
> exit is not consistant with the defined return value of the function.
> ....
> Furthermore, had you read my (and others) previous posting on this subject,
> you'd have seen my reference to K&R2 that stated that exit() from main()
> and return from main() were equivalent.

But, as you mention, the standard explicitly states that the two
methods are *equivalent* (see excerpt below), so in what way is the
standard inconsistent?  It says essentially, "you can do it this way
or you can do it that way, but both of those ways are equivalent".


	2.1.2.2.3  Program Termination

	A return from the initial call to the |main| function is
	equivalent to calling the |exit| function with the value
	returned by the |main| function as its argument.  If the
	|main| function executes a return that specifies no value,
	the termination status returned to the host environment is
	undefined.

------------------------------------------------------------------------
Bob Goudreau				+1 919 248 6231
Data General Corporation
62 Alexander Drive			goudreau@dg-rtp.dg.com
Research Triangle Park, NC  27709	...!mcnc!rti!xyzzy!goudreau
USA

steve@taumet.com (Stephen Clamage) (07/07/90)

In article <4249@jato.Jpl.Nasa.Gov> kaleb@mars.UUCP (Kaleb Keithley) writes:
>It also strikes me that what may be missing from the standard is a statement
>along the lines of "... use of exit() from main() is illegal..."

Such a statement is not only not missing, but the standard says the
opposite (section 2.1.2.2):

"A return from the initial call to the _main_ function is equivalent
to calling the _exit_ function with the value returned by the _main_
function as its argument."

Once again, the point of all of this is that the standard represents a
contract between the C compiler implementor and the C programmer.  The
implementor promises that the system will behave according to the standard
if the programmer writes code according to the standard.  The programmer
is free to write code some other way, but he cannot then expect his code
to work the same way when he tries it with some other compiler.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

ghoti+@andrew.cmu.edu (Adam Stoller) (07/07/90)

Excerpts from netnews.comp.lang.c: 6-Jul-90 Re: main() arguments, was
R.. Stephen Clamage@taumet.c (965)

> "A return from the initial call to the _main_ function is equivalent to
> calling the _exit_ function with the value returned by the _main_
function as its argument."


If one uses exit()  [with argument EXIT_SUCCESS (?)] - I believe ANSI
specifies that any functions that were registered via the atexit()
function should be called prior to the final termination of the program.

If one uses return from main() do the functions registered with atexit()
get called ? (i.e. just how equivelent are they?)

--fish
[return(exit(0));]

6sigma2@polari.UUCP (Brian Matthews) (07/07/90)

In article <4aZDy6i00Vtq8qIVAw@andrew.cmu.edu> ghoti+@andrew.cmu.edu (Adam Stoller) writes:
|If one uses exit()  [with argument EXIT_SUCCESS (?)] - I believe ANSI
|specifies that any functions that were registered via the atexit()
|function should be called prior to the final termination of the program.
|If one uses return from main() do the functions registered with atexit()
|get called ? (i.e. just how equivelent are they?)

You're looking at it backwards.  The standard doesn't say "we looked at
all of the things exit and return from main do, and they look the same,
so we'll say they're equivalent."  It says "exit and return from main
are equivalent" (paraphrasing slightly :-)).  Thus if exit causes the
atexit functions to run, a return from main must also, even if the
standard doesn't explicitly say this anywhere.
-- 
Brian L. Matthews	blm@6sceng.UUCP

kitchin@hpavla.AVO.HP.COM (Bruce Kitchin) (07/10/90)

>> Well, I tried it on as many compilers as I could get my hands on, and they
>> all worked.

Yes, but proof of what?  The only thing that you proved is that for those 
compilers that you were able to get your hands on, the writers of those compilers chose to make it work the way you did it.  However, what I believe others are
saying is that if you declare main as returning int and taking either zero or
two arguments of the appropriate type as defined by ANSI-C, then your program
will work on ANY ANSI conforming compiler and it will work if you use return
to terminate the main function or you use exit().  I believe that it will
also work for any K&R conforming compiler and between the two of them you 
should have all bases covered.  If you find one that doesn't work when you
follow ANSI's rules, you would have legitimate reason to complain to the
compiler writer.  However, if you declare main in any other way, ANSI clearly
states that any current or future compiler writer for any reason that seems
good to them can complain that your program is written wrong or even to emit
code that will lead to catastrophic program failure when executed.

I have seen in my own experience and even more in watching others doing the same thing, that using EMPIRICAL evidence as a basis for not obeying the docs 
is the first step towards an unexpected disaster just waiting to get you when
you are least expecting it.  I have used empirical evidence to show that
a product doesn't meet the documentation when the doc's say that something will
work and it clearly doesn't work.  But I've learned not to take advantage of
undocumented things that 'work'.  I've seen commercial products go belly-up
when a new revision of the supporting hardware or software did not conform to
some undocumented, but depended on, behavior.