21

我正在阅读 Kernighan 的 C 编程书,偶然发现putcfputc. 我不太明白两者之间的区别,也不明白什么时候可以使用另一个。我在 StackOverflow 上找到了一些涉及该主题的帖子,但仍不清楚。

如此处所述(putc 需要标准输出,而不是 puts):

  1. 根据 Kernighan 的书putc,等效于fputcputc可以实现为宏,并且putc可以多次评估其流参数。

  2. putc和之间的区别在于fputc,使用putc,您可能会运行本质上不安全的宏版本,因为它可能必须多次评估其流参数。这会导致大多数人没有意识到的并发症,因此不会提防,因此fputc最好使用。fputc的宏没有这个问题。

问题:

  1. putc可以实现为宏,但同样的问题是什么fputc

  2. 第二个声明提到了一些并发症和安全问题。那些是什么?

  3. putc不止一次地评估它的论点。那么与评估论点相比,它有哪些优点或缺点。

4

1 回答 1

21

宏实现的问题是,如果任何参数有副作用,这些副作用可能会被多次评估,可能导致未定义的行为。考虑这个玩具示例:

#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(). 由于它不是宏,因此多次评估其参数不会有任何潜在问题。宏有时可能很危险,因此请谨慎使用。

于 2012-12-23T05:54:22.387 回答