Bộ tiền xử lý C (C Preprocessor) không phải là một phần của trình biên dịch (compiler), nhưng là một bước riêng biệt trong quá trình biên dịch (compile). Nói một cách dễ hiểu, Bộ tiền xử lý C chỉ là một công cụ thay thế văn bản (text) và nó hướng dẫn compiler thực hiện xử lý cần thiết trước khi biên dịch thực sự. Chúng ta sẽ gọi tắt Bộ tiền xử lý C là CPP.
Tất cả các lệnh tiền xử lý bắt đầu bằng ký hiệu hash (#). Phần sau liệt kê tất cả chỉ thị tiền xử lý quan trọng.
Sr.No. | Directive & Description |
1 | #define Substitutes a preprocessor macro. |
2 | #include Inserts a particular header from another file. |
3 | #undef Undefines a preprocessor macro. |
4 | #ifdef Returns true if this macro is defined. |
5 | #ifndef Returns true if this macro is not defined. |
6 | #if Tests if a compile time condition is true. |
7 | #else The alternative for #if. |
8 | #elif #else and #if in one statement. |
9 | #endif Ends preprocessor conditional. |
10 | #error Prints error message on stderr. |
11 | #pragma Issues special commands to the compiler, using a standardized method. |
Ví Dụ Tiền Xử Lý
Chúng ta hãy phân tích những ví dụ sau để hiểu các loại chỉ thị (directive).
#define MAX_ARRAY_LENGTH 20
Chỉ thị này báo cho CPP thay thế MAX_ARRAY_LENGTH với 20. Sử dụng #define với hằng số giúp khả năng đọc dễ hơn.
#include <stdio.h> #include “myheader.h”
Những chỉ thị này báo CPP lấy stdio.h từ Thư viện Hệ thống (System Libraries) và thêm text trong file stdio.h vào file source hiện tại. Dòng kế tiếp báo CPP lấy myheader.h từ thư mục hiện tại và thêm nội dung của file myheader.h vào file nguồn hiện tại.
#undef FILE_SIZE #define FILE_SIZE 42
Báo cho CPP undefine macro FILE_SIZE đang tồn tại và define nó thành 42
#ifndef MESSAGE #define MESSAGE "You wish!" #endif
Nếu macro MESSAGE chưa được define thì define một macro MESSAGE
#ifdef DEBUG /* Your debugging statements here */ #endif
Dùng để yêu cầu CPP xử lý những lệnh bên trong, nếu macro DEBUG đã được define. Sẽ rất hữu dụng nếu bạn truyền cờ -DDEBUG cho bộ biên dịch gcc khi biên dịch. Việc này sẽ define DEBUG, do đó bạn có thể bật và tắt gỡ lỗi ngay lập tức trong quá trình biên dịch.
Macro Được Định Nghĩa Trước (Predefined Macros)
ANSI C có định nghĩa sẵn một số macro. Mặc dù chúng có sẵn để lập trình, tuy nhiên những macro này không nên bị thay đổi trực tiếp.
Sr.No. | Macro & Description |
1 | __DATE__ The current date as a character literal in “MMM DD YYYY” format. |
2 | __TIME__ The current time as a character literal in “HH:MM:SS” format. |
3 | __FILE__ This contains the current filename as a string literal. |
4 | __LINE__ This contains the current line number as a decimal constant. |
5 | __STDC__ Defined as 1 when the compiler complies with the ANSI standard. |
Hãy thử ví dụ sau:
#include <stdio.h> int main() { printf("File :%s\n", __FILE__ ); printf("Date :%s\n", __DATE__ ); printf("Time :%s\n", __TIME__ ); printf("Line :%d\n", __LINE__ ); printf("ANSI :%d\n", __STDC__ ); }
Khi đoạn code trên được compile và execute, nó sẽ cho kết quả như sau
File :test.c Date :Jun 2 2012 Time :03:36:24 Line :8 ANSI :1
Toán Tử Tiền Xử Lý (Preprocessor Operators)
Bộ tiền xử lý C cung cấp các toán tử sau để giúp tạo macro.
Toán Tử Tiếp Tục Macro (\) | The Macro Continuation (\) Operator
Một macro thường được giới hạn trong một dòng. Toán tử tiếp tục macro (\) được sử dụng để tiếp tục một macro quá dài đối với một dòng. Ví dụ:
#define message_for(a, b) \ printf(#a " and " #b ": We love you!\n")
Toán Tử Chuỗi Kí Tự Hóa (#)
Toán tử chuỗi ký tự hóa (stringize) là kí tự number-sign (‘#’), khi được sử dụng trong một định nghĩa macro, sẽ chuyển đổi một tham số macro thành một hằng chuỗi (string constant). Toán tử này chỉ có thể được sử dụng trong một macro có đối số (argument) hoặc danh sách tham số (parameter lest) cụ thể. Ví dụ.
#include <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") int main(void) { message_for(Carole, Debra); return 0; }
Biên dịch và chạy đoạn code trên, ta nhận được kết quả sau.
Carole and Debra: We love you!
Toán Tử Dán Token (##)
Toán tử dán token (token-pasting) ‘##’ cùng với một định nghĩa macro kết hợp hai đối số. Nó cho phép hai token riêng biệt trong định nghĩa macro được tham gia thành một token duy nhất. Ví dụ:
#include <stdio.h> #define tokenpaster(n) printf ("token" #n " = %d", token##n) int main(void) { int token34 = 40; tokenpaster(34); return 0; }
Đoạn code ví dụ trên sẽ cho kết quả sau.
token34 = 40
Có được kết quả là do output từ bộ tiền xử lý tạo ra.
printf ("token34 = %d", token34);
Ví dụ này cho thấy cách chuyển đổi token##n
thành token34
và ở đây chúng ta đã sử dụng cả chuỗi ký tự hóa (stringize) và dán token (token-pasting).
Toán Tử defined()
Toán tử tiền xử lý defined được sử dụng trong các biểu thức hằng (constant expression) để kiểm tra xem một định danh (identifier) có được định nghĩa (define) bằng cách sử dụng #define hay không. Nếu định danh này đã được định nghĩa, thì giá trị là true (non-zero). Nếu không được định nghĩa thì giá trị là false (zero). Toán tử defined có dạng như sau.
#include <stdio.h> #if !defined (MESSAGE) #define MESSAGE "You wish!" #endif int main(void) { printf("Here is the message: %s\n", MESSAGE); return 0; }
Đoạn code trên sau khi compile và chạy sẽ cho kết quả sau.
Here is the message: You wish!
Macro Được Tham Số Hóa (Parameterized Macros)
Một trong những chức năng ưu việt của CPP là khả năng mô phỏng hảm (function) bằng cách sử dụng macro được tham số hóa (parameterized macro). Ví dụ: chúng ta có thể code một hàm bình phương một số như sau.
int square(int x) { return x * x; }
Chúng ta có thể viết lại hàm trên bằng cách sử dụng macro như sau.
#define square(x) ((x) * (x))
Macro với đối số square(x) phải được định nghĩa bằng dẫn xuất (directive) #define trước khi được sử dụng.
Danh sách đối số phải được đặt trong dấu ngoặc đơn và ngay sau tên macro. Lưu ý, không được phép có khoảng cách (space) ở giữa tên macro và dấu mở ngoặc đơn. Ví dụ.
#include <stdio.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main(void) { printf("Max between 20 and 10 is %d\n", MAX(10, 20)); return 0; }
Sau khi biên dịch và chạy đoạn code trên, ta được kết quả như sau.
Max between 20 and 10 is 20
Xem thêm Macro Arguments của GNU.
Đôi khi người ta có thể dùng macro để giảm số dòng code, chi tiết có thể đọc thêm ở trang Stackoverflow How to define function as macro?