Thông báo! Chúng tôi đang cố gắng khôi phục lại các link download, các bạn vui lòng tải lại sau (dự kiến 10/12/2022 hoàn thành). Cảm ơn các bạn!

Lỗi macro function trong C và cách khắc phục

TuanPi | 22.12.14 | |
. . Không có nhận xét nào:

Lỗi macro là một lỗi rất thường xuyên gặp với những người mới viết C và đồng nhất hoàn toàn C với các ngôn ngữ khác. Thực tế, cách làm việc của macro trong C rất cần được để ý và nó tạo ra những lỗ hổng tai hại nếu không có kinh nghiệm.

Lỗi thay thế:

Giải thích:
Macro là một tên được sử dụng thay cho một đoạn chương trình. khi sử dụng macro (#define) trong C, nguyên tắc là C sẽ thay thế toàn bộ khai báo (phần sau tên macro) vào nơi sử dụng (nơi có lời gọi macro).
Do vậy, bất kỳ cái gì đằng sau tên macro sẽ được thay thế nguyên vẹn, bất kể ý nghĩa ra sao.
VD:
#define TONG(a,b) a+b
void main
{
    int x=5,y=10;
    printf("tong x+y=%d",TONG(x,y));
}
Khi dịch chương trình, bước đầu tiên, C sẽ thay macro TONG(x,y) bằng phần khai báo macro (sau #define TONG) một cách hoàn toàn máy móc:
 Theo thứ tự khai báo, đối số thứ nhất của macro được truyền vào số x, đối số thứ 2 truyền vào chữ y, do vậy, TONG(a,b) sẽ được thay thế thành x+y
--> printf("tong x+y=%d",x+y);
Bước tiếp theo, C sẽ dịch đoạn chương trình đã thay thế, hoàn toàn không còn dấu vết nào của macro ở bước này. Khi đó nó mới xem xét xem chương trình viết đúng hay sai.
Tương tự như vậy, một lời gọi macro TONG(a*3,b) sẽ được chuyển thành a*3+b.
Một ví dụ khác: macro chu vi:
#define CHUVI(d) 3.14*d
printf("Chu vi hinh tron = %d",CHUVI(5));
Tương tự như trên, bước 1, chương trình dịch sẽ so khớp đối số: Số 5 trong danh sách đối số sẽ được thay vào vị trí của chữ d trong CHUVI:
prìnt("Chu vi hinh tron = %d", 3.14*5);
Bước 2: Chương trình dịch sẽ dịch đoạn chương trình trên như thường.
Các bạn thử tính thử lời gọi hàm: CHUVI(a+3) sẽ cho ra kết quả bao nhiêu?
Giải đáp: Một cách hoàn toàn máy móc, C sẽ tìm tất cả các chữ d trong khai báo macro và thay bằng a+3, không cần quan tâm xem ý nghĩa logic của nó thế nào.
Và như vậy, ta sẽ có: CHUVI(a+3) = 3.14*a+3.
Rõ ràng, đây là một lời giải sai. Vì theo ta hình dung, trước tiên, phải lấy a+3 đã, rồi mới nhân kết quả với 3.14. Nhưng không, C đã thay một cách máy móc a+3 vào vị trí của d, và sau đó, theo thứ tự ưu tiên phép toán, thì phép nhân 3.14*a sẽ được thực hiện trước phép cộng 3.
Bạn thử kiểm tra lại khả năng của mình với ví dụ sau:
#define CUBE(x) x*x*x
Tính kết quả của CUBE(x+1) với x = 3?
Mong muốn đạt được là 4^3 = 64. Nhưng kết quả đạt được lại là: 3+1*3+1*3+1 = 10.

- Khắc phục:

Khắc phục lỗi hiểu sai macro function vô cùng đơn giản: Chỉ cần bạn tích cực sử dụng ngoặc là được:
#define TONG(a,b) ((a)+(b))
Bạn có thể thắc mắc tại sao cần cặp ngoặc bao phía ngoài nữa? Câu trả lời sẽ rất đơn giản nếu bạn chịu khó đọc kỹ đoạn vừa viết phía trên và tính thử giá trị của biểu thức sau:
 TONG(a,b)*5 với a = 3, b = 7?. Kết quả mong muốn là 50, kết quả thực sự đạt được nếu không có cặp ngoặc phía ngoài sẽ là 38.
Lỗi gọi hàm
(Lỗi này do Linh K42 đưa ví dụ)Xét đoạn code sau: #define max(a,b) ( (a) > (b)? (a): (b) )

void main()
{
int x[4] = {1,2,3,4};
int n = 4;
int i = 1,biggest;

biggest = x[0] ;
while (i{
biggest = max(biggest, x[i++]);
}
 Tất cả vấn đề là nằm ở cái dấu cộng cộng đã được bê nguyên vào trong macro.
#define max(a,b) ( (a) > (b)? (a): (b) )

vậy khi gọi big = max(big, x[i++]); thì nó sẽ làm như sau.

big = ( (big) > (x[i++]) ? (big) : (x[i++) )

đoạn này viết rõ ra thì nó tương đương thế này :

1 :if ( big > x[i++])
2 :{
3 : big = big;
4 :}
5 :else
6 :{
7 : big = x[i++];
8 :}

giả sử i = 1;
thì ở dòng thứ 1 thì sau khi so sánh big với x[1] xong nó sẽ tăng i lên 1, lúc này i =2. Sau đó nếu nhảy vào dòng 7 nó sẽ gán big = x[2], sau đó tăng i lên 1 vậy là i =3.
thế nên với đoạn code trên thì sau vòng thứ 1 big = x[2] mà sau vòng 2 thì big = x[4] , x[4] vốn ko được khai báo nên không xác định được giá trị.

Kết luận

Như vậy, macro function là một cái bẫy đối với những ai không hiểu nó, nhưng nó cũng là công cụ với những người có kinh nghiệm. Bạn cần hiểu rõ nó để tránh sai lầm.
Nguồn: http://bmtbd.uct.edu.vn/

Không có nhận xét nào:

Đăng nhận xét

Lên đầu trang