A function is a named sequence of statements. Function definitions appear only at file scope, and functions do not nest. All arguments are passed by value (C17 6.5.2.2, 6.9.1).
Function definitions
function-definition:
declaration-specifiers declarator [declaration-list] compound-statement
int add(int a, int b)
{
return a + b;
}
- A function may return any complete object type except an array type, or
void. - A function returning a value should use
return. If control reaches the closing brace and the caller uses the result, the behaviour is undefined (C17 6.9.1). - The storage-class specifiers allowed are
externandstatic, which set linkage. - The function name’s scope covers the rest of the translation unit, so it can call itself.
- Before C23 each parameter in a definition needed a name. C23 allows unnamed parameters.
- A function specifier may be
inlineor_Noreturn. See Inline functions and Functions that do not return.
Prototypes
direct-declarator:
direct-declarator ( parameter-type-list )
direct-declarator ( [identifier-list] )
parameter-type-list:
parameter-list
parameter-list , ...
A declaration that lists parameter types is a prototype. It lets the compiler check calls and convert arguments. A prototype’s parameter names are optional and have only prototype scope.
int add(int a, int b); /* with names */
int add(int, int); /* without names */
void log_it(void); /* no parameters */
Empty parentheses
Until C23, int f(); declares a function with an unspecified parameter list.
Calling it with any arguments compiles, and the default argument promotions apply. Only
int f(void) is a prototype with no parameters. In C23 int f()
means the same as int f(void), as in C++.
Old-style definitions
K&R C had no prototypes. A definition listed the parameter names, then declared their types before the body. C89 kept this form for compatibility and marked it obsolescent. C23 removed it.
int add(a, b) /* identifier list: old style, removed in C23 */
int a;
int b;
{
return a + b;
}
int before C99Calling a function with no declaration in scope was allowed until C99, which removed implicit function declarations.
Parameter adjustment
These adjustments happen in a parameter’s declared type (C17 6.7.6.3):
| Declared | Adjusted to |
|---|---|
Array of T | Pointer to T |
Function returning T | Pointer to that function |
Top-level qualifiers on parameters are not part of the function’s type, so
int f(const int) and int f(int) are the same type. The only
storage class allowed on a parameter is register. See
Array parameters.
Calling a function
Arguments are evaluated and copied to the parameters in an unspecified order, then the call happens (C17 6.5.2.2). There is a sequence point after the arguments and before the call. Each argument is converted as if by assignment to the parameter’s type, when a prototype is visible.
When no prototype is visible, or for a ... argument, the default
argument promotions apply: integer promotions, and float to
double. Calling a function with a different number of arguments, or with
incompatible types, is undefined behaviour unless the prototype allows it.
A function may call itself, and so may main. C does not allow calling a
function at file scope outside of any function, or defining one inside another.
Variadic functions
A parameter list ending in ... accepts extra arguments of any type. Before C23
at least one named parameter was required. C23 allows int f(...). The extra
arguments are read with the macros in <stdarg.h> (C17 7.16):
| Macro | Purpose | Since |
|---|---|---|
va_list | Type of the argument cursor | C89 |
va_start(ap, last) | Starts the cursor after the last named parameter. In C23 the second argument is optional | C89 |
va_arg(ap, type) | Reads the next argument as type | C89 |
va_copy(dest, src) | Copies a cursor | C99 |
va_end(ap) | Finishes with the cursor | C89 |
#include <stdarg.h>
int sum(int count, ...)
{
va_list ap;
int total = 0;
va_start(ap, count);
for (int i = 0; i < count; i++)
total += va_arg(ap, int);
va_end(ap);
return total;
}
sum(3, 10, 20, 30); /* 60 */
Because of the default promotions, va_arg(ap, float) and
va_arg(ap, char) are not valid: those types are promoted to double and
int. Reading an argument with the wrong type is undefined.
__func__
The predefined identifier __func__ (C99) is available inside every function
body. It behaves as if the function began with this declaration (C17 6.4.2.2):
static const char __func__[] = "function-name";
It is an ordinary identifier, not a macro, so it cannot be used by the preprocessor.
Functions that do not return
_Noreturn (C11) and the C23 attribute [[noreturn]] declare a
function that never returns to its caller, such as exit and abort
(C17 6.7.4). If the function does return, the behaviour is undefined. C23 deprecates
_Noreturn and the <stdnoreturn.h> header.
The main function
In a hosted environment, a program starts by calling main. The forms are
(C17 5.1.2.2.1):
int main(void) { /* ... */ }
int main(int argc, char *argv[]) { /* ... */ }
An implementation may also allow other forms. The name argv can be declared as
char **argv, and the parameter names can differ. The implementation declares no
prototype for main.
argcis the number of arguments and is non-negative.argv[argc]is a null pointer.argv[0]holds the program’s name, if available. If it is not, it is an empty string or null.- The strings pointed to by
argvcan be modified, and stay until the program ends.
Return value and termination
A return from the initial call to main has the same effect as calling
exit with the returned value (C17 5.1.2.2.3). Reaching the closing brace of
main returns 0, in C99 and later. 0 and
EXIT_SUCCESS report success, and EXIT_FAILURE reports failure;
both macros are in <stdlib.h>.
| Function | Effect |
|---|---|
return from main | Same as exit |
exit(n) | Runs functions registered with atexit, flushes and closes streams, ends the program |
quick_exit(n) | Runs functions registered with at_quick_exit; does not flush (C11) |
_Exit(n) | Ends without running handlers (C99) |
abort() | Abnormal termination by the SIGABRT signal |
Freestanding environments
On a freestanding system, such as a kernel or a program for a bare microcontroller, the name and type of the function called at start-up are implementation-defined, and the available library is limited (C17 5.1.2.1). See the list of freestanding headers.