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

Why are braces needed for preprocessor in order to have statement?

有这个代码:

#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)); )

错误: 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)); })

编译无误。 但唯一的变化是添加了大括号。那么为什么需要它们呢?预处理器假设什么是语句? (If only means being in braces.)我看到其他宏函数只在括号中声明(没有大括号),那么为什么这个应该有?

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

  • asm(…) 是 GCC 对 C 语言的扩展。 GCC 将 asm 后跟 ; 视为 语句 .
  • 括号是表达式的一部分。当你写(…)时,括号中的内容应该是一个表达式。由于 asm 不是表达式,因此 (asm(…);) 是一个错误。
  • GCC 有一个名为 statement expressions 的扩展,其中 ({…}) 的值类似于表达式,但可能包含语句。在 ({…}) 内,您可以将语句放在大括号内,GCC 将计算它们并使用最后一个 表达式语句 1 的值在它们中作为 ({…}) 表达式的值。 (({…}) 中的最后一条语句应该是表达式语句,而不是某种其他类型的语句,就像 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));

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

GREATER(a, b, res);

而不是让那些习惯于以 ; 结尾的语句的人看起来很奇怪:

GREATER(a, b, res)

(虽然在定义中使用了;,你仍然可以写成GREATER(a, b, res);,但是这会扩展为;;,这会导致问题,因为if (foo) GREATER(a, b, res); else… 将无法将 elseif 关联,因为额外的 ;。)

脚注

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

如果你编译发布的代码片段,你确实会得到一个错误,因为宏被定义为 #define GREATER(a, b, res) ( \ /* no braces here */ 并且片段的其余部分被解析为单独的源代码行,因为 \ 不是 #define 行的最后一个字符。

你应该小心评论的位置。

更一般地说,asm 语句不是表达式,而是语句,因此不能出现在括号之间。如果你想在表达式上下文中使用宏,你必须通过用 ({}) 将语句转换为 statement-expression,一个 gcc扩展名,就像 asm 语句一样。对于要在表达式上下文中使用的结果,您必须指定值:

/* used braces here - correct */
#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)); res; })

最后,计算 ab 中较大者的汇编代码效率低下。编译器可能会从 res = greater(a, b) 生成更好的代码,其中 greater 定义为 static inline int greater(int a, int b) { return a > b ? a : b; }