Lập trình C - C Macro
Khái niệm macro
Macro - một cái tên nghe khá hổ báo, được dùng để chỉ những hàm được viết ở phần Preprocessor, thay vì đặt nó vào trong phần thực thi của file nguồn. Ngoài ra khi nói đến macro có 1 nghĩa khác nữa, ám chỉ đến tất cả những phần định nghĩa được viết trong phần Preprocessor. Trong bài viết này thì mình dùng macro với cách dùng đầu tiên. Ví dụ đơn giản về macro như sau
#define PRINT_HELLO_WORLD printf("Hello world")
Macro trên không tham số, chỉ một dòng duy nhất, và compiler sẽ đơn giản là thay thế bất cứ chỗ nào có PRINT_HELLO_WORLD
ở trong source code bằng câu lệnh print("Hello world)
. Đây là để làm ví dụ thôi chứ mình không có khuyến khích ai thực hành đoạn code trên cả.
Sau khi không gặp bất cứ vấn đề gì với đồng chí PRINT_HELLO_WORLD ở trên. Chúng ta xét tiếp 1 ví dụ về macro có tham số.
#define ADD_TWO_NUMBER(x, y) ((x) + (y))
Và dùng nó ở đoạn code dưới đây
int z_1 = ADD_TWO_NUMBER(10, 3);
float z_2 = ADD_TWO_NUMBER(10.1, 3.7);
Macro trên sẽ khớp với bất cứ đoạn code nào có dạng ADD_TWO_NUMBER(param_1, param_2)
và thay nó bằng param_1 + param_2
và nó không (thể) biết param_1
và param_2
là gì, có thể là biểu thức có thể là biến, có thể là string
mà cũng có thể là int
. Đó cũng chính là điểm mạnh và cũng là điểm yếu của macro, bạn có thể dùng hàm trên để cộng 2 biến kiểu int
hay float
như trên. Tuy nhiên nó cũng tiềm tàng nhiều mối nguy hiểm mà mình sẽ đề cập ở 1 bài khác.
Các thao tác với macro
Toán tử ##
Cái này làm mình gặp chút khó khăn trong lúc viết vì nó xung đột với kí pháp của markdown. Toán tử ##
có tác dụng nối 2 token lại với nhau (mình gặp rắc rối với việt hoá từ token này, hy vọng các bạn không quá khó hiểu), tương tự như các bạn nối string
thôi, không có gì phức tạp cả.
Ví dụ:
#define float_type fl##oat
...
float_type a = 10.0;
Về ứng dụng thực tế của ##
thì thường được dùng trong để đặt tên cho biến, hàm hoặc class. Ví dụ như bạn muốn khai báo 3 biến là normal_bike_price
, premium_bike_price
, low_bike_price
, thì thay vì khai báo thủ công, bạn có thể dùng macro như sau.
#define DECLARE_VARIABLES(type, name) type normal_##name, premium_##name, low_##name
...
DECLARE_VARIABLES(int, bike_price);
Câu lệnh trên sẽ được chuyển thành đoạn code sau khi chạy chương trình:
int normal_bike_price, premium_bike_price, low_bike_price;
Macro nhiều dòng (multi line macro)
Để khai báo một function (tầm cỡ vài chục câu lệnh) thì ý tưởng viết tất cả chúng vào tất cả 1 dòng là không sáng sủa tí nào. Và để xuống dòng trong macro thì bạn sẽ dùng ký tự \
. Như trong ví dụ sau đây chúng ta sẽ sửa lại đoạn code trên 1 chút để nó thành 1 macro có nhiều dòng nhằm cho thấy tác dụng của kí tự \
.
#define DECLARE_VARIABLES(type, name) type normal_##name;\
type premium_##name;\
type low##_name;
Toán tử #
#
dùng để chuyển 1 token nào đó thành chuỗi. Ví dụ như sau.
#define print_variable_name_and_value(x) printf(#x " value is: %d", x)
...
int x = 10;
print_variable_name_and_value(x);
câu lệnh print_variable_name_and_value(x);
sẽ được chuyển thành printf("x" " value is: %d", x);
và kết quả câu lệnh trên sẽ là
x value is: 10
Macro tiền định nghĩa trong C++
C++ cung cấp một số macro được định nghĩa trước như liệt kê dưới đây:
Macro | Mô tả |
---|---|
__LINE__ | Chứa số dòng hiện tại của chương trình khi nó đang được biên dịch |
__FILE__ | Chứa tên file hiện tại của chương trình khi nó đang được biên dịch |
__DATE__ | Chứa một chuỗi month/day/year là ngày source code được biên dịch |
__TIME__ | Chứa một chuỗi hour:minute:second là thời gian chương trình được biên dịch |
Dưới đây là ví dụ cho tất cả macro ở trên trong C:
#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("STDC :%d\n", __STDC__ );
return 0;
}
Kết quả:
File :simple.c
Date :Dec 6 2015
Time :12:28:46
Line :6
STDC :1