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.