【问题标题】:fputc vs putc in CC中的fputc与putc
【发布时间】:2012-12-10 03:23:54
【问题描述】:

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

这里提到(putc needs stdout, vs puts):

  1. 根据 Kernighan 的书,putc 等同于 fputc,但 putc 可以实现为宏,putc 可以多次评估其流参数。

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

问题:

  1. putc 可以实现为宏,但与fputc 相同有什么问题?

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

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

【问题讨论】:

  • 关于性能,红色here: "fputc子程序和putc宏一样,但是fputc是一个真正的子程序而不是宏。它比putc运行的慢,但占用的时间更少每次调用的空间。” -

标签: c stdio


【解决方案1】:

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

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

【讨论】:

  • 谢谢!这几乎回答了我所有的疑问。现在有一个与您的帖子相关的特定查询。有人可以解释一下这些“副作用”在更广泛和一般意义上是什么意思吗?谢谢
  • 请注意,putc 作为宏只允许多次评估文件流参数;不允许多次评估字符参数。还要注意,必须有一个实际的函数putc,并且您可以将它与#undef putc(putc)(c, fp) 一起使用。
  • 考虑:FILE *files[3]; ...open 3 files...; FILE **fpp; while ((c = *str++) != '\0') { fpp = files; for (int i = 0; i < 3; i++) putc(c, *fpp++);。如果putc() 是一个多次评估其流指针的宏,那么*fpp++ 可能会被多次评估,从而导致混乱。如果putc() 是一个函数,或者如果您调用了fputc(),则保证*fpp++ 只被评估一次。这在很大程度上是一个人为的例子,但也是半合理的。
  • @JimBalter:不,你的说法毫无根据和错误——这样的要求。 C99 §7.1.4/1 说“在头文件中声明的任何函数都可以另外实现为头文件中定义的类似函数的宏 [...]”,而 §7.19.7.8/2 说“putc 函数等效于fputc,除了如果它被实现为宏,它可能会多次评估stream,因此该参数绝不应该是具有副作用的表达式。”它保证是一个函数,但它可能是一个宏。
  • 我已经同意了,而且我删除了我原来的评论,那你为什么还要喋喋不休?
猜你喜欢
  • 1970-01-01
  • 2017-04-04
  • 2011-09-27
  • 2017-12-24
  • 2011-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多