Preprocessor trong C không phải là một phần của trình biên dịch, nhưng 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, một bộ tiền xử lý C chỉ là một công cụ thay thế văn bản và nó chỉ thị trình biên dịch cần thực hiện trước khi biên dịch. Chúng ta sẽ tham khảo bộ tiền xử lý C dưới dạng CPP.
Tất cả các lệnh tiền xử lý bắt đầu bằng một biểu tượng dấu thăng (#). Nó phải là ký tự đầu tiên 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ị tiền xử lý quan trọng.
No | Chỉ thị & Mô tả |
---|---|
1 | #define Thay thế macro tiền xử lý. |
2 | #include Chèn một header cụ thể từ một file khác. |
3 | #undef Hủy định nghĩa macro tiền xử lý. |
4 | #ifdef Trả về true nếu macro được định nghĩa. |
5 | #ifndef Trả về true nếu macro không được định nghĩa. |
6 | #if Kiểm tra nếu một điều kiện thời gian biên dịch là true. |
7 | #else Phương án thay thế theo sau #if. |
8 | #elif #else và #if trong một câu lệnh. |
9 | #endif Kết thúc tiền xử lý có điều kiện. |
10 | #error In thông báo lỗi trên stderr. |
11 | #pragma Đưa ra các lệnh đặc biệt cho trình biên dịch, sử dụng một phương thức được chuẩn hóa. |
Nội dung chính
Ví dụ về Preprocessor
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 <stdio.h> #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 "Have a nice day!" #endif
Nó báo cho CPP chỉ định MESSAGE nếu MESSAGE chưa được định nghĩa.
#ifdef DEBUG /* khai bao cac lenh debug o day */ #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.
Macro được định nghĩa trước
ANSI C định nghĩa một số macro. Mặc dù mỗi cái có sẵn để sử dụng trong lập trình, các macro được xác định trước không nên được sửa đổi trực tiếp.
No | Macro & Mô tả |
---|---|
1 | __DATE__ Ngày hiện tại có định dạng "MMM DD YYYY". |
2 | __TIME__ Thời gian hiện tại có định dạng "HH:MM:SS". |
3 | __FILE__ Chứa tên của file hiện tại có kiểu chuỗi. |
4 | __LINE__ Chứa số dòng hiện tại dưới dạng hằng số thập phân. |
5 | __STDC__ Được định nghĩa là 1 khi trình biên dịch tuân thủ tiêu chuẩn ANSI. |
Ví dụ:
#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__ ); }
Kết quả:
File :D:\c-examples\test.c Date :Nov 2 2018 Time :14:21:08 Line :8 ANSI :1
Các toán tử tiền xử lý
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 (\)
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 ": Have a nice day!\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 <stdio.h> #define message_for(a, b) \ printf(#a " and " #b ": Have a nice day!\n") int main(void) { message_for(Hai, Tam); return 0; }
Khi mã trên được biên dịch và thực hiện, nó tạo ra kết quả sau:
Hai and Tam: Have a nice day!
Toán tử Token Pasting (##)
Toán tử Token Pasting (##) trong một macro #define 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 <stdio.h> #define tokenpaster(n) printf ("token" #n " = %d", token##n) int main(void) { int token34 = 40; tokenpaster(34); return 0; }
Kết quả:
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ý:
Kết quả:
printf ("token34 = %d", token34);
Ví dụ cho thấy việc nối chuỗi token##n thành token34 và ở đây chúng ta đã sử dụng stringize và token-pasting.
Toán tử Defined()
Toán tử tiền xử lý defined được sử dụng trong biểu thức hằng số để xác định một identifier được định nghĩa sử dụng #define. Nếu identifier được chỉ định là defined, gía trị là true (non-zero). Nếu không được định nghĩa giá trị là false (zero). Ví dụ:
#include <stdio.h> #if !defined (MESSAGE) #define MESSAGE "Have a nice day!" #endif int main(void) { printf("Message: %s\n", MESSAGE); return 0; }
Kết quả:
Message: Have a nice day!
Macro tham số
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ụ, chương trình tính diện tính hình vuông như sau:
int square(int x) { return x * x; }
Chúng ta có thể viết lại chương trình 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 ngay sau 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 <stdio.h> #define MAX(x,y) ((x) > (y) ? (x) : (y)) int main(void) { printf("Max giua 20 va 10 la %d\n", MAX(10, 20)); return 0; }
Kết quả:
Max giua 20 va 10 la 20