Names specified here
Name |
Description |
Notes
|
Source |
Availability |
_Noreturn |
Indicator of function that does not return |
L |
|
|
Q |
Keyword |
|
|
|
|
C11
|
inline |
Specifier for in-line functions |
L |
|
|
Q |
Keyword |
|
|
|
C99
|
C11
|
noreturn |
Indicator of function that does not return |
L |
|
M |
Q |
|
|
|
|
|
C11
|
return |
Exit from a function |
L |
|
|
|
Keyword |
C89
|
C90
|
C95
|
C99
|
C11
|
void |
Empty return type |
L |
|
|
T |
Native |
C89
|
C90
|
C95
|
C99
|
C11
|
void |
Empty parameter list |
L |
|
|
T |
Native |
C89
|
C90
|
C95
|
C99
|
C11
|
The behaviour of a C program is defined by the
functions from which it is built. A function is a
named sequence of statements that can be executed
(invoked or called) on demand. The
body of a function is a block statement which is
executed when the function is invoked. The function can also
declare parameters, automatic variables outside of the block
that are initialized on each invocation by
arguments, values computed just before invoking
the function. For example, here is a simple function that
returns the sum of its arguments using a return
statement:
int sum(int x, int y)
{
return x + y;
}
x
and y
are
the function's parameters, and their values are the arguments
of each invocation. The braces {
and
}
delimit the function's body. The
name of the function is sum
. The
return
statement causes the expression x + y
to be
evaluated, and then terminates the invocation of the
function, returning the value of the expression to the caller
as the result of the call. The int
before the
name is the return type of the function, which is
the type of the
result.
An invocation of the function specifies its name and the
arguments in brackets, for example:
int s;
s = sum(10, 20);
sum(10, 20)
is a function-call
expression. Its value is the result of invoking the function
with the supplied arguments, which are 10
and 20
in this example.
The call expression behaves as if it is replaced with the
function's body, with the parameters as local variables
initialized by the supplied arguments:
int s;
{
int x = 10;
int y = 20;
s = x + y;
}
The result does not have to be stored in a variable. It
could be passed directly as an argument to another function
call, such as printf
:
#include
printf("%d\m", sum(10, 20));
That would be roughly equivalent to the following:
#include
{
int x = 10;
int y = 20;
printf("%d\m", x + y);
}
The compiler will check that the types of the arguments
match the types of the parameters. If not, arguments are
converted to the correct type if possible, or it is an
error.
A function can have multiple return
s. The
first return
that is executed determines the result, and terminates the
call. For example:
int absdiff(int x, int y)
{
if (x < y)
return y - x;
return x - y;
}
If the first return
is
executed (because x
is less than
y
), all remaining statements are
ignored.
Early returns like this can sometimes cause problems for
program maintenance. For example, if you want to trace the
values of variables on entry to and exit from a function, you
now have multiple exit points. In these cases, it is
sometimes clearer to be more long-winded:
int absdiff(int x, int y)
{
int d;
if (x < y)
d = y - x;
else
d = x - y;
return d;
}
This form still has two exit points, but is less
suggestive that one is somehow more preferred than the
other:
int absdiff(int x, int y)
{
if (x < y)
return y - x;
else
return x - y;
}
int absdiff(int x, int y)
{
return x < y ? y - x : x - y;
}
Functions without
parameters
A function does not have to take any arguments. For
historical reasons, this is not indicated by an empty
parameter list, but by using
void
:
int noargexample() { . . . } //
int noargexample(void) { . . . } //
The first form is bad because it declares a function
accepting unspecified arguments, so the
argument-parameter check is disabled.
Functions without return
values
A function does not have to return a value. The type
void
exists expressly for
declaring functions that do not return a value:
#include
void print_warning(int sec)
{
printf("You have %d seconds remaining\n", sec);
}
When execution of the last statement in the function
completes, the invocation also terminates. You can also
use return
to exit from the function at an earlier point:
#include
void print_warning(int sec)
{
if (sec < 1) {
printf("Too late!\n");
return;
}
printf("You have %d seconds remaining\n", sec);
}
Non-returning
functions
It is occasionally useful to have a function that
never returns, such as exit
. From C11, you can explicitly
declare that a function does not return, using the
keyword
_Noreturn
:
_Noreturn void exit(int code)
{
. . .
}
It is slightly more aesthetic to use the macro
noreturn
:
#include
noreturn void exit(int code)
{
. . .
}
noreturn
allows a compiler to determine
that subsequent code is at least sometimes
unreachable, especially under otherwise problematic
circumstances. For example:
int x;
if (test) {
exit(0);
} else {
x = 10;
}
printf("x = %d\n", x);
If the test is true, and exit
is declared without
noreturn
, the compiler may reason that
x
has unspecified value when
it is printed, and warn the programmer. With
noreturn
, the compiler may determine
that the printf
won't be reached
unless x
has a specified
value.
If a function declared with
noreturn
returns, you get undefined
behaviour. A good compiler will probably warn if
it cannot determine that that cannot happen.
Don't use
noreturn
with a function that might not
return. The POSIX function execve
is an example that must not be
declared with
noreturn
. If all goes well, it does not
return. However, if it fails, it does return, and the
caller should handle it:
if (execve("subproc", args, env) == -1) {
perror("exec");
exit(EXIT_FAILURE);
}
Function definitions,
declarations and prototypes
When a function body is present, as in all the
examples above, it is a function definition.
It is the set of function definitions present in
translation
units constituting a program that define its
behaviour. A function definition must appear exactly once
in a program to be able to invoke it.
When the body is absent, and replaced by a semicolon
;
, it is a function
prototype. For example, here are prototypes for
some of the functions we've seen so far:
int sum(int x, int y);
void print_warning(int sec);
_Noreturn void exit(int code);
Both prototype and definition serve as a function
declaration.
Prototypes for the same function may appear multiple
times, but must be consistent with each other and with
the definition.
Parameter names in prototypes need not be the same as
those given in the definition, and can be omitted. The
following declarations are all consistent with earlier
examples:
int sum(int, int);
void print_warning(int shoo);
_Noreturn void exit(int);
A prototype allows a function call's arguments' types
to be checked against the function's parameter types. To
invoke a function, a declaration for the function must be
in scope. Before
C99, if a function that isn't
in scope is called, it is assumed to return int
, and
take unspecified arguments, preventing the checking their
types against parameter types. From C99, it is an error to attempt
to invoke a function not in scope.
There are three main places to put prototypes:
//
#include "mydecls.h"
//
int sum(int, int);
void myfunc(void)
{
//
int sum(int, int);
. . .
}
A function to be called from translation units
other than the one in which it is defined should be
declared in a header. The header can then be
included in any translation unit that needs to call it.
The translation unit that defines it should also include
the declaring header, to ensure that the prototype
remains consistent with the definition.
In-line functions
Function calls can be expensive, and the overhead of a
call can outweigh the time spent actually executing its
statements. You can hint that the call should be
optimized by the compiler by not actually generating a
call in the translated language, but by writing the body
of the function in place of the call. The declaration of
the function must now include the body and the keyword
inline
:
//
inline int sum(int x, int y)
{
return x + y;
}
To be used in mutliple translation units, the declaration
including the body must appear in a header, and a definition
must be provided in one of the translation units. The
definition must be what appears to be a prototype,
explicitly using the normally redundant keyword
extern
:
//
extern int sum(int x, int y);
Function-definition
syntax