【问题标题】:Does C have One Definition Rule like C++?C 是否有像 C++ 一样的定义规则?
【发布时间】:2016-01-25 06:25:31
【问题描述】:

最近发现有一些情况是绝对违反了C++的ODR,但是在C编译器中编译是OK的。

例如,这个奇怪的场景(和我一起):

来源 1

int var_global=-3;

来源 2

#include <stdio.h>
#include <conio.h>

unsigned int var_global;

int main() {    

 printf("%d \n",var_global);
 getch();
 return 0;

}

我的打印结果是-3(即使在Source 2var_globalunsigned)并且@的重新定义没有错误987654328@.

我知道 C 与 C++ 有不同的规则,但我不认为它有那么大的不同。

我有谷歌并阅读了很多结果,但没有 C++ 的官方结果like this

所以问题是:

C 有没有像 C++ 一样的定义规则?

和:

官方叫什么?

我需要它与 C++ 的规则进行比较,以便我可以更深入地理解这两种语言。

p/s:我使用 Visual Studio 2010 编译上述代码。

【问题讨论】:

  • "绝对会违反 C++ 的 ODR 但会在 C 编译器中编译正常"。你试过用 C++ 编译器编译它吗?任何实际的错误信息?
  • 请不要在回答完问题后更改问题。这会使回答者看起来像傻瓜。 :) 您可以随时edit 提出问题并使用 cmets 要求更清楚。
  • 对不起,我提到了我编辑的内容,但不知道为什么它不显示。让我重新编辑一下。
  • 嗯...你把这两个文件链接在一起了吗?如何?从我们在您的问题中可以看出,源 1 中没有任何内容被项目使用。这里 C 和 C++ 之间没有区别。因此,您似乎只是在使用 2 个完全不同的链接器并在 C 案例中搞砸了链接?
  • @Lundin 当然我在同一个项目中构建这两个文件。

标签: c++ c global-variables language-lawyer variable-declaration


【解决方案1】:

我认为您正在寻找的是 C11 标准中的第 6.2.7 章,兼容类型和复合类型,(强调我的

所有引用相同对象或函数的声明都应具有兼容类型; 否则,行为未定义。

与兼容类型有关,

如果类型相同,则两种类型具有兼容的类型。

在您的情况下,intunsigned int 不是兼容的类型。因此undefined behavior

为了更清楚一点,在您的源代码 2 中,unsigned int var_global; 是一个声明,它与其他声明(和定义)不匹配,因此,这是 UB。

也就是说,像这样的声明

 printf("%d \n",var_global);

将始终将%d 的参数视为int 类型。如果类型和格式说明符不匹配,您将再次调用undefined behavior


编辑:

编辑后,答案是,使用-fno-common 得到想要的错误。 (我相信缺少extern 是你所困扰的问题)。

引用在线 GCC 手册,

-fno-common

在 C 代码中,控制 未初始化的全局变量的位置。传统上,Unix C 编译器通过将变量放在一个公共块中来允许在不同的编译单元中对这些变量进行多重定义。这是 -fcommon 指定的行为,是 GCC 在大多数目标上的默认行为。 另一方面,ISO C 不要求这种行为,并且在某些目标上可能会带来速度或代码大小的损失变量引用。 -fno-common 选项指定编译器应将未初始化的全局变量放在目标文件的数据部分,而不是将它们生成为公共块。 如果在两个不同的编译中声明了相同的变量(没有extern),则在链接它们时会出现多定义错误。在这种情况下,您必须使用 - fcommon 代替。使用 -fno-common 编译对于它提供更好性能的目标很有用,或者如果您希望验证程序是否可以在始终以这种方式处理未初始化变量声明的其他系统上运行。


我不知道 C 标准中提到了“一个定义规则”的措辞,但是沿着这条线,您可以查看附件 §J.5.11,多个外部定义

一个对象的标识符可能有多个外部定义,带有或 没有明确使用关键字extern;如果定义不一致,或超过 一个已初始化,行为未定义。

【讨论】:

  • 我已经编辑了源代码以避免混淆,请再次检查问题。这种情况导致在 C++ 编译器中重新定义错误,但在 C 编译器中没有。这就是为什么我要求 C 的 ODR。
  • @TrieuTheVan 你没有澄清这个问题,你改变了这个问题。
  • 我有两个问题是粗体的,它们没有任何改变:)。可能是代码混淆了(我已经修复了它们)但是这两个问题非常清楚。
  • @TrieuTheVan 问题不仅在于 bold 行,它也与代码一起出现。为了确保,您确实了解在这样的代码中将 int 更改为 unsigned int 的影响,对吧?
  • @TrieuTheVan 好吧,据我所知,我已经添加了标准引号。如果其他答案澄清更多,将期待对其有更多的了解。 :)
【解决方案2】:

您所看到的与单一定义规则无关。这与 %d 期望有符号值有关,因此几乎可以肯定在您的实现中将其视为有符号值。

但是,这不是您应该依赖的。根据 C 标准 7.19.6.1 The fprintf function /9(我指的是 C99,但 C11 在此处显示的方面几乎相同):

如果任何参数不是相应转换规范的正确类型,则行为未定义。

由于您使用的是未定义的行为,因此实现可以随意做任何事情。此外,该标准还明确指出,如果(来自附件 J)是未定义的行为:

同一对象或函数的两个声明指定的类型不兼容。

在您的情况下,这两个声明确实指定了同一个对象,因为它们都具有外部链接。

现在你可能认为有符号和无符号整数是兼容的,但你错了:6.2.7 Compatible and composite type6.2.5 Types 明确表示有符号和无符号变体不兼容:

如果它们的类型相同,则两种类型具有兼容类型。

对于每个有符号整数类型,都有一个对应的(但不同)无符号整数类型(用关键字 unsigned 指定),它使用相同的存储量(包括符号信息)并且具有相同的对齐要求。

【讨论】:

  • 是的,我知道 %d 点,但在这种情况下,我关注的是变量被重新定义,C 编译器没问题。
  • @paxdiablo 先生,我已添加回标签。如果您不同意标记,请告诉我。谢谢。 :)
猜你喜欢
  • 2014-09-23
  • 2014-10-28
  • 1970-01-01
  • 2011-05-10
  • 2012-07-17
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多