How can exit(n) give a function the return value?

  • Thread starter lovecreatesbeauty
  • Start date
L

lovecreatesbeauty

Hello experts,

Is the following code snippet legal? If it is, how can exit() do the
keyword return a favor and give a return value to the main function?

Can a function call (or only this exit(n)) statement provide both
function call and return features of the C programming language?

/* headers omitted */
int main (void)
{
exit(0); /* instead of `return 0;' */
}

Sincerely,

lovecreatesbeauty
 
I

Ian Collins

lovecreatesbeauty said:
Hello experts,

Is the following code snippet legal? If it is, how can exit() do the
keyword return a favor and give a return value to the main function?

Can a function call (or only this exit(n)) statement provide both
function call and return features of the C programming language?

/* headers omitted */
int main (void)
{
exit(0); /* instead of `return 0;' */
}
I guess _exit(0) is closer to return(0), exit does some clean-up
processing before terminating.

Both return the status to the caller (if there is one). Exactly how
this magic is performed would be platform specific.

The exit functions differ form return in that they will terminate the
program no matter where they are called.
 
E

Eric Sosman

lovecreatesbeauty said:
Hello experts,

Is the following code snippet legal? If it is, how can exit() do the
keyword return a favor and give a return value to the main function?

Can a function call (or only this exit(n)) statement provide both
function call and return features of the C programming language?

/* headers omitted */
int main (void)
{
exit(0); /* instead of `return 0;' */
}

First: Yes, the code is legal. But exit() has nothing
to do with the `return' keyword, and it does not supply a
return value for the main() function. When you call exit(),
main() does not return at all: exit() terminates the program
without ever returning.

Consider a slightly more complicated program:

#include <stdio.h>
#include <stdlib.h>
static void func(void);
int main(void) {
puts ("Before func()");
func();
puts ("After func()");
return EXIT_SUCCESS;
}
static void func(void) {
exit(EXIT_FAILURE);
}

Clearly, exit() does not return a value from func(). If it
caused func() to return, main() would resume right after the
call to func() and would proceed to the subsequent puts() --
which we know does not occur. Furthermore, func() is a `void'
function and cannot return any value at all! So a little
thought reveals that exit() and `return' are unrelated, even
though both (in proper context) can cause program termination.
 
M

Micah Cowan

lovecreatesbeauty said:
Hello experts,

Is the following code snippet legal? If it is, how can exit() do the
keyword return a favor and give a return value to the main function?

Can a function call (or only this exit(n)) statement provide both
function call and return features of the C programming language?

/* headers omitted */
int main (void)
{
exit(0); /* instead of `return 0;' */
}

Only exit() can do this. It takes advantage of specific knowledge of
how to exit and provide a result code to the operating system; so
nobody would be able to simply write their own (portably).

exit() is provided mostly so that you can exit your program
/suddenly/, perhaps from somewhere deeply nested within your
program. It is probably most frequently encountered as a response to
an error condition that is encountered by the program.

As a matter of style, I would never call exit() from main, though: it
would provide no advantages over the return keyword.
 
L

lovecreatesbeauty

said:
static void func(void) {
exit(EXIT_FAILURE);
}

said:
Furthermore, func() is a `void'
function and cannot return any value at all!

Thank you.

But in my sample code int main(); requires a return value, and a
`return n' statement following `exit( 0)' can not be reached of course.

Does ANSI C allow a default return value for functions without return
statement? I am anxious on it.
 
P

Peter Nilsson

lovecreatesbeauty said:
... in my sample code int main(); requires a return value,

main() never _requires_ a return value. [Actually, there is one
exception
in that if main is being called recursively in C90, and the calling
function
uses the return value, then the behaviour is undefined.]
and a `return n' statement following `exit( 0)' can not be reached of course.

You don't need one. If you're compiler refuses to compile the code
because your main finishes on exit and not return, then you have
failed to invoke it in conforming mode.

Your compiler may issue warnings, but it can issue warnings for
_any_ reason. It is up to the programmer to decipher wether a
warning warants action or not.
Does ANSI C allow a default return value for functions without return
statement?

C90 and C99 both allow non-void functions to return to the caller
without
explicitly returning a value (i.e. without a return statement,) so long
as
the calling function doesn't attempt to use the value...

int foo() { } /* no return value */
void bah() { foo(); } /* fine! */

[C++ is different in that regard.]

The main function itself has always been further special cased. In C99,
falling off main (reaching the end without a return) will result in a
value
of 0 being returned.

In C90, if the first call to main returns without returning a value,
then
the 'status' returned to the host is undefined, but the program is
otherwise well behaved.
I am anxious on it.

