bicup
bicup

Reputation: 305

Call exit() without argument

In "Advanced Programming in the UNIX Environment", on page 199 (Sec 7.3), I read:

If (a) any of these functions [exit, _exit, _Exit] is called without an exit status

I wrote a simple program to verify this:

#include <stdio.h>
#include <unistd.h>

int main() {
    exit();
}

But gcc source.c complains:

source.c:5:5: error: too few arguments to function ‘exit’

How can it be possible? Do they mean, "called directly from assembly"?

EDIT: the paragraph says:

All three exit functions expect a single integer argument, which we call the exit status. Most UNIX System shells provide a way to examine the exit status of a process. If (a) any of these functions is called without an exit status, (b) main does a return without a return value, or (c) the main function is not declared to return an integer, the exit status of the process is undefined.

Upvotes: 2

Views: 1918

Answers (4)

the busybee
the busybee

Reputation: 12600

Even if you use assembly it is not possible to call a function with an argument if it expects one.

  1. If the argument is expected in a CPU register: There is always a value in each register. It might be unknown, but in most cases it can be determined by the control flow before the call.

  2. If the argument is expected on the stack: There are always values in the memory the stack pointer points at. Same as above.

    There is only one case I can think of that is different. If the memory region for the stack is protected by a memory management unit and exit() can't access the argument because it is beyond the region's limit.

So you always have an exit status; but you might not know its value.

EDIT

If some system has an architecture as @EricPostpischil describes exit() will detect the missing exit status. Not knowing such a system I can only guess what happens then. It might be that the called exit() will return '0' (zero) to the calling process, it might as well choose another defined value, or it might take some random value.

In most cases, the result is the same: The exit status is not defined. But there is one because the calling process receives one.

Upvotes: 1

The edit contains 3 claims:

All three exit functions expect a single integer argument, which we call the exit status. Most UNIX System shells provide a way to examine the exit status of a process. If

a) any of these functions is called without an exit status,

b) main does a return without a return value,

c) the main function is not declared to return an integer

the exit status of the process is undefined.

None of these 3 cases is not as straightforward. Let's tackle them in order.

First a). You can call exit() without arguments in C89, if you didn't include <stdlib.h>. Then exit will be implicitly assumed to be of type int exit(), with undeclared arguments. This will lead to undefined behaviour and anything can happen. In C99+ there is no implicit function declaration so you must explicitly declare exit with wrong type to be able to call it without an argument. If <stdlib.h> has been included, it is a constraint violation to call exit() without an argument, and the compiler must diagnose this. The program is an invalid program. Compiler can however successfully compile an invalid program too. In that case the behaviour is undefined.

b) if main does use return without a value, it is a constraint violation, for a function that returns int must return a value of type int. In that case the compiler must diagnose it as such and may refuse to compile the program. It can however successfully compile this invalid program, as in case a). However if the implementation uses C99+ and the return statement is omitted and the execution proceeds to the last } of main it is as if main ended with return 0;, i.e. the behaviour is well-defined.

c) if the main function is not declared to return an int (not just any integer), then main must use some other implementation-defined prototype, and in that case the implementation should probably define the behaviour. If the prototype is not of any implementation-defined type, the behaviour is undefined.

Now, the question is what is the limit of the undefined behaviour in these cases? It is not necessarily bounded - in fact anything is allowed, including the program crashing as if by signal, or the compiler refusing to compile the program, or the program to hang upon call to exit and not return at all. Of course it is a quality-of-implementation issue, and most compilers will probably to refuse to compile the program and if a program is successfully compiled, then the resulting executable would indeed return an undefined exit status.

Upvotes: 1

Steve Summit
Steve Summit

Reputation: 47952

So I think what your book is trying to say is, "If your program exits without a defined exit status, the exit status is undefined". (Which when I say it like that sounds so obvious it's kind of ridiculous, but there we are.)

So then the question is, "How can a program exit without a defined exit status?"

One way is to fall off the end of main() without returning a value. (Once upon a time that gave you, in effect, a random exit status, although these days C is defined with a special exception, namely that if you forget to return a value from main, the compiler quietly inserts return 0; for you.)

And then another way to exit without a defined exit status would be to call exit without supplying a value -- that is, to call exit(). Here, again, things have changed since (it seems) that book was written. Once upon a time, C didn't have function prototypes, and compilers therefore didn't complain if you called a function without a prototype for it in scope.

But even today, if you write

/* deliberately no proper #include line here */

int main()
{
    exit();
}

you might be able to get an at least somewhat surprising, not-necessarily-0 exit status. If that's not random enough for you, you could try to increase the odds by changing it to

int main()
{
    puts("Hello, world!");
    exit();
}

Upvotes: 1

Petr Skocik
Petr Skocik

Reputation: 60068

If you forget about prototypes (ANSI C invention) and simply declare exit as taking an unspecified number of parameters in the K&R style

void exit();/*unspecified number of parameters*/

then you can do

void exit();/*unspecified number of parameters*/
int main()
{
    exit(1);
}

and this is a well-formed program.

With this type of declaration, the compiler won't stop you from completely omitting the argument:

void exit();
int main()
{
    exit();
}

but this is technically undefined C.

In practice, exit is in a separate translation unit (the standard library) so the compiler has no way of messing it up, and whether the above will crash or return some garbage value depends on what your machine does if code attempts to read a register or a memory location with unspecified contents. (The unspecified contents exit will attempt to grab could be a "trap representation" and then you get a crash, otherwise it'll simply grab and use some garbage integer value.)

Upvotes: 1

Related Questions