Skip to main content
C, despite being a foundational language with decades of use, has several common anti-patterns that can lead to bugs, security vulnerabilities, and maintenance problems. Here are the most important anti-patterns to avoid when writing C code.
Always check return values from functions that can fail, such as memory allocation, file operations, and network calls.
Use bounded string functions and explicit buffer size management to prevent buffer overflows, which can lead to security vulnerabilities.
Always free allocated memory, even in error paths, to prevent memory leaks.
Never use gets() as it has no bounds checking. Use fgets() or other bounded input functions instead.
Be aware of integer overflow, especially when calculating sizes for memory allocation.
Always initialize variables before using them to avoid undefined behavior.
Use const for function parameters that should not be modified to communicate intent and enable compiler optimizations.
Use named constants instead of magic numbers to improve code readability and maintainability.
Avoid global variables as they create hidden dependencies and make code harder to test and reason about.
Always use header guards to prevent multiple inclusion of header files, which can lead to compilation errors.
Avoid using void* without proper type checking. Use tagged unions or other type-safe alternatives when possible.
Always use function prototypes to enable compiler type checking and avoid implicit declarations.
Always check if memory allocation functions like malloc return NULL before using the allocated memory.
Avoid using unsafe string functions like strcpy and strcat. Use bounded alternatives like strncpy and strncat, or better yet, use safer string handling libraries.
Always include a default case in switch statements to handle unexpected values.
Use static analysis tools and compiler warnings to catch common bugs and issues in your code.
Prefer inline functions over macros when possible, as they provide type checking and avoid common macro pitfalls.
Use defensive programming techniques to handle invalid inputs and edge cases.
Use consistent, well-documented error codes or an error handling system to communicate failures.