我正在阅读Kernighan的C编程书,偶然发现putc
和fputc
,想知道两者之间有什么区别,或者何时使用另一种。我在StackOverflow上找到了一些有关该主题的帖子,但在理解它们时遇到了一些麻烦。
正如他们在这里提到的[putc needs stdout, vs puts):
根据Kernighan的书
putc
等效于fputc
,但是putc
可以实现为宏,并且putc
可以多次评估其流参数。
putc
和fputc
之间的区别在于,使用putc
可能会运行本质上不安全的宏版本,因为它可能不得不多次评估其流参数。这会导致大多数人不知道的并发症,因此不会引起注意,因此fputc
更好用。fputc
的宏没有此问题。
问题:
[putc
可以实现为宏,但是用fputc
做同样的问题是什么?
第二句话提到了一些复杂性和安全性问题。那是什么?
putc
多次评估其自变量。因此,与评估参数相比,它有什么优点或缺点。
宏实现的问题是,如果任何参数有副作用,这些副作用可能会被多次评估,可能会导致未定义的行为。考虑这个玩具示例:
#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()
。由于它不是宏,因此多次评估其参数没有任何潜在的问题。宏有时可能很危险,因此请谨慎使用。