Bộ tiền xử lý trong C không phải là một phần của trình biên dịch mà là một bước riêng biệt trong quá trình biên dịch. Nói một cách đơn giản, bộ tiền xử lý C là một công cụ thay thế văn bản và nó chỉ thị trên trình biên dịch khi cần thực hiện trước khi xử lý.
Tất cả các lệnh tiền xử lý bắt đầu bằng một biểu tượng ký tự (#), là ký tự đầu tiên không trống và dễ đọc, một chỉ thị tiền xử lý nên bắt đầu trong cột đầu tiên. Phần sau liệt kê tất cả các chỉ thị quan trọng trong tiền xử lý:
Directive |
Miêu tả |
#define |
Thay thể cho bộ tiền xử lý macro |
#include |
Chèn một header đặc biệt từ file khác |
#undef |
Không định nghĩa một macro tiền xử lý |
#ifdef |
Trả về giá trị true nếu macro này được định nghĩa |
#ifndef |
Trả về giá trị true nếu macro này không được định nghĩa |
#if |
Kiểm tra nếu điều kiện biên dịch là đúng |
#else |
Phần thay thế cho #if |
#elif |
#else một #if trong một lệnh |
#endif |
Kết thúc điều kiện tiền xử lý |
#error |
In thông báo lỗi trên stderr |
#pragma |
Thông báo các lệnh đặc biệt đến bộ biên dịch, sử dụng một phương thức được tiêu chuẩn hóa |
Phân tích các ví dụ sau để hiểu các chỉ thị khác nhau:
#define MAX_ARRAY_LENGTH 20
Chỉ thị này yêu cầu CPP thay thế các trường hợp MAX_ARRAY_LENGTH bằng 20. Sử dụng #define cho các hằng số để tăng khả năng đọc.
#include
#include "myheader.h"
Các chỉ thị này yêu cầu CPP lấy stdio.h từ System Libraries và thêm văn bản vào tệp nguồn hiện tại. Dòng tiếp theo yêu cầu CPP lấy myheader.h từ thư mục cục bộ và thêm nội dung vào tệp nguồn hiện tại.
#undef FILE_SIZE
#define FILE_SIZE 42
Nó yêu cầu CPP xác định FILE_SIZE hiện tại và xác định nó là 42.
#ifndef MESSAGE
#define MESSAGE "You wish!"
#endif
Nó báo cho CPP chỉ định MESSAGE nếu MESSAGE chưa được định nghĩa.
#ifdef DEBUG
/* Your debugging statements here */
#endif
Nó báo cho CPP xử lý các câu lệnh kèm theo nếu DEBUG được định nghĩa. Điều này rất hữu ích nếu bạn chuyển cờ -DDEBUG tới trình biên dịch gcc tại thời điểm biên dịch. Điều này sẽ xác định DEBUG, vì vậy bạn có thể bật và tắt gỡ lỗi khi đang di chuyển trong khi biên dịch.
ANSI C định nghĩa một số macro, mặc dù mỗi cái đều có sẵn để sử dụng trong lập trình, nhưng các macro được xác định trước nên không thể sửa đổi trực tiếp.
Macro |
Mô tả chi tiết |
__DATE__ |
Ngày hiện tại dưới dạng ký tự theo nghĩa đen trong định dạng "MMM DD YYYY". |
__TIME__ |
Thời gian hiện tại dưới dạng ký tự theo nghĩa "HH: MM: SS". |
__FILE__ |
Điều này chứa tên tệp hiện tại dưới dạng chuỗi ký tự. |
__LINE__ |
Điều này chứa số dòng hiện tại dưới dạng hằng số thập phân. |
__STDC__ |
Được định nghĩa là 1 khi trình biên dịch tuân thủ tiêu chuẩn ANSI. |
Hãy thử ví dụ sau:
#include
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 mã trên trong tệp test.c được biên dịch và thực thi, nó tạo ra kết quả sau:
File :test.c
Date :Jun 2 2012
Time :03:36:24
Line :8
ANSI :1
Bộ tiền xử lý C cung cấp các toán tử sau để giúp tạo macro:
Toán tử Macro Continuation (\)
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 macro quá dài cho một dòng. Ví dụ:
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
Toán tử Stringize (#)
Toán tử stringize hoặc number-sign ('#'), khi được sử dụng trong định nghĩa macro, chuyển đổi một tham số macro thành hằng số chuỗi. Toán tử này chỉ có thể được sử dụng trong một macro có một đối số hoặc danh sách tham số được chỉ định. Ví dụ:
#include
#define message_for(a, b) \
printf(#a " and " #b ": We love you!\n")
int main(void) {
message_for(Carole, Debra);
return 0;
}
Khi mã trên được biên dịch và thực hiện, nó tạo ra kết quả sau:
Carole and Debra: We love you!
Toán tử Token Pasting (##)
Toán tử dán mã thông báo (##) trong một định nghĩa macro kết hợp hai đối số. Nó cho phép hai thẻ riêng biệt trong định nghĩa macro được kết hợp thành một mã thông báo duy nhất. Ví dụ:
#include
#define tokenpaster(n) printf ("token" #n " = %d", token##n)
int main(void) {
int token34 = 40;
tokenpaster(34);
return 0;
}
Khi mã trên được biên dịch và thực hiện, nó tạo ra kết quả sau:
token34 = 40
Nó xảy ra như vậy bởi vì ví dụ này dẫn đến kết quả đầu ra thực tế sau đây từ bộ tiền xử lý:
printf ("token34 = %d", token34);
Ví dụ này cho thấy việc nối mã thông báo ## n vào mã token34 và ở đây chúng tôi đã sử dụng cả chuỗi và dán mã thông báo.
Toán tử Defined ()
Toán tử được xác định sử dụng trong các biểu thức hằng số để xác định nếu một định danh được định nghĩa bằng cách sử dụng #define. Nếu định danh được chỉ định được xác định, giá trị là đúng (khác không). Nếu ký hiệu không được xác định, giá trị là false (số không). Toán tử được định nghĩa được quy định như sau:
#include
#if !defined (MESSAGE)
#define MESSAGE "You wish!"
#endif
int main(void) {
printf("Here is the message: %s\n", MESSAGE);
return 0;
}
Khi mã trên được biên dịch và thực hiện, nó tạo ra kết quả sau:
Here is the message: You wish!
Tham số Macro
Một trong những chức năng mạnh mẽ của CPP là khả năng mô phỏng các chức năng sử dụng các macro tham số hóa. Ví dụ:
int square(int x) {
return x * x;
}
Chúng ta có thể viết lại đoạn mã trên bằng cách sử dụng macro như sau:
#define square(x) ((x) * (x))
Các macro có đối số phải được xác định bằng cách sử dụng lệnh #define trước khi chúng có thể được sử dụng. Danh sách đối số được đặt trong dấu ngoặc đơn và phải ngay lập tức theo tên macro. Không được phép sử dụng dấu cách giữa tên macro và dấu ngoặc đơn mở. Ví dụ:
#include
#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;
}
Khi mã trên được biên dịch và thực hiện, nó tạo ra kết quả sau:
Max between 20 and 10 is 20
Tài liệu về lập trình C khác: