[Khóa học C++] Bài 16 - A first look at the preprocessor

Share:

Preprocessor (hay còn gọi là tiền xử lý) là một chương trình đặc biệt, tách rời với chương trình C++ bạn thường viết, khi chương trình của bạn được biên dịch, nó chạy trước compiler để xử lý các text (văn bản).

Khi preprocessor chạy, nó sẽ quét source code của bạn từ trên xuống dưới trong mỗi file. Nó quét source code của bạn làm gì? Câu trả lời là, để tìm các directive (chỉ thị).
Directive là các lệnh được bắt đầu với ký tự # và kết thúc lệnh bằng ký tự xuống hàng, không có dấu chấm phẩy ;.

Preprocessor không thông minh cho lắm, nó không hiểu cú pháp của C++. Và nó sẽ kiểm soát tất cả source code trước khi chương trình được biên dịch.
Output của preprocessor sẽ được đưa vào trình biên dịch khi bạn biên dịch chương trình.

Một cách tóm tắt: Chương trình của bạn sẽ được xử lý trong preprocessor trước khi được biên dịch.
Và preprocessor không sửa hay có bất kì thao tác thay đổi nào trong source code bạn đã viết ra, mà nó ảnh hướng tới code của bạn theo những luật bạn đã đặt ra.

Bây giờ, chúng ta hãy đi qua các loại directive, từ đây bạn sẽ hiểu được preprocessor hoạt động như thế nào.

Includes

Trong các bài trước, bạn đã từng thấy #include, và đây chính là một directive. Bạn hãy nhớ lại rằng, khi bạn include một file, toàn bộ nội dung của file được include sẽ được copy paste vào ngay tại ví trí nó được include. Lưu ý là điều này diễn ra trước khi chương trình thực sự được biên dịch, và chúng ta nói rằng, đây là một hành động của preprocessor.

Trong bài header file, bạn cũng đã biết về 2 cách sử dụng include là: #include <file name> hoặc #include "file name". Nếu bạn đã quên thì hãy quay lại ngay bài header file.

Macro defines

Một directive khác bạn sẽ được học chính là #define, người ta thường gọi đây là macro. Một macro là một luật dùng để xử lý text do chính bạn tạo ra. Nó sẽ biến đổi các input text thành các output text.

Có 2 loại macro là: object-like macro và function-like macro (function-like macro bạn sẽ không học trong bài này).
Có 2 cách để sử dụng object-like macro: Có text thay thế (substitution text) và không có text thay thế (without substitution text).

Object-like macros with substitution text

Bạn hãy xem ví dụ sau:


Khi preprocessor đọc tới dòng số 1, và phát hiện ra directive #define, lúc này MY_FAVORITE_NUMBER chính là một identifier, và 9 là một substitution text. Kết quả là ở đâu có MY_FAVORITE_NUMBER thì sẽ được thay thế bằng 9.
Và đây là kết quả sau khi preprocessor chạy:

Khi chương trình được biên dịch xong và chạy, My favorite number is: 9 sẽ xuất hiện ở màn hình console.

Object-like macros without substitution text

Dưới đây là một ví dụ về một macro mà không có text thay thế.

Cách hoạt động của macro này tương tự với macro có text để thay thế. Khi preprcessor quét trong file, mỗi khi gặp identifier USE_YEN thì chương trình sẽ thay thế nó bằng rỗng, nghĩa là xóa nó đi và không có gì xuất hiện cả.

Bạn có nghĩ rằng macro này vô nghĩa? Hoàn toàn không, bạn hãy xem phần tiếp theo.

Conditional compilation

Trong chương trình của bạn, nhiều khi có một số đoạn code, bạn không muốn compile nó, bạn chỉ compile nó trong một số trường hợp nhất định (ví dụ như debug), bạn sẽ sử dụng các directive sau để thực hiện biên dịch có điều kiện: #ifdef (if define) #ifndef (if not define) và #endif (end if).

Bạn hãy xem ví dụ sau để hiểu rõ về các điều kiện này:

Ban đầu preprocessor sẽ chạy trước và khi tới dòng số 3, preprocessor sẽ kiểm tra xem identifier PRINT_JOE có được define trước đó hay không, và thật sự PRINT_JOE được define ngay dòng 1, kết quả dòng số 4 sẽ được compile.
Khi preprocessor quét tới dòng số 7, nó nhận thấy rằng PRINT_BOB chưa hề được define trước đó, cho nên dòng số 8 sẽ không được compile.

Sau khi preprocessor hoàn thành nhiệm vụ, chương trình của chúng ta chỉ còn mỗi dòng số 4.
Dựa vào ví dụ này, bạn có thể dễ dàng thấy được tác dụng của cặp #ifdef #endif này.

Bây giờ nếu bạn sửa dòng số 7 thành #ifndef PRINT_BOB thì kết quả cả 2 dòng số 4 và 8 đều được compile.

Bạn cũng lưu ý rằng, preprocessor chỉ thay thế được trong cứ pháp của C++, nó không thể thay thế text trong chính các directive của nó như ví dụ sau:

The scope of defines

Các directive chỉ có hiệu lực từ khi nó được define cho tới khi hết file chứa nó. Và chắc chắn rồi, những directive ở file này sẽ không bao giờ ảnh hướng tới file khác.

Trong bài này chỉ giới hạn về preprocessor, bạn sẽ học nhiều hơn về các directive trong các bài sau.



2 nhận xét: