在函数原型声明的中间是否允许_Pragma运算符?

Are _Pragma operators allowed in the middle of function prototype declarations?

提问人:anol 提问时间:6/9/2023 更新时间:6/9/2023 访问量:49

问:

我遇到过一些这样的代码:

void _Pragma("function") f() {
  int i = 0;
  _Pragma("loop");
  while (i < 100) {
    ...
  }
}

GCC 编译它没有问题。但是我看不出它允许 出现在这些位置的标准中的确切位置,尤其是在 和 之间。_Pragmavoidf()

我的 C11 标准 (N1570) 版本规定:

6.10.9 编译指示运算符

语义学

形式的一元运算符表达式:

_Pragma ( string-literal )

...

给出的示例的行为几乎为 ,这意味着,在一条线上。#pragma

6.5.3 节。一元运算符甚至没有提到._Pragma

我在句法规则中没有看到一元运算符被允许位于 和 之间的位置。voidf()

GCC 文档只是声称:

该标准尚不清楚操作员可以出现在哪里。_Pragma

我认为在语法上没有明确允许的任何地方都是被禁止的。

是未指定或实现定义的,以允许出现此类情况?_Pragma

C 语言-律师 杂注

评论

0赞 Ian Abbott 6/9/2023
我希望在翻译阶段 4 允许宏调用的任何地方都允许它。第 6.5.3 节没有提到它,因为该部分在翻译阶段 7 之前无关紧要。例如,我希望是相邻字符串文字的有效组合。"Hello " _Pragma("") " world!\n"
0赞 Lundin 6/9/2023
“第6.5.3节.一元运算符甚至没有提到_Pragma。它是一个宏运算符,与 和 类似。在此上下文中,运算符仅表示“具有操作数的东西”。###
0赞 Eric Postpischil 6/9/2023
第 6.10 条中的“一元运算符”(包括“预处理”)不是指第 6.5 条(表达式)的一元运算符。第 6.10 条还使用“一元运算符”作为运算符,这不属于第 6.5 条的一部分。您也不应该期望在第 6.4 至 6.9 条的语法中看到,因为这是翻译阶段 7 的语法,在第 4 阶段的预处理之后。预处理在 6.10 1 中具有自己的语法,并且运算符可以出现在预处理器标记序列中的任何位置,而不受 6.10 中的语法约束。defined_Pragma_Pragma

答:

3赞 Lundin 6/9/2023 #1

_Pragma几乎只有一个目的:允许您在预处理器宏中使用编译指示。将其用于任何其他目的没有(不应该)有意义。#define

除此之外,6.10.9 说:

生成的字符序列通过转换阶段 3 进行处理,以生成预处理标记,这些标记就像 pragma 指令中的 pp 标记一样执行。

你可以把它解释为:本来是要写在自己的一行上,因为作为一个预处理器指令,它就是这样工作的。在这种情况下,由于诸如 之类的代码是无效的,因此 也应该如此。#pragmavoid #pragma function_Pragma

引用您找到的 gcc 文档:

为了安全起见,您可能最好将其排除在 #define 以外的指令之外,并将其放在自己的行上。

此外,一般的最佳实践包括:

  • 不要仅仅为了它而编写奇怪的代码。
  • 不要仅仅为了它而使用奇怪的编译器扩展。

尽管编译器应该忽略无法识别的编译指示,这似乎是 gcc、clang 等人正在做的事情。如果您使用严格的设置进行编译,则两者都不会“毫无问题地编译它”,它们会发出警告,然后忽略它。-std=c17 -pedantic -Wall -Wextra