Programming · free software · the open webSubscribe via RSS

What Does It Mean for a Function to Be First-Class in C?

From the earlier reference collection. Read the current software blog.

C functions can be selected at runtime by passing and storing pointers to them. A small callback illustrates what that enables and where C’s syntax differs from languages that treat functions as ordinary values.

A function pointer is a callable value

Calling a function by name is the familiar case: sort_items(items, count) asks the program to run sort_items. C also lets code refer to a function through a pointer. A pointer can be assigned, passed to another function, and stored for later use. The receiving code can call through it without knowing which particular function was chosen.

This is a useful, limited sense of “first-class.” C supports functions as indirect call targets, but a function is not an object that can be copied with arbitrary attached state. A function pointer has a type that describes the function’s parameter and return types; incompatible function-pointer types cannot safely be substituted.

A callback selects behavior

Suppose a program has a list of readings and wants to apply a caller-selected rule to each one. It can define a callback type and accept a pointer of that type:

typedef int (*ReadingRule)(int value); and int count_matches(const int *values, size_t n, ReadingRule rule) { int count = 0; for (size_t i = 0; i < n; ++i) if (rule(values[i])) ++count; return count; }

A separate function such as int is_high(int value) { return value > 80; } can be passed as count_matches(readings, n, is_high). The counting loop remains unchanged when a different rule is needed. The caller supplies the policy; the loop supplies the traversal.

Store the choice when timing matters

The same pointer can live in a structure or array, making it possible to associate operations with entries. A user-interface component might store a function to call when activated. A parser might keep a table mapping token kinds to handler functions. In both cases, storing a pointer postpones the choice of behavior until runtime.

C does not automatically capture surrounding variables in a callback. If the rule needs a threshold that can vary, pass that context separately—for example, provide both a callback pointer and a void *context argument. The callback can interpret the context using a type the caller and callee agree on. This explicit pairing replaces the implicit captured environment offered by some languages.

Keep the type and lifetime clear

A callback API should document its signature, when the callback runs, and whether the context remains valid for that whole period. A synchronous loop can often use a pointer to a local context because the call finishes before the function returns. An asynchronous operation may retain it, so a short-lived local variable would be unsafe.

Function pointers therefore offer flexible dispatch without requiring a language runtime or object system. Their constraints are visible: matching types, explicit context, and careful lifetime management. Those constraints make the design compact, but they also belong in the interface contract.

Suggest a correction · All posts