正如引用的段落中所解释的,有一种简单的方法可以防止标准库头中定义的类似函数的宏的宏扩展:使用后面不跟(
的标识符来引入参数列表,例如:在标识符两边使用括号.
例如:
#include <stdio.h>
// copy stdin to stdout bypassing macro expansion
int main(void) {
int c;
while ((c = (getc)(stdin)) != EOF) {
(putc)(c, stdout);
}
return 0;
}
或者,您可以在使用代码中的函数之前取消定义宏.这将确保预处理器保持getc
和putc
标识符不变,并且编译器将使用它们在<stdio.h>
中的函数声明.
#include <stdio.h>
// copy stdin to stdout preventing macro expansion
#undef getc
#undef putc
#undef fgetc
#undef fputc
int main(void) {
int c;
while ((c = getc(stdin)) != EOF) {
putc(c, stdout);
}
return 0;
}
一种更简单的方法是使用函数fgetc
和fputc
,即使它们也可以被实现为用于性能的宏,它们也被保证表现为函数:
#include <stdio.h>
// copy stdin to stdout using the functions
int main(void) {
int c;
while ((c = fgetc(stdin)) != EOF) {
fputc(c, stdout);
}
return 0;
}
在任何情况下,都不要try 修改标准标头,因为在现代系统上,标头应该是只读的.
另请注意,在<stdio.h>
中定义的标准函数的宏版本只允许对流参数(对应于FILE *
的参数)进行多次求值,而不允许对字符参数进行多重求值.不能保证这一点的古代系统不符合任何C标准,并且完全过时.例如,参见ISO C90文件中putc
函数的规范:
7.9.7.8 100 function
Synopsis个
#include <stdio.h>
int putc(int c, FILE *stream);
Description个
putc
函数等同于fputc
,不同之处在于,如果它作为宏实现,它可能会多次计算stream
,因此参数永远不应该是有副作用的表达式.
最后一句话是:不要担心这一点,毫不犹豫地使用宏或函数,但不要将有副作用的表达式作为流参数传递,这无论如何都会非常麻烦和容易出错.