Then get yourself the last public draft of the C standard and read it
for yourself. ;-)
 
J

Jordan Abel

I guess _exit(0) is closer to return(0), exit does some clean-up
processing before terminating.

So does return from main. The difference is in whether automatic
variables declared in main are avaible to atexit handlers.
 
B

Ben Pfaff

lovecreatesbeauty said:
Is the following code snippet legal? If it is, how can exit() do the
keyword return a favor and give a return value to the main function?

You seem to be under the impression exit() somehow causes a
return from main(). That is one possible implementation. But a
more common implementation is that a return from main() causes
exit() to be returned. That is, some implementations effectively
start from a function called, say, _start, which might be written
this way:

int
_start (int argc, char *argv[])
{
exit (main (argc, argv));
}
 
C

Chris Torek

... a more common implementation is that a return from main() causes
exit() to be returned.

Presumably you mean "causes exit() to be called" here:
That is, some implementations effectively start from a function
called, say, _start, which might be written this way:

int
_start (int argc, char *argv[])
{
exit (main (argc, argv));
}

And in fact, most real-world implementations have code much like
this, although _start or __startup or whatever it is named tends
to be a bit more complicated. (In many cases it has to compute
argc and/or argv somehow, load libraries, hook up the standard I/O
streams and otherwise initialize the C runtime support, and so on.)

The fact that someone else wrote the startup code, which calls
exit() with whatever your main() returns, is why you should return
a value from your main(). C99 allows you to omit the return value
(forcing the compiler to supply one for you in this case), but C90
does not, and in any case, why should you give up your power to
return a useful value?
 
R

Richard Tobin

lovecreatesbeauty said:
Is the following code snippet legal? If it is, how can exit() do the
keyword return a favor and give a return value to the main function?

It doesn't. If you call exit(), main() doesn't return, so it doesn't
matter that it hasn't got a return statement.

-- Richard
 
P

pemo

Jordan said:
So does return from main. The difference is in whether automatic
variables declared in main are avaible to atexit handlers.

I read that initially as "automatic variables are available to atexit
handlers"

Wrong - right?
 
R

Richard Bos

pemo said:
I read that initially as "automatic variables are available to atexit
handlers"

Wrong - right?

And there's the rub.

If the program is ended through exit(), all auto objects in main() still
exist when the atexit handlers are called. One of the handlers could
refer to such an object - for example, if a global pointer had been set
to point at one of them.

If the program is ended through a return from main(), all auto objects
in main() will already have disappeared when the atexit handlers are
called. If a handler dereferences a pointer to such an ex-object, your
data could be pushing up the daisies.

Whether anyone sane will ever write an atexit handler that depends on
this difference - well, that's another question.

Richard
 
E

Eric Sosman

Richard said:
If the program is ended through a return from main(), all auto objects
in main() will already have disappeared when the atexit handlers are
called. If a handler dereferences a pointer to such an ex-object, your
data could be pushing up the daisies.

Whether anyone sane will ever write an atexit handler that depends on
this difference - well, that's another question.

The problem can occur even without an explicit atexit()
handler:

int main(void) {
char buffer[16384];
setvbuf (stdout, buffer, _IOFBF, sizeof buffer);
...
return 0; /* woops! */
}
 
R

Robin Haigh

Richard Bos said:
And there's the rub.

If the program is ended through exit(), all auto objects in main() still
exist when the atexit handlers are called. One of the handlers could
refer to such an object - for example, if a global pointer had been set
to point at one of them.

If the program is ended through a return from main(), all auto objects
in main() will already have disappeared when the atexit handlers are
called. If a handler dereferences a pointer to such an ex-object, your
data could be pushing up the daisies.

Whether anyone sane will ever write an atexit handler that depends on
this difference - well, that's another question.


I can see people doing this:

#include <stdlib.h>

char **pa;

void cleanup(void) {
int i;
for ( i=0 ; i<10 ; i++ )
free(pa);
}

int main(void){
int i;
char *a[10];
for ( i=0 ; i<10 ; i++ ) {
a = malloc(whatever);
}
pa = a;
atexit(cleanup);
/* etc */
}

You aren't going to see what's wrong with this unless you've heard about the
issue. I certainly wouldn't call it insane. It's a nasty little trap.

One way to steer cleer of it is always to terminate using exit and never
return from main (though of course that leaves a hostage to fortune for
future maintenance)
 
L

lovecreatesbeauty

Peter said:
lovecreatesbeauty said:
... in my sample code int main(); requires a return value,

main() never _requires_ a return value. [Actually, there is one
exception in that if main is being called recursively in C90, and the calling
function uses the return value, then the behaviour is undefined.]

