Chương 14: Bộ Tiền Xử Lý Trong C

Lập trình C � 2003 Kishori Mundargi

CHƯƠNG 14:

Bộ tiền xử lý trong C

Bộ tiền xử lý là một phần của tiến trình biên dịch C, thực hiện các câu lệnh bắt đầu bằng dấu #, như đã giới thiệu ở Chương 2. Mặc dù phần lớn trình biên dịch C đã tích hợp sẵn bộ tiền xử lý, nhưng việc tiền xử lý vẫn được coi là độc lập vì nó được thực hiện trên mã nguồn trước khi biên dịch. Các câu lệnh tiền xử lý có cú pháp khác với các câu lệnh C thông thường, được sử dụng cho các mục đích sau:

  1. Bao hàm các tập tin header
  2. Biên dịch có điều kiện
  3. Định nghĩa macro

Bao hàm các tập tin header

Chỉ thị # include được sử dụng để bao hàm các tập tin hệ thống và các tập tin header do người dùng định nghĩa. Chỉ thị này yêu cầu bộ tiền xử lý C quét tập tin được bao hàm trước khi tiến hành xử lý tập tin hiện tại. Sau đây là hai cách để bao hàm một tập tin:

#include <systemfile.h> #include "userfile.c"

Câu lệnh đầu tiên tìm một tập tin theo tên systemfile trong các thư mục hệ thống chuẩn (standard system directories). Một lập trình viên có thể chỉ rõ các thư mục để tìm kiếm các tập tin được bao hàm bằng cách sử dụng tùy chọn -I của trình biên dịch. Bạn cũng có thể làm cho trình biên dịch bỏ qua việc tìm kiếm trong các thư mục hệ thống bằng tùy chọn -noinstdinc

Câu lệnh thứ hai được sử dụng để bao hàm các tập tin header định nghĩa bởi lập trình viên trong thư mục hiện tại. Trước tiên bộ tiền xử lý sẽ tìm kiếm tập tin trong thư mục chứa chương trình, sau đó mới tìm kiếm trong các thư mục hệ thống.

Chỉ thị tiền xử lý này phải được đặt trước bất cứ dòng nào có sử dụng tập tin header được bao hàm. Bộ tiền xử lý tìm kiếm tập tin này trong hệ thống và chèn nội dung của tập tin này vào chương trình tại vị trí dòng lệnh #include.

Biên dịch có điều kiện

Có những trường hợp bạn cần phải phát triển một gói phần mềm có thể chạy trên nhiều nền tảng hệ điều hành khác nhau mà không gặp lỗi. Những ứng dụng như thể được gọi là ứng dụng xách tay (portable). Rất dễ gặp một phần nào đó của chương trình chỉ hoạt động được trên một hệ điều hành. Trong những trường hợp mà bạn phải bật hoặc tắt nhiều câu lệnh trong chương trình như vậy, chúng ta có thể dùng biên dịch có điều kiện. Chức năng được ứng dụng khi chạy chương trình để chuyển đổi giữa chế độ debug (sửa lỗi) và chế độ release (phát hành).

Một vài câu lệnh thông dụng được sử dụng trong biên dịch có điều kiện là #define, #ifdef, #ifndef, #endif, #else, #if, #elif . Xem xét ví dụ sau:

#ifdef _WIN32_ #define INT_SIZE 32 #else #define INT_SIZE 16 #endif

Các câu lệnh trên kiểm tra xem đây có phải hệ điều hành Windows NT hay không bằng cách kiểm tra sự tồn tại của hằng _WIN32_, nếu tồn tại thì hằng INT_SIZE được gán giá trị là 32 bits, ngược lại với các hệ điều hành khác thì hằng này được gán giá trị 16 bits.

Chỉ thị #error hiển thị một thông báo lỗi dựa theo định nghĩa theo sau chữ error. Chỉ thị này có thể dừng quá trình biên dịch và cảnh báo lập trình viên về một lỗi xuất hiện trong quá trình biên dịch chương trình.

#ifdef DEBUG #ifdef RELEASE #error DEBUG and RELEASE both are defined #endif #endif

Chỉ thị #warning tương tự như #error nhưng chỉ đưa ra cảnh báo mà không dừng quá trình biên dịch.

Chỉ thị #pragma không có định nghĩa rõ ràng, nó phụ thuộc vào hãng sản xuất trình biên dịch. Trong bộ tiền xử lý C GNU, #pragma chỉ được sử dụng với #pragma once. Chỉ thị này có ý nghĩa là một tập tin header chỉ được bao hàm một lần duy nhất. Tuy nhiên hiện này chỉ thị này không còn được sử dụng.

Macros

Các macro được sử dụng để gán tên cho các hằng, khi chúng được sử dụng nhiều lần trong một chương trình. Sử dụng macro giúp chương trình dễ đọc hơn và linh động hơn.

#define

Như ta đã thấy, macro #define là macro được sử dụng phổ biến nhất. Mục đích của nó là định nghĩa một cái tên cho một giá trị hay hằng nào đó.

#define TRUE 1 #define MAX 100

Khi những cái tên trên xuất hiện trong chương trình thì chúng sẽ được thay thế bằng giá trị tương ứng bởi bộ tiền xử lý.

Chú ý: Các câu lệnh #define có thể xuất hiện tại bất cứ vị trí nào trong chương trình nhưng thông thường sẽ được tập hợp ở đầu chương trình.

Macro #define giúp chương trình dễ mở rộng hơn, giá trị của một hằng có thể thay đổi tại duy nhất một vị trí nhưng có tác động tới mọi vị trí mà hằng đó được sử dụng.

Các macro #define có thể chứa các biểu thức (expressions) hoặc các định danh (identifiers) được định nghĩa từ một macro trước đó. Các macro có thể chứa tham số như các hàm. Xét ví dụ sau:

#define SQUARE(x) x*x #define HEIGHT 25 #define WIDTH 30 #define AREA (HEIGHT * WIDTH)

Bạn cũng có thể đặt một hàm vào trong macro bằng cách sử dụng ký tự nối tiếp dòng \, và đóng các câu lệnh trong cặp ngoặc nhọn. Nếu bạn quên dấu ngoặc thì chỉ có câu lệnh đầu tiên được đưa vào macro.

Định nghĩa của macro có thể chứa các tham số, giống như hàm. Các tham số được đóng trong ngoặc tròn. Dấu mở ngoặc phải viết liền ngay sau tên macro. Các macro được sử dụng thay cho hàm để giảm bớt thời gian trễ liên quan đến việc gọi hàm. Tuy nhiên quá nhiều macro sẽ làm tăng kích thước của chương trình sau khi biên dịch. Vậy các macro nên được dùng trong trường hợp một hàm được gọi nhiều lần trong một chương trình. Bên cạnh đó, khi ta sử dụng macro, trình biên dịch sẽ chỉ kiểm tra số lượng tham số chứ không kiểm tra kiểu dữ liệu, do vậy có thể tồn tại những lỗi tiềm tàng. Điểm quan trọng nhất cần lưu ý là ta chỉ có thể có con trỏ hàm chứ không có con trỏ macro.

Kiểm tra

Câu hỏi luyện tập

Bài tập lớn

Sửa bài tập lớn

[Ý kiến của bạn rất quan trọng với chúng tôi. Nếu bạn có bình luận, góp ý hay thắc mắc gì liên quan tới chương này xin vui lòng gửi tới [email protected] .]

Previous: Thao tác với tập tin

Next: Những vấn đề khác

Từ khóa » Tiền Xử Lý Là Gì