› Module 12: Preprocessor & Advanced Topics › 12.1 Preprocessor Directives
M O D U L E

Module 12: Preprocessor & Advanced Topics

Compile-time text substitution, indirect calls through function pointers, and low-level bit manipulation.

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()
Function add(int,int) at address 0x4000 points to Function pointer fp value = 0x4000

Calling fp(2,3) invokes add(2,3) indirectly through the pointer -
used for callbacks, dispatch tables and passing behaviour as data.

A function pointer holding a function's entry address.

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
};
MODULE COMPREHENSIVE QUIZ

Module 12 Final Quiz

Test your understanding across all 3 topics of this module with interview-grade questions, tricky edge cases, and code output predictions.

Take Module 12 Final Quiz →
Yukti Mascot