微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决导致非标准行为的#pragma 是否会导致 __STDC__ 宏不被定义为 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 操作符)(强调):

sizeof 运算符不应应用于具有函数类型或不完整类型的表达式,...

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

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

解决方法

在这里我们看到 #pragma 导致非标准行为:违反了“应要求”,未生成诊断消息,调用编译器的后端,生成并成功执行 .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,该指令“使实现以实现定义的方式运行。这种行为可能会导致翻译失败或导致翻译器或生成的程序以 [其他] 不符合规范的方式运行。”因此,假设编译指示由实现记录以抑制有关 sizeof 运算符的 6.5.3.4 1 中违反约束的警告,那么 C 标准允许这样做。 6.10.6 1 中的这条规则覆盖了 6.5.3.4 1 中的约束。该行为是 C 标准允许的,所以它是符合的。

,

标准为未来的语言扩展保留 #pragma STDC ... - 所有其他编译指示都是实现定义的 (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。损坏的实现将提供大量奇怪的诊断信息。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。