Perhaps I did not make it clear in my reply, sorry to bring this
misunderstanding. I do not know whether the entry point function main
needs a return value or not up to now. But I meant the prototype:

int main();

indicated that the function call expression had a value. There was no
return statement inside the function body. In my original post, there
is only one exit(0) call inside the function body. So how can the
semantics of the prototype be fulfilled?

I can understand this one: void f1(void) { exit(0); }
I can not understand this one: int f2(void) { exit(0); }

I read Richard Tobin's reply and I think I understand it. In my
original sample code, the int main(); function does not return, and it
does not mind whether it get a return value. As well as the discard of
the return value of a function like int main();, no return value is not
illegal. Do I understand it correct?

Thank all of you.
 
R

Robin Haigh

lovecreatesbeauty said:
Peter said:
lovecreatesbeauty said:
... in my sample code int main(); requires a return value,

main() never _requires_ a return value. [Actually, there is one
exception in that if main is being called recursively in C90, and the calling
function uses the return value, then the behaviour is undefined.]

Perhaps I did not make it clear in my reply, sorry to bring this
misunderstanding. I do not know whether the entry point function main
needs a return value or not up to now. But I meant the prototype:

int main();

indicated that the function call expression had a value. There was no
return statement inside the function body. In my original post, there
is only one exit(0) call inside the function body. So how can the
semantics of the prototype be fulfilled?

I can understand this one: void f1(void) { exit(0); }
I can not understand this one: int f2(void) { exit(0); }

I read Richard Tobin's reply and I think I understand it. In my
original sample code, the int main(); function does not return, and it
does not mind whether it get a return value. As well as the discard of
the return value of a function like int main();, no return value is not
illegal. Do I understand it correct?

Yes. In general, the flow of control in a program depends on the input
data, and can't be determined at compile time.(*) So there isn't much point
in requiring that an int function must contain a return statement, because
you can't tell whether any of the return statements will ever be reached.

At run time, only the statements executed matter.

(*) In fact some algorithms have theoretically unknown behaviour even
without any input data
 
J

Jordan Abel

Peter said:
lovecreatesbeauty said:
... in my sample code int main(); requires a return value,

main() never _requires_ a return value. [Actually, there is one
exception in that if main is being called recursively in C90, and the calling
function uses the return value, then the behaviour is undefined.]

Perhaps I did not make it clear in my reply, sorry to bring this
misunderstanding. I do not know whether the entry point function main
needs a return value or not up to now. But I meant the prototype:

int main();

indicated that the function call expression had a value. There was no
return statement inside the function body. In my original post, there
is only one exit(0) call inside the function body. So how can the
semantics of the prototype be fulfilled?

Some of the people in this group have radical ideas about the semantics
of main() in particular. I think Peter Nilsson's claim (which I disagree
with) is that even without an exit statement, main doesn't need to
return a value, since main's caller is outside the scope of the C
standard.
I can understand this one: void f1(void) { exit(0); }
I can not understand this one: int f2(void) { exit(0); }

Well - funny you ask. Due to yet another K&R holdover, int functions
don't have to return a value - "return;" or reaching the final right
brace is valid. However, the "returned value" cannot be used by any
caller.
 
B

Ben Pfaff

Jordan Abel said:
Due to yet another K&R holdover, int functions don't have to
return a value - "return;" or reaching the final right brace is
valid.

"return;" in a function returning int is no longer allowed in
C99:

6.8.6.4 The return statement
Constraints

1 A return statement with an expression shall not appear in a
function whose return type is void. A return statement
without an expression shall only appear in a function whose
return type is void.
 
K

Keith Thompson

Robin Haigh said:
Yes. In general, the flow of control in a program depends on the input
data, and can't be determined at compile time.(*) So there isn't much point
in requiring that an int function must contain a return statement, because
you can't tell whether any of the return statements will ever be reached.
[...]

Theoretically, the language could require that there be a return
statement along all possible execution paths. For example:

int foo(void)
{
if (something) {
/* A */
}
else {
/* B */
}
/* C */
}

If there's a return statement at C, *or* if there are return
statements at both A and B, then one of them will always be executed;
otherwise the compiler can't prove that foo() can never reach the
closing brace without executing a return. (There are cases where a
function will never reach the closing brace but the compiler can't
prove it.)

Some compilers do this analysis and issue a warning such as "control
reaches end of non-void function". With some effort, the standard
could require all compilers to do this analysis, and make it a
constraint violation if the check fails. (I'm not necessariliy
advocating such change in the standard, just saying that it would be
possible.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,008
Latest member
Rahul737

Latest Threads

Top