Functions in C
Functions are the primary unit of abstraction in C. They encapsulate logic, enable reuse, and map directly to call stack frames.
Basic Functions
#include <stdio.h>
int square(int x) {
return x * x;
}
void greet(const char *name) {
printf("Hello, %s!\n", name);
}
int main(void) {
printf("%d\n", square(5));
greet("World");
return 0;
}
Pass by Value vs Pointer
void swap_wrong(int a, int b) {
int tmp = a; a = b; b = tmp; /* only swaps copies */
}
void swap(int *a, int *b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
int main(void) {
int x = 1, y = 2;
swap(&x, &y); /* x=2, y=1 */
}
Arrays decay to pointers when passed — the function cannot know the original size.
Recursion
unsigned long factorial(unsigned n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
int fib(int n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2); /* inefficient — use iteration */
}
Each recursive call consumes stack space. Deep recursion can overflow the stack.
Static Variables
void counter(void) {
static int count = 0; /* persists between calls */
count++;
printf("Called %d times\n", count);
}
File-scope static limits visibility to the translation unit — internal linkage.
Function Pointers
#include <stdio.h>
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int compute(int (*op)(int, int), int x, int y) {
return op(x, y);
}
int main(void) {
printf("%d\n", compute(add, 5, 3)); /* 8 */
printf("%d\n", compute(sub, 5, 3)); /* 2 */
}
Common Pitfalls
- Treating compiler warnings as optional rather than actionable feedback.
- Skipping error checks on library and system calls.
- Copy-pasting examples without adapting to your project’s conventions.
Best Practices
- Enable strict compiler warnings and fix them before merging.
- Write small, testable units with clear input/output contracts.
- Document non-obvious invariants and preconditions.
- Use version control and code review for every change.
Memory and Performance Notes
Function call overhead is small but inlining (static inline) eliminates it for small helpers in headers.
Exercise
Implement int max3(int a, int b, int c) and a function pointer table for +, -, *, / operations on two integers.
Hint: Function pointer syntax reads right-to-left: int (*fp)(int,int) is a pointer to function taking two ints.
Summary
Apply these concepts in small programs before moving to larger projects. Combine with adjacent topics in the learning path for deeper mastery.
Real-World Application
These concepts appear in production codebases — from operating system kernels to embedded firmware. Study open-source projects that use this topic extensively to see idiomatic patterns at scale.
Debugging Checklist
- Reproduce the issue with the smallest possible input.
- Enable compiler warnings and sanitizers.
- Use a debugger to inspect state at the failure point.
- Verify assumptions about types, sizes, and return values.
- Compare working and broken code paths side by side.
- Write a regression test once the bug is fixed.
Further Reading
Consult the ISO C standard, Effective C by Robert C. Seacord, and your compiler documentation for platform-specific behavior.
Quick Reference
Review the code examples on this page before starting the exercise. Type them manually to build muscle memory.