我正在阅读Kernighan撰写的C编程书,并且遇到了这个问题。两者之间的差异或他们各自的使用标准。我在StackOverflow上发现了一些处理此问题的帖子。我得到了答案的一部分,但理解它们有点麻烦。
正如他们在此提到的那样(putc needs stdout, vs puts):
根据Kernighan的书
putc
相当于fputc
,但putc
可以实现为宏,putc
可以多次评估其流参数。- 醇>
putc
和fputc
之间的区别在于使用putc
,您可能会冒运行本质上不安全的宏版本,因为它可能需要更多地评估其流参数不止一次。这会导致大多数人不知道并且因此不注意的并发症,因此fputc
更好用。fputc
的宏没有这个问题。
问题:
putc
可以作为宏实现,但与fputc
相同的问题是什么?
第二个陈述提到了一些并发症和安全问题。那些是什么?
putc
不止一次地评估其参数。那么与评估论证相比,它有什么优势或劣势。
答案 0 :(得分:17)
宏实现的问题是,如果任何参数有副作用,可能会多次评估这些副作用,可能会导致未定义的行为。考虑一下这个玩具示例:
#define SQUARE(x) ((x) * (x))
大多数情况下,这将按预期运行,但如果传入f()
之类的表达式,则调用函数f()
的副作用将发生两次,而不是一次,因为预处理器只是一个不知道C的文本转换器:
int f()
{
printf("f() was called\n");
return 42;
}
...
int x = SQUARE(f()); // This calls f() twice! It gets expanded to this:
// int x = (f() * f());
为了正确看待这一点,putc
函数(如果实现为宏)可以多次评估其stream
参数。因此,如果该流来自一个函数:
FILE *get_file()
{
// Potential side effects could happen here
return some_file;
}
...
putc('A', get_file());
然后,这可能导致函数get_file()
被多次调用,可能会产生不必要的副作用。
当然,解决方案是调用常规函数,例如fputc()
而不是putc()
。由于它不是一个宏,因此不会有多次评估其参数的任何潜在问题。宏有时会很危险,所以要小心使用它们。