Function Pointers
c2-1 named this chapter directly: "one way C approximates method-like dispatch without a language feature for it at all." Course 2 closes by actually building that mechanism — the same one every object-oriented language's runtime dispatch is quietly built from underneath.
Function Pointer Syntax
A pointer to a function taking two ints and returning int. The parentheses around *operation are mandatory — without them, this parses as something else entirely, a genuine syntax trap worth memorizing rather than re-deriving each time.
Assigning & Calling Through a Function Pointer
A function name decaying to a pointer to itself is genuinely the same phenomenon as array-to-pointer decay from c1-6, just applied to functions instead of arrays.
Callbacks
Passing a function pointer so another function can call it later — a real, common C idiom.
A Simple vtable-Style Dispatch Pattern
The real payoff: store a function pointer alongside data, and let different instances point at different implementations.
A second shape type populates its own struct's function pointer with a different implementation entirely — calling shape->area(shape) dispatches to the correct one, purely because the pointer was set to point there.
What C++ and Rust Formalize
This is not just an analogy — it's the literal mechanism. A C++ class with virtual methods, and Rust's own dyn Trait, both work by storing a table of function pointers alongside the data, dispatched through one level of indirection — exactly what was just built by hand above. The difference is who manages the table: here, the programmer wires it up manually and must get it right; in C++ and Rust, the compiler generates the table automatically and guarantees it's always fully and correctly populated.
| Concept | Rust dyn Trait | C function pointer struct |
|---|---|---|
| Who builds the dispatch table | the compiler, automatically | the programmer, by hand |
| Guaranteed fully populated? | yes — enforced at compile time | no — nothing checks it at all |
| Calling an unset entry | not possible — compile error | undefined behavior — a NULL or garbage call |
NULL call, or a call through a mismatched signature, with no compiler check whatsoever. This is precisely what Rust's dyn Trait and C++'s virtual mechanism guarantee away entirely.
Coding Challenges
Write two functions, add(int, int) and subtract(int, int), and a single function pointer variable that can be pointed at either one. Call it once through each, printing both results.
📄 View solutionWrite a function apply_to_all(int *arr, int n, void (*fn)(int)) that calls fn on every element of an array, and a print_doubled(int) function to pass as the callback that prints each value doubled.
📄 View solutionExtend the chapter's Circle vtable-style example with a second shape (e.g. a Rectangle) that has its own area function, both sharing a common calling pattern. Then explain what specifically would happen if a third shape's function pointer were left unset (NULL) and its area were called.
📄 View solutionChapter 7 Quick Reference — Course 2 Complete
return_type (*name)(param_types);— the parentheses around*nameare mandatory- A function name decays to a pointer to itself — the same phenomenon as
c1-6's array decay - Callbacks — passing a function pointer so another function can call it later
- A struct of data plus a function pointer is a hand-rolled vtable — real dispatch, written manually
- C++'s
virtualand Rust'sdyn Traitare the exact same mechanism, compiler-managed and guaranteed correct — this chapter's version is neither - Course 2 complete. Course 3 covers bit manipulation, hand-built data structures, concurrency, undefined behavior in full depth, debugging tooling, and a real capstone CLI project