Preprocessor (Bộ tiền xử lý) chạy trước compiler, xử lý các chỉ thị (directives) bắt đầu bằng #. Nó thực hiện: include file, định nghĩa macro, biên dịch điều kiện, thay thế văn bản.
Các chỉ thị chính
1. #include - Nhúng file
#include <stdio.h> // File hệ thống (tim trong include path)
#include "myheader.h" // File dự án (tim trong thư mục hiện tại trước)
2. #define - Định nghĩa macro
// Hằng số
#define PI 3.14159
#define MAX_SIZE 100
// Macro hàm (function-like)
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
// Macro nhiều dòng (dùng \)
#define LOG(msg) \
do { \
fprintf(stderr, "[%s:%d] %s\n", __FILE__, __LINE__, msg); \
} while(0)
Ví dụ sử dụng macro
#include <iostream>
#define PI 3.14159
int main() {
std::cout << "PI = " << PI << std::endl; // Thay thế văn bản: PI -> 3.14159
}
Kết quả
PI = 3.14159
3. Biên dịch điều kiện (Conditional Compilation)
// #ifdef / #ifndef - Kiểm tra macro đã định nghĩa
#ifdef DEBUG
std::cerr << "Debug: x = " << x << std::endl;
#endif
#ifndef NULL
#define NULL 0
#endif
// #if / #elif / #else / #endif - Điều kiện biểu thức
#if VERSION >= 2
// Code phiên bản 2+
#elif VERSION == 1
// Code phiên bản 1
#else
#error "Phien ban khong ho tro"
#endif
// #if 0 - Comment khối code lớn
#if 0
// Code này bị bỏ qua khi biên dịch
old_function();
deprecated_code();
#endif
Ví dụ hoàn chỉnh
#include <iostream>
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? (a) : (b))
int main() {
int i = 100, j = 30;
#ifdef DEBUG
std::cerr << "Trace: Vao main\n";
#endif
#if 0
// Đoạn này không biên dịch
std::cout << "Bi comment boi #if 0\n";
#endif
std::cout << "Min = " << MIN(i, j) << std::endl;
#ifdef DEBUG
std::cerr << "Trace: Ra main\n";
#endif
}
Kết quả
Trace: Vao main Min = 30 Trace: Ra main
4. #undef - Hủy định nghĩa macro
#define TEMP 100
// ... dùng TEMP ...
#undef TEMP // Không còn dùng được TEMP
5. #line - Thay đổi dòng/file cho error message
#line 100 "custom_file.c"
int x; // Lỗi sẽ báo: custom_file.c:100
6. #pragma - Chỉ thị compiler-specific
#pragma once // Header guard hiện đại (hầu hết compiler hỗ trợ)
#pragma warning(disable: 4996) // MSVC tắt warning
#pragma GCC optimize("O3") // GCC bật tối ưu
Quy trình tiền xử lý
- Đọc file nguồn
- Xử lý
#include(đệ quy nhúng file) - Xử lý
#define,#undef - Xử lý
#if,#ifdef,#else,#endif(loại bỏ code không biên dịch) - Thay thế macro trong code
- Xuất file đã tiền xử lý (translation unit) cho compiler
Xem file sau tiền xử lý
# GCC/Clang:
gcc -E source.c -o source.i
# MSVC:
cl /E source.c
Lưu ý quan trọng
- Preprocessor không hiểu cú pháp C/C++ — chỉ xử lý văn bản
- Macro không tuân theo scope (phạm vi) của C++
- Ưu tiên
const/constexpr,inline function,templatethay vì macro #pragma oncethay thế header guard truyền thống