导致非标准行为的 #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 标准允许的,因此符合。
简单的问题:导致非标准行为的 #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 标准允许的,因此符合。