1. Essence of Macros
The preprocessor is the first step in the C language compilation process, performing text replacement on the source code before actual compilation. Macro definitions are a form of string replacement mechanism that does not involve type checking or semantic analysis.
Compilation Process:
Source Code → Preprocessing → Compilation → Assembly → Linking → Executable File
2. Defining Constants
Using <span>#define</span> to define symbolic constants is the most basic usage:
#define PI 3.14159
#define MAX_SIZE 1024
#define BUFFER_LEN (MAX_SIZE * 2)
// Usage example
double area = PI * r * r;
char buffer[MAX_SIZE];
Advantages:
- Facilitates unified modification
- Improves code readability
- Compile-time constants, no runtime overhead
3. Macro Functions
3.1 Basic Usage
Macros can take parameters, achieving functionality similar to functions:
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))
// Usage
int max_val = MAX(10, 20); // Expands to ((10) > (20) ? (10) : (20))
int sq = SQUARE(5); // Expands to ((5) * (5))
Note: Importance of Parentheses
// Incorrect example
#define SQUARE_BAD(x) x * x
int result = SQUARE_BAD(1 + 2); // Expands to 1 + 2 * 1 + 2 = 5 (Incorrect!)
// Correct example
#define SQUARE(x) ((x) * (x))
int result = SQUARE(1 + 2); // Expands to ((1 + 2) * (1 + 2)) = 9
3.2 Stringizing Operator <span>#</span>
<span>#</span> converts macro parameters to strings:
#define TO_STRING(x) #x
#define LOG(var) printf(#var " = %d\n", var)
// Usage example
const char* str = TO_STRING(Hello); // Expands to "Hello"
int count = 100;
LOG(count); // Output: count = 100
3.3 Concatenation Operator <span>##</span>
<span>##</span> is used to concatenate two tokens:
#define CONCAT(a, b) a##b
#define MAKE_FUNC(name) void func_##name() {
printf("Function: " #name "\n");
}
// Usage example
int CONCAT(var, 123) = 456; // Expands to int var123 = 456;
MAKE_FUNC(init) // Generates function func_init()
MAKE_FUNC(cleanup) // Generates function func_cleanup()
3.4 Variadic Macros
C99 introduced variadic macros, using <span>...</span> and <span>__VA_ARGS__</span>:
// Basic usage
#define LOG_INFO(...) printf("[INFO] " __VA_ARGS__)
#define DEBUG(fmt, ...) printf("[%s:%d] " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
// Usage example
LOG_INFO("Server started on port %d\n", 8080);
// Expands to: printf("[INFO] " "Server started on port %d\n", 8080);
DEBUG("Value: %d, Status: %s", 42, "OK");
// Output: [main.c:10] Value: 42, Status: OK
GNU Extension: Named Variadic Parameters
#define LOG(level, fmt, args...) \
printf("[%s] " fmt "\n", level, ##args)
LOG("ERROR", "Failed to open file");
LOG("WARNING", "Disk usage: %d%%", 95);
4. File Inclusion
4.1 <span>#include</span> Directive
#include <stdio.h> // System header file, searched in system paths
#include "myheader.h" // User header file, searched in current directory first
Differences:
<span><></span>: Searches only in system include paths (<span>/usr/include</span>, compiler specified paths)<span>""</span>: Searches current directory first, then system paths
4.2 Preventing Multiple Inclusions
Method 1: Include Guard
// myheader.h
#ifndef MYHEADER_H
#define MYHEADER_H
// Header file content
void my_function();
#endif // MYHEADER_H
Method 2: <span>#pragma once</span>
// myheader.h
#pragma once
// Header file content
void my_function();
<span>#pragma once</span> is more concise but not standard C (though almost all compilers support it).
5. Command Line Definitions
Macros can be defined at compile time using the <span>-D</span> option:
gcc -DDEBUG -DVERSION=2 main.c -o app
Usage in code:
#ifdef DEBUG
printf("Debug mode enabled\n");
#endif
#ifndef VERSION
#define VERSION 1
#endif
printf("Version: %d\n", VERSION);
Practical Applications:
- Debug switches
- Version control
- Platform difference handling
- Feature trimming
6. Loop Unrolling Optimization
Macros can be used for manual loop unrolling to enhance performance:
// Regular loop
for (int i = 0; i < 4; i++) {
sum += array[i];
}
// Using macro unrolling
#define UNROLL_4(op) \
op(0); op(1); op(2); op(3);
#define ADD(i) sum += array[i]
UNROLL_4(ADD)
// After expansion, equivalent to:
// sum += array[0];
// sum += array[1];
// sum += array[2];
// sum += array[3];
Advantages: Reduces loop checks and jumps, improving CPU pipeline efficiency.
Disadvantages: Code bloat, requires careful consideration.
Summary
Macro definitions are powerful text processing tools in C language, and their proper use can:
- ✅ Enhance code reusability
- ✅ Enable compile-time calculations
- ✅ Support conditional compilation
- ⚠️ But be cautious of side effects and debugging difficulties
In the next article, we will introduce conditional compilation directives and other functionalities of the preprocessor.