Functions#
A function in C has a return type, a name, a parameter list,
and a body. There are no closures, no methods, no overloading.
Functions are first-class values (function pointers) and
support variadic parameters through <stdarg.h>.
For function pointers used inside structs to fake methods, see
OOP. For the call / return semantics around defer-
style cleanup, see Control flow.
Declaration vs definition#
A declaration (a prototype) introduces the name and signature; a definition also provides the body. The operator puts prototypes in headers and definitions in source files.
/* in scan.h */
int probe(const char *host, int port);
/* in scan.c */
int probe(const char *host, int port) {
/* … */
return 0;
}
A function with no body but visible to other translation units
is declared extern; the linker resolves it.
Parameters#
Parameters are typed and passed by value. To mutate caller state, pass a pointer.
void inc(int *p) { (*p)++; }
int x = 0;
inc(&x); /* x is now 1 */
Arrays decay to pointers when passed; the operator passes the length alongside.
void print(const int *xs, size_t n) {
for (size_t i = 0; i < n; i++) printf("%d\n", xs[i]);
}
Strings are const char * when not modified; char * when
the function writes into them.
Return values#
A function returns at most one value. Multiple results need an output pointer or a struct return.
int divmod(int a, int b, int *rem) {
*rem = a % b;
return a / b;
}
struct pair { int x, y; };
struct pair make_pair(int x, int y) {
return (struct pair){.x = x, .y = y};
}
The classic convention: return an error code as the value; write results through pointer parameters.
int load(const char *path, char **out, size_t *out_n) {
FILE *f = fopen(path, "rb");
if (!f) return -1;
/* … */
*out = buf;
*out_n = len;
return 0;
}
static#
static on a function gives it internal linkage; only the
current translation unit can call it. The operator marks
helpers static by default; only deliberately-public functions
omit static.
static int normalize(int x) { /* private to this .c */ return x; }
int process(int x) { return normalize(x); }
inline#
inline is a hint to the compiler. Modern compilers usually
inline whenever profitable; write inline for
trivial helpers in headers so the linker does not complain about
multiple definitions.
/* in header */
static inline int max(int a, int b) { return a > b ? a : b; }
Variadic#
... declares a variadic parameter list; <stdarg.h>’s
va_start / va_arg / va_end macros read it.
#include <stdarg.h>
void logf(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
logf("%s=%d\n", "port", 8080);
The variadic API has no type information; the caller and callee
must agree on the format. printf family uses the format
string as the source of truth.
Function pointers#
A function pointer holds the address of a function. The type
mirrors the signature with a (*) around the name.
int (*op)(int, int); /* pointer to int(int,int) */
op = &add;
int z = op(1, 2);
typedef int (*op_t)(int, int);
op_t handler = &add;
Use function pointers for plugin tables, sort comparators, vtables (see OOP).
int cmp_int(const void *a, const void *b) {
int x = *(const int*)a, y = *(const int*)b;
return (x > y) - (x < y);
}
int xs[] = {3, 1, 4, 1, 5};
qsort(xs, 5, sizeof xs[0], cmp_int);
Recursion#
C supports recursion; there are no tail-call guarantees, so deep recursion can blow the stack. The operator rewrites deeply-recursive algorithms iteratively or with an explicit heap-allocated stack.
long factorial(long n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
Compound literals as call arguments#
C99+ lets the operator build an aggregate inline.
process(&(struct config){.host = "0.0.0.0", .port = 80});
The lifetime of the compound literal is the enclosing block; storing its address past the block is undefined behaviour.
__attribute__#
GCC / Clang attributes carry extra info to the compiler. The operator’s go-tos.
Attribute |
Effect |
|---|---|
|
function never returns. |
|
validate |
|
warn if the caller ignores the return value. |
|
call |
C23 standardises some of these as [[noreturn]],
[[deprecated]], [[nodiscard]].