fputc vs putc in C

Viv*_*Rai 14 c stdio

我正在阅读Kernighan的C编程书,并且遇到了这个问题.两者之间的差异或他们各自的使用标准.我在StackOverflow上发现了一些处理此问题的帖子.我得到了答案的一部分,但理解它们有点麻烦.

正如他们在这里提到的(putc需要stdout,vs puts):

  1. 根据Kernighan的书,putc它相当于fputcputc可以作为宏实现,并且putc可以不止一次地评估它的流参数.

  2. putc和之间的区别fputc是,通过使用putc,您冒着运行本质上不安全的宏版本的风险,因为它可能不得不多次评估其流参数.这导致大多数人不知道并且因此不注意的并发症,因此fputc更好地使用.fputc的宏没有这个问题.

问题:

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

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

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

Ada*_*eld 18

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

#define SQUARE(x) ((x) * (x))
Run Code Online (Sandbox Code Playgroud)

大多数情况下,这将按预期运行,但如果你传入一个表达式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());
Run Code Online (Sandbox Code Playgroud)

为了正确看待这个问题putc,如果实现为宏,该stream函数可以多次评估其参数.所以,如果该流来自一个函数:

FILE *get_file()
{
    // Potential side effects could happen here
    return some_file;
}
...
putc('A', get_file());
Run Code Online (Sandbox Code Playgroud)

然后,这可能会导致函数get_file()被多次调用,可能会产生不必要的副作用.

当然,解决方案是只调用常规函数fputc()而不是putc().由于它不是一个宏,因此不会有多次评估其参数的任何潜在问题.有时候宏会很危险,所以要小心使用它们.