[comp.lang.c] void main

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