12.1 Preprocessor Directives
Directives begin with # and are processed BEFORE compilation, by simple text substitution. #include pulls in header file contents (angle brackets for standard library, quotes for local files). #define creates macros — simple constants (#define PI 3.14) or parameterized macros (#define SQ(x) ((x)*(x))). Conditional compilation (#ifdef, #ifndef, #endif) includes or excludes code blocks based on whether a macro is defined — commonly used for header guards and platform-specific code.
#define SQ(x) ((x) * (x)) // macro, expanded by text substitution
#ifndef MYHEADER_H // header guard prevents double inclusion
#define MYHEADER_H
// declarations...
#endif
Common Macro Trap:
Always surround macro arguments with parentheses: #define SQ(x) ((x) * (x)). Without parentheses, #define SQ(x) x * x called with SQ(2 + 3) expands to 2 + 3 * 2 + 3 = 11, instead of (2 + 3) * (2 + 3) = 25!
12.2 Function Pointers
A function pointer stores the address of a function instead of a variable, allowing a function to be passed around, stored in a data structure, and called indirectly: int (*fp)(int,int) = add; then fp(2,3) calls add(2,3). This underlies callbacks, plugin systems, and 'dispatch tables' that select behaviour at runtime rather than compile time.
int (*fp)(int, int) = add; // fp now points to function add()
Calling fp(2,3) invokes add(2,3) indirectly through the pointer -
used for callbacks, dispatch tables and passing behaviour as data.
12.3 Bit Fields and Bitwise Operators
Bitwise operators (& AND, | OR, ^ XOR, ~ NOT, << left shift, >> right shift) manipulate individual bits directly — used for flags, masks, and performance-critical low-level code. Bit fields let a struct member occupy an exact number of bits rather than a full byte/word (unsigned int flag : 1;), packing multiple small values tightly together, which is common in embedded programming and network protocol headers.
struct PacketHeader {
unsigned int version : 4; // 4 bits (0-15)
unsigned int type : 4; // 4 bits
unsigned int flagAck : 1; // 1 bit flag (0 or 1)
unsigned int flagSyn : 1; // 1 bit flag
};