main() arguments, was Re: typedef-ing an array
Chris Torek
chris at mimsy.umd.edu
Wed Jul 4 04:32:34 AEST 1990
In article <25247 at 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 at sun.udel.edu> toor at 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 at jato.Jpl.Nasa.Gov> kaleb at 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), at 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 at cs.umd.edu Path: uunet!mimsy!chris
More information about the Comp.lang.c
mailing list