导致非标准行为的 #pragma 是不是会导致 __STDC__ 宏未定义为 1?
Posted
技术标签:
【中文标题】导致非标准行为的 #pragma 是不是会导致 __STDC__ 宏未定义为 1?【英文标题】:Shall a #pragma leading to nonstandard behavior cause __STDC__ macro not to be defined to 1?导致非标准行为的 #pragma 是否会导致 __STDC__ 宏未定义为 1? 【发布时间】:2021-07-12 04:31:31 【问题描述】:简单问题:导致非标准行为的#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 运算符)(已添加重点):
sizeof 运算符不应应用于具有函数类型或不完整类型的表达式,...
这里我们看到#pragma
导致非标准行为:违反了“shall 要求”,未生成诊断消息,调用编译器的后端,生成并成功执行了.exe
。但是,这种非标准行为不会导致 __STDC__
宏未定义为 1
。
问题的原因:测试。一项类似于t28.c
的测试失败,因为它需要返回码1
(__STDC__
未定义为1
)。系统的哪个部分包含错误:测试或编译器(或两者)?
【问题讨论】:
(a)#pragma
后面没有STDC
导致实现以实现定义的方式运行。这可能包括更改__STDC__
的行为,但在这种情况下可能不会。但进一步回答需要说明此编译指示的实施文档。 (b) 在这个 C 实现中,__STDC__
通常用什么替换(即没有#pragma
并且没有代码导致编译警告或错误)?
@thebusybee:这与__STDC__
的问题有什么关系?
为什么你的问题会问到__STDC__
没有被定义为 1 而结果表明它是 1?你的帖子说程序的实际结果是0,这意味着#if
的“then”部分被使用了,这意味着__STDC__ == 1
是真的。
“一致的实现”是实现的属性,而不是您的代码。杂注或其他任何内容的存在不会改变实现的一致性。
@pmor:编译指示和命令行开关是不同的东西。为了评估一致性,每个单独的开关组合与编译器都被认为是不同的 C 实现。正在编译的源代码中存在编译指示不会使编译器成为不同的 C 实现。
【参考方案1】:
这里我们看到
#pragma
导致非标准行为:违反了“shall 要求”,没有生成诊断消息,调用编译器的后端,生成并成功执行了.exe
。但是,这种非标准行为不会导致__STDC__
宏未定义为 1。
首先,__STDC__
宏并非旨在成为报告“在此编译期间发生的导致非标准行为的某些事情”的机制。它不是以那种方式动态(变化)的。对__STDC__
的渴望只是为了报告C 实现符合要求。 (这只是一个愿望,因为 C 标准可以要求符合标准的实现将 __STDC__
定义为 1,但无法控制不符合标准的实现将其定义为。)如果实现符合标准,__STDC__
将始终为 1任何特定编译中发生的事情。
(请注意,一个编译器可能包含由各种命令行开关选择的多个 C 实现,例如为各种类型请求不同的大小、启用或禁用符合标准的扩展、启用来自 C 标准的不符合标准的变体,等等。某些开关组合可能会导致 __STDC__
被定义为 1,而另一些则不会。)
其次,#pragma warning( disable : 34 )
不会导致非标准行为。根据 C 2018 6.10.6 1,该指令“导致实现以实现定义的方式运行。该行为可能会导致翻译失败或导致翻译器或生成的程序以 [anotherly] 不符合的方式运行。”因此,假设实现记录了 pragma 以抑制有关违反 6.5.3.4 1 中关于 sizeof
运算符的约束的警告,那么这是 C 标准允许的。 6.10.6 1 中的这条规则覆盖了 6.5.3.4 1 中的约束。该行为是 C 标准允许的,因此符合要求。
【讨论】:
【参考方案2】:标准保留 #pragma STDC ...
用于未来的语言扩展 - 所有其他 pragma 都是实现定义的 (C17 6.10.6)。
不要混淆哪个__STDC__
,它设置为1 以将编译器标记为符合标准的实现 (C17 6.10.8.1)。
该术语依次在 C17 4/5 和 4/6 中定义:
严格遵守的程序应仅使用本国际标准中指定的语言和库的那些特性
两种形式的一致的实现是托管的和独立的。一个符合的 托管实施应接受任何严格遵守的程序。 //-/ 一个符合标准的实现可以有扩展(包括额外的库函数),只要它们不改变任何严格符合标准的程序的行为。
这并不意味着只要应用程序是严格符合标准的程序就设置__STDC__
,而是如果编译器及其当前选项是符合标准的实现,则将其设置为固定为 1 或 0。
例如,编译器可能包含在标准头文件中转储非标准标识符的 POSIX 库。如果这些标识符通过命名空间冲突影响严格符合程序的行为,则不应将 __STDC__
设置为 1。一个不符合的示例是当我使用 gcc -std=gnu17
编译它时:
#include <string.h>
#if __STDC__== 1
int strdup; // file scope declaration
#endif
我收到“错误:'strdup' 被重新声明为不同类型的符号”。这是不合规的行为。可以说,__STDC__
在-std=gnu17
下运行时设置为 1 是一个错误,因为这不是一个符合要求的实现。
如果我切换到-std=c17
,它会按照应有的方式干净地编译 - 然后 gcc 是一个符合要求的实现。
否则,正如我们从上面引用的部分可以看出,符合标准的实现仍然可以有扩展。
在您的具体情况下,您选择禁用诊断作为非标准扩展。这是程序员的调用,而不是使编译器不兼容的东西。它只是使 应用程序 不符合标准,但编译器仍然可以编译一个严格符合标准的程序,即使你没有提供它。
如果您想展示 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。损坏的实现将给出大量奇怪的诊断信息。
【讨论】:
感谢您的详细回答。仅供参考:cl t318.c /std:c11 /Za && t318.exe
不会产生错误、警告并打印 2
。版本:19.28.29913。
仍然不明白:为什么#pragma
导致非标准行为不会导致实现不符合?在示例中,符合t28.c
的实现需要产生诊断消息,但是,由于#pragma
不会产生这样的诊断消息,但是,__STDC__
仍然是 1。以上是关于导致非标准行为的 #pragma 是不是会导致 __STDC__ 宏未定义为 1?的主要内容,如果未能解决你的问题,请参考以下文章
_packed / #pragma pack() 字节对齐问题