Thông thường, ngoài việc cung cấp một khai báo chức năng, tiêu đề C tiêu chuẩn có thể cung cấp một "mặt nạ vĩ mô" để làm cho mọi thứ nhanh hơn. Ví dụ: nếu tôi bao gồm ctype.h
, tệp tiêu đề sẽ khai báoCó bất kỳ hạn chế nào đối với tiêu chuẩn C cho phép các chức năng được triển khai dưới dạng macro không?
int isdigit(int c);
Nhưng cũng có thể che dấu khai báo bằng macro. Tôi tin rằng đây là một di isdigit
vĩ mô theo tiêu chuẩn C:
#define isdigit(c) ((c) >= '0' && (c) <= '9')
Tất nhiên, macro này cũng rất nguy hiểm vì nó giới thiệu hành vi undefined nếu bạn làm điều này trong khi vĩ mô được xác định:
int c = 'A';
printf("%d\n", isdigit(c++));
Để tránh UB trong trường hợp giả định này, tôi phải bao quanh tên hàm với các số parens: (isdigit)(c++)
. Vì vậy, câu hỏi của tôi là: có bất kỳ hạn chế nào đối với loại macro mặt nạ nào mà tiêu đề chuẩn có thể xác định không? Chúng có được đảm bảo không gây ra hành vi không xác định nếu một biểu thức đối số có tác dụng phụ hay chúng được cho phép về mặt kỹ thuật có hành vi kỳ lạ như chúng ta thấy ở trên không? Các giới hạn ở đâu?
Macro được đề xuất của bạn không phù hợp. Với các ngoại lệ của việc triển khai macro (tùy chọn) của các hàm 'getc()' và 'putc()', một định nghĩa macro của hàm chuẩn có thể không đánh giá bất kỳ đối số nào của nó nhiều hơn một lần. Nếu được gọi là 'isdigit (C++)' hành vi macro của bạn, nhưng câu lệnh hoạt động đúng với các macro tuân thủ và với một hàm. –
Xin lỗi, ý bạn là gì bởi "macro chuẩn"? Trong mọi trường hợp, khi tôi nói "di động", tôi có nghĩa là "cư xử tốt trên tất cả các trình biên dịch C tuân thủ tiêu chuẩn", khi thông qua một đối số thích hợp mà không có tác dụng phụ. –
Tiêu chuẩn C quy định rằng bất kỳ chức năng chuẩn nào cũng có thể được thực hiện dưới dạng macro. Nhưng nó cũng yêu cầu bất kỳ việc thực hiện macro nào của bất kỳ hàm C chuẩn nào (trừ 'getc()' và 'putc()') chỉ phải đánh giá các đối số của nó một lần, vì macro phải hoạt động chính xác như thể nó là một hàm. Hơn nữa, mọi hàm phải được khai báo và triển khai thực hiện như một hàm để địa chỉ của nó có thể được lấy và truyền xung quanh như một con trỏ đến hàm. Đó là khá nhiều thông tin mà Paul Griffiths đã trích dẫn bạn từ tiêu chuẩn. Lưu ý rằng 'setjmp()' rõ ràng là một macro. –