Tiền xử lý (Preprocessor) là gì?
Chương trình chạy trước compiler, xử lý các chỉ thị bắt đầu bằng #. Không hiểu C, chỉ xử lý văn bản.
3 nhóm chính
- Macro substitution - Thay thế macro
- File inclusion - Nhúng file
- Conditional compilation - Biên dịch điều kiện
1. Macro Substitution Directives
#define - Định nghĩa macro
// Hằng số
#define PI 3.14159
#define MAX_SIZE 100
#define TEN_CHUONG_TRINH "MyApp"
// Macro hàm (function-like)
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define BINH_PUONG(x) ((x) * (x))
#define IN_LOG(fmt, ...) printf("[LOG] " fmt "\n", ##__VA_ARGS__)
// Macro nhiều dòng (dùng \)
#define KHOI_TAO_MANG(arr, size) \
do { \
for (int i = 0; i < size; i++) arr[i] = 0; \
} while(0)
#undef - Hủy macro
#define DEBUG 1
// ... code dùng DEBUG ...
#undef DEBUG
// DEBUG không còn hiệu lực
Ví dụ chương trình Macro
#include <stdio.h>
#define WAIT getchar()
#define IN_CHUOI(s) printf("%s\n", s)
int main() {
IN_CHUOI("Xin chao");
IN_CHUOI("Tien xu ly macro");
WAIT;
return 0;
}
2. File Inclusion Directives
#include - Nhúng file
// Thư viện hệ thống - tìm trong include path
#include
#include
// File dự án - tìm trong thư mục hiện tại trước
#include "myheader.h"
#include "utils/string.h"
3. Conditional Compilation (Biên dịch điều kiện)
#ifdef / #ifndef / #endif
#define DEBUG 1
#ifdef DEBUG
printf("Debug: x = %d\n", x);
#endif
#ifndef NDEBUG
assert(x > 0);
#endif
#if / #elif / #else / #endif
#define VERSION 2
#if VERSION == 1
printf("Phien ban 1\n");
#elif VERSION == 2
printf("Phien ban 2 - Moi\n");
#else
printf("Phien ban khac\n");
#endif
#error / #warning
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
#error "Can C99 or later"
#endif
#if defined(__linux__)
#warning "Chay tren Linux, kiem tra path separator"
#endif
Defined operator
#if defined(DEBUG) && defined(VERBOSE)
// Cả DEBUG và VERBOSE đều định nghĩa
#endif
#if defined(WIN32) || defined(_WIN32)
// Windows
#endif
Các Macro định sẵn (Predefined Macros)
| Macro | Giá trị |
|---|---|
__FILE__ | Tên file nguồn hiện tại (string) |
__LINE__ | Số dòng hiện tại (int) |
__DATE__ | Ngày compile "Mmm dd yyyy" |
__TIME__ | Giờ compile "hh:mm:ss" |
__func__ | Tên hàm hiện tại (C99) |
__STDC__ | 1 nếu tuân thủ ANSI C |
__STDC_VERSION__ | Phiên bản C (199409L, 199901L, 201112L...) |
Chương trình demo Predefined Macros
#include <stdio.h>
void hamDemo() {
printf("File: %s\n", __FILE__);
printf("Dong: %d\n", __LINE__);
printf("Ngay: %s\n", __DATE__);
printf("Gio: %s\n", __TIME__);
printf("Ham: %s\n", __func__);
}
int main() {
hamDemo();
return 0;
}
Kết quả
File: main.c
Dong: 5
Ngay: Dec 15 2024
Gio: 14:30:25
Ham: hamDemo
Stringizing (#) và Token Pasting (##)
// Stringizing: biến tham số thành chuỗi
#define TO_STRING(x) #x
printf("%s\n", TO_STRING(Hello)); // "Hello"
// Token Pasting: ghép token
#define CONCAT(a, b) a##b
int xy = 100;
printf("%d\n", CONCAT(x, y)); // 100
// Ứng dụng: Tạo tên biến/hàm tự động
#define DECLARE_ARRAY(name, size) int name##_arr[size] = {0}
DECLARE_ARRAY(student, 50); // → int student_arr[50] = {0};
Best Practice
- Dùng
const/enumthay macro cho hằng số (type safety, debug) - Dùng
inlinethay macro hàm - Luôn bao tham số macro trong ngoặc:
#define MAX(a,b) ((a)>(b)?(a):(b)) - Dùng
do { ... } while(0)cho macro nhiều câu lệnh - Include guard cho mọi header
- Tránh macro phức tạp, khó debug