导致非标准行为的 #pragma 是否会导致 __STDC__ 宏未定义为 1?

Shall a #pragma leading to nonstandard behavior cause __STDC__ macro not to be defined to 1?

简单的问题:导致非标准行为的 #pragma 是否会导致 __STDC__ 宏不被定义为 1? (C标准有明确规定吗?如果有,那么在哪一节?如果没有,那为什么?)提问原因:见下文

示例代码(t28.c):

#pragma warning( disable : 34 )
typedef int T[];

int main()
{
    int rc = sizeof(T);
#if __STDC__ == 1
    rc = 0;
#else
    rc = 1;
#endif
    return rc;
}

调用:cl t28.c /std:c11 /Za && t28 ; echo $?

预期结果:1

实际结果:0

编译器版本:

cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.28.29913 for x64

注意:C11(6.5.3.4 sizeof 和 _Alignof 运算符)(强调已添加):

The sizeof operator shall not be applied to an expression that has function type or an incomplete type, ...

这里我们看到 #pragma 导致非标准行为:违反“应要求”,不生成诊断消息,调用编译器后端,生成 .exe 并成功执行。但是,这种非标准行为不会导致 __STDC__ 宏未定义为 1.

提问原因:测试。一项类似于 t28.c 的测试失败了,因为它需要 return 代码 1__STDC__ 未定义为 1)。系统的哪一部分包含错误:测试或编译器(或两者)?

标准保留 #pragma STDC ... 用于未来的语言扩展 - 所有其他 pragma 都是 implementation-defined (C17 6.10.6)。

不要混淆哪个 __STDC__,它设置为 1 以将编译器标记为 符合标准的实现 (C17 6.10.8.1)。

这个术语在C17 4/5和4/6中依次定义:

A strictly conforming program shall use only those features of the language and library specified in this International Standard

The two forms of conforming implementation are hosted and freestanding. A conforming hosted implementation shall accept any strictly conforming program. /--/ A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program.

这并不意味着只要应用程序是严格符合标准的程序就会设置 __STDC__,而是如果编译器及其当前选项是符合标准的,则它固定设置为 1 或 0实施。


例如,编译器可能包含 POSIX 库,这些库在标准 headers 中转储 non-standard 标识符。如果这些标识符通过名称空间冲突影响严格符合程序的行为,则不应将 __STDC__ 设置为 1。non-conforming 示例是当我使用 gcc -std=gnu17:[=22 编译时=]

#include <string.h>
#if __STDC__== 1
int strdup; // file scope declaration
#endif

我收到“错误:'strdup' 重新声明为不同类型的符号”。这是 non-compliant 行为。可以说,当 -std=gnu17 下的 运行 时 __STDC__ 设置为 1 是一个错误,因为这不是一个符合规范的实现。

如果我切换到 -std=c17,它会按应有的方式干净地编译 - 然后 gcc 是一个符合标准的实现。


否则,正如我们从上面引用的部分中可以看出的,符合标准的实现仍然可以有扩展。

在您的特定情况下,您选择禁用诊断作为 non-standard 扩展。这是程序员发出的调用,而不是 编译器 non-compliant 的调用。它只是使 应用程序 non-conforming,但编译器可能仍然能够编译一个严格符合要求的程序,即使你没有给它一个。

如果您想演示 non-conforming VS 是怎样的,那么这是一个更好的程序:

#include <stdio.h>

int main()
{
  int x = __STDC__;
  #if defined(__STDC_NO_VLA__) && __STDC_NO_VLA__==1
    int arr [2];
  #else
    int arr [x];
  #endif

  printf("%zu", sizeof arr / sizeof *arr);
}

在支持 VLA 的情况下,符合标准的实现应该打印 1,否则打印 2。损坏的实现将给出大量奇怪的诊断信息。

Here we see that #pragma leads to nonstandard behavior: "shall requirement" is violated, diagnostic message is not generated, compiler's backend is called, .exe is produced and successfully executed. However, this nonstandard behavior does not cause __STDC__ macro not to be defined to 1.

首先,__STDC__ 宏并不是一种报告“编译期间发生的导致非标准行为的事情”的机制。它不是那样动态(变化)的。 __STDC__ 的愿望只是让它报告 C 实现符合要求。 (这只是一种愿望,因为 C 标准可以要求符合的实现将 __STDC__ 定义为 1,但无法控制不符合的实现将其定义为 1。)如果实现符合,__STDC__ 将无论在任何特定编译中发生什么,始终为 1。

(请注意,一个编译器可能包含由各种命令行开关选择的多个 C 实现,例如为各种类型请求不同的大小,启用或禁用符合标准的扩展,启用 C 标准的不符合标准的变体,等等on。某些开关组合可能会导致 __STDC__ 被定义为 1 而其他则不会。)

其次,#pragma warning( disable : 34 )不会导致不规范的行为。根据 C 2018 6.10.6 1,该指令“使实现以实现定义的方式运行。该行为可能会导致翻译失败或导致翻译器或生成的程序以 [otherwise] 不合规的方式运行。”因此,假设实现记录了编译指示以抑制关于违反 6.5.3.4 1 中关于 sizeof 运算符的约束的警告,那么这是 C 标准允许的。 6.10.6 1 中的这条规则覆盖了 6.5.3.4 1 中的约束。该行为是 C 标准允许的,因此符合。