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

标签 c inline-assembly preprocessor

有这段代码:

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

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

最佳答案

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

  • 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 语句表达式 是一个后跟 ; 的表达式。

关于c - 为什么预处理器需要大括号才能有语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60248754/

相关文章:

c - 有什么简单的方法可以提高这个自旋锁函数的性能吗?

c - 如何在 Visual C++ 6.0 中编写以下内联汇编代码?

多语句函数的 C 预处理器语法?

c - openGL 2D像素旋转

谁能告诉我如何在sunsolaris系统中运行udp客户端服务器程序?

xcode - 为什么内联汇编不能与 Xcode 中的 ARC 一起使用?

preprocessor - 测试空宏定义

swift - 在 Swift 中创建一个 "forCount"控制结构

c - 初学者加密程序

c++ - LLVM Pass 在特定地址分配内存