💡 The post is primarily inspired by this article, click to read more.
Motivation
Function pointers in C have a reputation for being difficult to use and prone to bugs. The syntax is confusing, and misusing them can lead to crashes or undefined behavior. However, when applied carefully, function pointers enable flexible and extensible code. In this post, I’ll explore best practices for making function pointers more readable, safe, and usable in C.
Please refer to 👉 my previous post for an introduction to the basics of function pointers.
Readable syntax with typedefs
The syntax for declaring raw function pointer types in C is messy:
A better approach is to typedef the function pointer:
This makes the code more readable and allows catching errors at compile time. Use descriptive typedefs for all function pointer types.
Safety first
Use const
to prevent accidentally modifying function pointers:
Always check for NULL
before calling a function pointer:
When indexing function pointer arrays, validate the bounds:
Consider an error handler function as the last entry in the array to catch off-by-one errors.
Uses cases
Some common uses cases are:
- Dispatch tables/switch statements
- Plugin architectures
- Asynchronous callbacks/events
- Decoupling event sources from handlers
Code snippets
Define a typedef for the function pointer:
Declare a function pointer variable:
Define a function accepting a function pointer argument:
An array of function pointers acting as a dispatch table:
Using a function pointer for a plugin architecture:
Conclusion
With some care taken to use typedefs, validation, and bounds checking, function pointers can be used effectively in C without introducing bugs. They enable useful patterns like dispatch tables and callbacks while avoiding common pitfalls.
- Use typedefs to define cleaner syntax for function pointer types. This makes the code more readable.
- Declare function pointers as constant whenever possible to prevent accidental modification.
- Always validate that function pointers are not
NULL
before use. - Do bounds checking when indexing into arrays of function pointers to avoid calling invalid functions.
- Consider using an error handling function as the last entry in a function pointer array to catch off-by-one errors.
- Fill any “holes” in function pointer arrays with default handler functions rather than nulls.