karl@haddock.ima.isc.com (Karl Heuer) (06/22/89)
In article <22420@iuvax.cs.indiana.edu> bobmon@iuvax.cs.indiana.edu (RAMontante) writes: >Doug Gwyn's comment about main()'s return value can also be finessed by >declaring it as void main(void) > >[I don't know if this is legitimate, but it silences the warning messages.] Firstly, if you really are falling off the bottom of |main()|, then this is worse than useless; it's silencing a warning about a real bug. Secondly, even if you're not falling off the bottom (e.g., if you terminate with an explicit |exit()|), it is (alas) not sanctioned by the pANS. The only valid types for |main()| are |int main(void)| or |int main(int, char **)|.$ #pragma OPINION ON Given that |main()| is already a special case in that it has two valid types, I think that it should also have been allowed to return either |int| or |void| (where |void| would be used in the same sense as with |exit()| and |abort()|, i.e. "function does not return" rather than "function returns no value"). That way, those of use who prefer% |exit()| over |return| could use the |void| declaration, avoiding the potential warning message "|int| function returns no value", which should be (but isn't) a standard lint warning, now that the existence of |void| has made pseudo-void& functions obsolete. #pragma OPINION OFF Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint ________ $ Although |argv| can be *written* with array brackets, the resulting type of |main| will still be |int (int, char **)|. % Discussion about whether |exit| or |return| is "better" is no more welcome than discussion about indentation styles. & pseudo-void functions are those that return no value, yet are not declared |void|, often due to having been written before |void| was common usage. See K&R1 for examples.
bdm659@csc.anu.oz (11/17/89)
In article <7733@cdis-1.uucp>, tanner@cdis-1.uucp (Dr. T. Andrews) writes: > [[The question is: why is it wrong to declare main() as void, instead of > int, even though he program is terminated by calling exit() explicitely?]] > > I should like to hear details of a case where the code will not > work. It seems ridiculous to declare un-portable the results of a > void-vs-int return linkage which is never made, and I should like > to hear of an implementation where this actually fails. > > If no real-world example is available, I should be almost as > satisfied with a conjectural example (clearly marked as conjecture). > Such should prove enlightening not only to earlier posters, but > to me as well. A day spent without learning SOMETHING is a poor > day indeed. > > 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. > Declaring such programs non-portable does not seem to me to be a > productive use of X3J11's time; obviously, I can use a little bit of > education from someone who knows better than myself. Here's a *hypothetical* implementation which goes wrong. Suppose the stack is used for returning function values, like this: * called function: on entry, allocate enough stack space for the function value on return, copy the function value into the stack space previously allocated * calling function: when the called function returns, pop the function value off the stack Clearly, the called and calling functions must agree about the type of the called function, or else the stack pointer gets mispositioned. This would not normally be an issue here if main() calls exit() instead of returning normally. However, in this hypothetical implementation, one of the things that exit() does is to deliberately unwind the stack to the point before when main() was called. [Why? Well, maybe it wants to make as much stack as possible available for whatever it's going to do next, like i/o rundown or calling an exit handler.] This unwinding process might lead to a mispositioned stack-pointer just as an ordinary return might. I don't know of any real implementation that does things exactly like this, but I don't think that this hypothetical implementation is so outrageous that it should be ruled out of order. Your code is non-portable. In general, if two parts of your program (and supporting code) disagree on the type of a function, you are just begging for trouble. If all you wish to do is to avoid a warning message from a finicky compiler, why don't you just PRETEND that main() is returning something? Brendan McKay. bdm@anucsd.oz or bdm@anucsd.oz.au or bdm659@csc1.anu.oz.au
bdm659@csc.anu.oz (11/18/89)
In article <7733@cdis-1.uucp>, tanner@cdis-1.uucp (Dr. T. Andrews) writes: > [[The question is: why is it wrong to declare main() as void, instead of > int, even though he program is terminated by calling exit() explicitely?]] > > I should like to hear details of a case where the code will not > work. It seems ridiculous to declare un-portable the results of a > void-vs-int return linkage which is never made, and I should like > to hear of an implementation where this actually fails. > > If no real-world example is available, I should be almost as > satisfied with a conjectural example (clearly marked as conjecture). I've thought of an even simpler hypothetical implementation for which declaring main() as void fails to work. This one may even exist (anyone?). Suppose the implementation of calling a function goes like this: * push the arguments onto the stack * move the stack pointer some more to make some room for the function to return its value in * branch to the function The called function gets its arguments by indexing off the stack pointer. Now, if the calling function has the wrong idea about what the type of the function being called is, the called function will not find its arguments correctly. Thus, if you declare main() as void, you can't expect to be able to use argv and argc. It makes no difference whether you are leaving main() by doing return() or exit(). Brendan McKay. bdm@anucsd.oz.au or bdm659@csc1.anu.oz.au
CMH117@PSUVM.BITNET (Charles Hannum) (11/18/89)
The basic problem is that you ass/u/me that the compiler will always return values in a register. If this is the case, then fine. But it is emphatically **NOT** the case. Making such gross assumptions about the way any particular compiler works is just begging for trouble. C was never defined to return values on the stack. The way this is done may vary from compiler to compiler, and to ass/u/me it does it one particular way makes your code non-portable. If all you want to do is disable the warning, just add a return(0) at the end of your main() function. Even if it isn't optimized out, it will only take a couple of bytes. This is a much better alternative than declaring main() as a void. -- - Charles Martin Hannum II "Klein bottle for sale ... inquire within." (and PROUD OF IT!!!) "To life immortal!" c9h@psuecl.psu.edu "No noozzzz izzz netzzzsnoozzzzz..." cmh117@psuvm.psu.edu "Mem'ry, all alone in the moonlight ..."
will@charyb.COM (Will Crowder) (11/18/89)
In article <1018.25642ba8@csc.anu.oz> bdm659@csc.anu.oz writes:
[lots of stuff about void main() unportability having to do with passing
return values on the stack]
I've used a 6 or so C compilers in 3 very different environments,
(680x0, 80x86, SPARC), and all of them pass return values in registers.
The question I have is this: for every C compiler I've used, it doesn't
matter how many arguments you call a function with. The return linkage
will still work, because it's the *caller's* job to fix up the stack
after the function returns. Are there any implementations in which this
is not true? Can someone point me to a C compiler which does use a vastly
different calling convention? I'm just curious. Passing return values
on the stack doesn't seem like a very good performance move, and most
machines have at least one register to pass something in...If the caller
needs the contents of that register saved, it can save it itself before
calling the function...again, stack frame integrity is the caller's
responsibility in C (or at least any sane implementation), no?
Will
bdm659@csc.anu.oz (11/19/89)
In article <314@charyb.COM>, will@charyb.COM (Will Crowder) writes: > In article <1018.25642ba8@csc.anu.oz> bdm659@csc.anu.oz writes: > > [lots of stuff about void main() unportability having to do with passing > return values on the stack] > > I've used a 6 or so C compilers in 3 very different environments, > (680x0, 80x86, SPARC), and all of them pass return values in registers. > > The question I have is this: for every C compiler I've used, it doesn't > matter how many arguments you call a function with. The return linkage > will still work, because it's the *caller's* job to fix up the stack > after the function returns. Are there any implementations in which this > is not true? Can someone point me to a C compiler which does use a vastly > different calling convention? I'm just curious. Passing return values > on the stack doesn't seem like a very good performance move, and most > machines have at least one register to pass something in...If the caller > needs the contents of that register saved, it can save it itself before > calling the function...again, stack frame integrity is the caller's > responsibility in C (or at least any sane implementation), no? Since functions can return values which are quite large (eg. structures), some mechanism other than registers must be used sometimes. The compiler can use whatever mechanism it pleases so long as it is consistent. The method might even depend on the type instead of the size, so you can't even suppose that a function can be safely declared int in one place and struct {int i;} in another. Brendan.
gwyn@smoke.BRL.MIL (Doug Gwyn) (11/19/89)
In article <314@charyb.COM> will@charyb.UUCP (Will Crowder) writes: >The question I have is this: for every C compiler I've used, it doesn't >matter how many arguments you call a function with. The return linkage >will still work, because it's the *caller's* job to fix up the stack >after the function returns. Are there any implementations in which this >is not true? It's not true for C in general, although many implementations are designed to work that way. Before the ,... mechanism of X3.159, the fact that it was highly desirable for printf() to be implementable as an ordinary C function imposed considerable constraints on the feasibility of various designs for function linkage implementation. With the advent of the ,... notion (distinct interface for variadic functions), more possibilities now are feasible. >Can someone point me to a C compiler which does use a vastly >different calling convention? Sure. Try ORCA/C (published by ByteWorks for the Apple IIGS). There are also many C implementations where the run-time library assists in restoring context, and often special hardware features can be exploited to help with this. In those cases, neither the caller nor the callee can be said to be responsible for these things. >I'm just curious. Passing return values on the stack doesn't seem like >a very good performance move, and most machines have at least one register >to pass something in...If the caller needs the contents of that register >saved, it can save it itself before calling the function...again, stack >frame integrity is the caller's responsibility in C (or at least any sane >implementation), no? First of all, please note that you have a particular notion of the kind of computer architecture involved. There are stack-based architectures and "register working set window" architectures where the notion of reserving a "general-purpose register" to hold the return value across a function linkage is inappropriate. Even on conventional general-register oriented architectures, if the returned object is large it won't fit in a register. "Stack frame integrity" is the responsibility of BOTH the implementor correctly implementing the C language specification AND the programmer not stepping beyond the bounds of what the C language specification promises. That is why we sometimes say that the C standard defines a "treaty point" between implementor and programmer.
mcdonald@uxe.cso.uiuc.edu (11/19/89)
>I've used a 6 or so C compilers in 3 very different environments, >(680x0, 80x86, SPARC), and all of them pass return values in registers. >The question I have is this: for every C compiler I've used, it doesn't >matter how many arguments you call a function with. The return linkage >will still work, because it's the *caller's* job to fix up the stack >after the function returns. Are there any implementations in which this >is not true? Yes. Microsoft C for the 8086 with the /Gc compiler switch. In this case the callee restores the stack with a "ret n" instruction. The return value for integral and pointer types still goes in registers. The /Gc switch produces minutely faster code. (I tried e-mail but your address is not known to our nameserver.) Doug MCDonald
ORCUTT@cc.utah.edu (11/21/89)
I always use return instead of exit when I am returning from main. I do this because most compilers know that code after a block closed by return is dead code, like when both branches if an if ... else block end in a return. I thus get warnings about such code. I reserve exit for returning for functions below main in the tree. For this reason, I always declare main as returning an int to avoid warnings from compilers that don't like void functions to return values.
meissner@dg-rtp.dg.com (Michael Meissner) (11/21/89)
In article <89321.113706CMH117@PSUVM.BITNET> CMH117@PSUVM.BITNET (Charles Hannum) writes: | The basic problem is that you ass/u/me that the compiler will always return | values in a register. If this is the case, then fine. But it is emphatically | **NOT** the case. Making such gross assumptions about the way any particular | compiler works is just begging for trouble. C was never defined to return | values on the stack. The way this is done may vary from compiler to compiler, | and to ass/u/me it does it one particular way makes your code non-portable. An internal DG C compiler that produced 16-bit Eclipse code DID NOT return function results in a register, in order to be compatible with the original AOS common language runtime environment. Functions which return values would have an extra word passed, which pointed to the return area. All of the arguments were then moved down one stack slot. A void function (ie, subroutine) would not push the return address. The 32 bit MV C compiler (which is a supported product) does believe the user when they call a void function, and assumes accumulator Ac0 is not modified by the function. If the function called really does return something (error indication, # bytes printed, etc.), it will mess up optimization. | If all you want to do is disable the warning, just add a return(0) at the end | of your main() function. Even if it isn't optimized out, it will only take | a couple of bytes. This is a much better alternative than declaring main() | as a void. Besides it's the correct thing to do.... | Michael Meissner, Data General. If compiles where much Uucp: ...!mcnc!rti!xyzzy!meissner faster, when would we Internet: meissner@dg-rtp.DG.COM have time for netnews?
karl@haddock.ima.isc.com (Karl Heuer) (11/24/89)
In article <374@helens.Stanford.EDU> mike@relgyro.STANFORD.EDU (Mike Macgirvin) writes: >OK, this thing has my head spinning. I use Microsoft 'C' with full >warnings. I do something easy like: > int main(void) { ... exit(0); } >And the darn thing complains that I'm not returning a value. >So in order to make the compiler shut up, I have to: > int main(void) { ... exit(0); /* NOTREACHED */ return (0); } >[Which is overkill, and *creates* warnings on other compilers.] I asked X3J11 to bless `void main()' (it shouldn't be any more difficult than supporting both `int main(void)' and `int main(int, char **)'), but the proposal was rejected, alas. You're right; the second solution is overkill. The correct solution is something like: #include <stdlib.h> /* for exit() */ int main(void) { ... exit(0); /* NOTREACHED */ } (or the same without the word "int" in front; this may make a difference). If the compiler believes that main() is falling off the end, then it is ignorant of the fact that exit() doesn't return, and if the implementation doesn't provide a path to convey such facts, it shouldn't be trying to issue warnings about it. If the compiler realizes that main() doesn't return, but it doesn't like the mismatch between the declared type "int" and the actual type "nonreturning", then it is ignorant of the fact that this is a quite legal thing to do, and even if it wants to warn about it in general, it ought to make a special case for main(). If the vendor tells you that you should use return instead of exit to avoid the problem, then the vendor is ignorant of the fact that many of us have reasons to prefer exit(), and may well be more inclined to switch vendors than to switch coding styles just to agree with the vendor's misguided notion of correctness. Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint (I *do* have reasons for using exit(). The reasons for using return are about equal, and there's not a whole lot of point debating them.)