【问题标题】:What does #define FOO(x,c) (void)( { c = ( x ) ; }) do?#define FOO(x,c) (void)( { c = ( x ) ; }) 是做什么的?
【发布时间】:2019-08-18 08:33:16
【问题描述】:

我正在查看 Linux 源代码,以下行看起来很奇怪。 (void) ( { .... ;} ) 构造可能有什么用?

#define GETCH(queue,c) \
        (void)({c=(queue).buf[(queue).tail];INC((queue).tail);})

这是我的想法:

1. (void) 抑制一些编译器警告。

2. {....} 将两个语句组合在一起,以便它可以正确使用 while(...) GETCH(q,c);

还有其他原因吗?有没有可用的文档?

来源:Linux-0.01 Source - GitHub

【问题讨论】:

  • 神奇的是外括号:gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs "StatementExpressions" 是对 C 标准的扩展。
  • 你是 100% 正确的
  • @AnttiHaapala:您不应该将其作为that question 的副本关闭。语句表达式扩展只回答了这个问题的一部分。显然,没有理由在这个宏中使用语句表达式:它只用大括号就可以执行相同的操作。宏形成一个语法实体,当它后跟分号时,形成一个语句,因此可以用来代替if (X) S; else T; 中的S,单独使用大括号则不成立。
  • 那么你搜索正确的副本
  • @AnttiHaapala:或者我们可以回答这个问题。正确。

标签: c linux-kernel


【解决方案1】:

在这个宏中,作者使用了一个 GCC“语句表达式”扩展来做两件事:

  • 提供两个语句,一个赋值 (c=…) 和一个 INC 宏的使用,我认为它会对其参数进行一些增量。
  • 将这两条语句打包,以便在后跟分号时形成一条语句。

后者允许在这样的地方使用这个宏的原因:

if (some expression)
    GETCH(queue,c);
else
    some other statement;

例如,假设宏是使用普通大括号编写的,如下所示:

#define GETCH(queue,c) \
    {c=(queue).buf[(queue).tail];INC((queue).tail);}

然后它将提供两个语句并将它们分组,因此我们可以在简单的代码中使用它们,如下所示:

statement X;
GETCH(queue,c);
statement Y;

但是,请考虑if 语句中的后果。我们会有

if (some expression)
    {c=(queue).buf[(queue).tail];INC((queue).tail);};
else
    some other statement;

有一个问题:{ … };两个语句。 ; 是一个空表达式语句。在ifelse 之间不能有两条语句。当编译器在两个语句之后看到else 时,会发出警告。但是GETCH 的作者希望您能够使用后跟分号的宏作为一个语句——他们希望它“看起来像”一个函数调用,您可以在其后跟一个分号。

所以,作者做了其他事情。他们使用了 GCC 的一个特性,称为语句表达式。它不是 C 标准的一部分;它是 GCC 定义的扩展。使用 GCC 时,以表达式语句结尾并括在括号 (({ … })) 中的复合语句(大括号中的语句,{ … })就像一个表达式,其值是大括号内的最后一个表达式语句。例如({ if (x) z=x*x; else z=5; 3*z; }) 的作用类似于一个值为3*z 的表达式,此外还执行大括号内的语句。因为这是一个单一的表达式,你可以在它后面加上一个分号来形成一个单一的语句。

所以,如果作者写了:

#define GETCH(queue,c) \
    ({c=(queue).buf[(queue).tail];INC((queue).tail);})

那么我们可以这样写:

if (some expression)
    GETCH(queue,c);
else
    some other statement;

作者还加入了(void)的演员表:

#define GETCH(queue,c) \
    (void)({c=(queue).buf[(queue).tail];INC((queue).tail);})

我希望这样做的目的是防止表达式语句被用作表达式——作者只希望它在语法中作为单个语句起作用,而不希望使用它的结果值。

也就是说,我不明白为什么作者为此选择使用 GCC 扩展。在标准 C 中有一种众所周知的方法:

#define GETCH(queue,c) \
    do {c=(queue).buf[(queue).tail];INC((queue).tail);} while (0)

do里面的语句只会执行一次(因为先执行,然后检查while表达式,结果为假,所以不会出现循环),后面跟上一个分号形式一条语句。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 2020-10-02
    • 2011-05-13
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    相关资源
    最近更新 更多