为什么预处理器需要大括号才能有语句?

Her*_*man 2 c preprocessor inline-assembly

有这个代码:

#define GREATER(a, b, res) ( \ /* no braces here */
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); )
Run Code Online (Sandbox Code Playgroud)

错误: c.c:4:2: error: expected expression before ‘asm’ asm("cmp %1, %2\n\t" \

但是这个(只更改了大括号,其他所有内容都保留了 - 代码在其他方面是正确的):

#define GREATER(a, b, res) ({ \ /* used braces here - correct */
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); })
Run Code Online (Sandbox Code Playgroud)

将编译没有错误。但唯一改变的是添加了大括号。那么为什么需要它们呢?预处理器假定语句是什么?(如果只是意味着在大括号中。)我已经看到其他只在括号中声明的宏函数(没有花括号),那么为什么这个函数应该有呢?

Eri*_*hil 6

预处理器指令不涉及此问题。问题是:

  • asm(…)是 C 语言的 GCC 扩展。GCC 将asm, 后跟 a;视为一个语句。
  • 括号是表达式的一部分。写的时候(…),括号的内容应该是表达式。因为asm不是表达式,(asm(…);)是错误。
  • GCC 有一个扩展名为statement expressions,其中({…})具有类似于表达式的值,但可能包含语句。在里面({…}),你可以把语句放在大括号里面,GCC 会对它们求值,并使用其中最后一个表达式语句1的值作为({…})表达式的值。(in 的最后一个语句({…})应该是一个表达式语句,而不是其他类型的语句,就像一个for循环。)

因此({ asm(…); })被接受为表达式。

然而,尽管 GCC 接受了它,但它违反了 GCC 文档中的声明“复合语句中的最后一件事应该是一个表达式,后跟一个分号……”。看起来您的宏并不打算用作表达式;它把结果放入res但本身没有价值。在这种情况下,您可以通过从原始代码中删除括号来使其成为一个简单的语句:

#define GREATER(a, b, res) \
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b));
Run Code Online (Sandbox Code Playgroud)

此外,人们通常更喜欢;在此类宏中省略final ,因为这样在源代码中使用时,宏可以像语句一样编写:

GREATER(a, b, res);
Run Code Online (Sandbox Code Playgroud)

而不是对习惯于以以下结尾的语句的人来说很奇怪;:

GREATER(a, b, res)
Run Code Online (Sandbox Code Playgroud)

(虽然;在定义中,您仍然可以写GREATER(a, b, res);,但是这扩展为 have ;;,这可能会导致问题,因为由于额外的,if (foo) GREATER(a, b, res); else…将无法将其else与该相关联。)if;

脚注

1个甲语句表达式是一个表达式后跟一个;。