22

为什么我不能有int a; 在 2 C 文件中。我打算将两者结合起来以使其可执行。我从经验中知道我做不到,但我想找到标准 C99 所说的地方并密封我的理解。

我正在阅读来自http://www.open-std.org/jtc1/sc22/wg...docs/n1256.pdf的 ISO C99 标准。它在第 42 页上说:

6.2.2 标识符的链接

1 在不同作用域或同一作用域中多次声明的标识符可以通过称为链接的过程来引用同一对象或函数。链接分为三种:外部、内部和无。

2 在构成整个程序的一组翻译单元和库中,具有外部链接的特定标识符的每个声明都表示相同的对象或函数。在一个翻译单元中,带有内部链接的标识符的每个声明都表示相同的对象或函数。没有链接的标识符的每个声明都表示一个唯一的实体。

3 如果对象或函数的文件范围标识符的声明包含存储类说明符 static,则该标识符具有内部链接。

4 对于使用存储类说明符 extern 声明的标识符,在该标识符的先前声明可见的范围内,如果先前声明指定内部或外部链接,则后面声明的标识符的链接与在事先声明中指定的链接。如果前面的声明不可见,或者前面的声明没有指定链接,则标识符具有外部链接。

5 如果函数的标识符声明没有存储类说明符,则其链接的确定与使用存储类说明符 extern 完全一样。如果对象的标识符声明具有文件范围且没有存储-class 说明符,它的链接是外部的。

读完后,如果我声明一个变量,比如 say int a; 在 2 个源文件中。然后根据规则 5 和 4 都具有外部链接。然后根据规则 2,两者都应该引用同一个对象。那么为什么编译器会产生问题。在标准中暗示我们不能在 2 个源文件中这样声明,这应该会引发编译错误。首先,在标准中,它说 int a 是一个定义,然后它说 2 个定义实例是不可接受的。根据我的经验,我知道这是不允许的,但如果我能在标准中找到这一点并密封我的理解,这对我来说将非常有用。

标准中的以下摘录是否构成此规则?还是我错过了胶水?

声明指定了一组标识符的解释和属性。标识符的定义是对该标识符的声明: ——对于一个对象,导致为该对象保留存储;——对于函数,包括函数体;— 对于枚举常量或 typedef 名称,是标识符的(唯一)声明。

如 5.1.1.1 所述,预处理后的程序文本单元是一个翻译单元,它由一系列外部声明组成。这些被描述为“外部”,因为它们出现在任何函数之外(因此具有文件范围)。正如 6.7 中所讨论的,一个声明也会导致为标识符命名的对象或函数保留存储空间是一个定义。

外部定义是一个外部声明,它也是函数(内联定义除外)或对象的定义。如果用外部链接声明的标识符在表达式中使用(而不是作为结果为整数常量的 sizeof 运算符的操作数的一部分),则在整个程序的某处,该标识符应有一个外部定义;否则,不得超过一个。

谢谢。

4

1 回答 1

18

我认为你需要 6.9.2/2:

具有文件范围的对象的标识符声明没有初始化程序,并且没有存储类说明符或具有存储类说明符static,构成一个暂定定义。如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,则行为与翻译单元包含该标识符的文件范围声明完全相同,复合类型为翻译单元的末尾,初始化器等于 0。

和 6.9/5:

外部定义是一个外部声明,它也是函数(内联定义除外)或对象的定义。如果用外部链接声明的标识符在表达式中使用(而不是作为sizeof结果为整数常量的运算符的操作数的一部分),则在整个程序的某处,该标识符应该只有一个外部定义;否则,不得超过一个。

基本上,int a;是一个暂定的定义。您可以在一个翻译单元中拥有多个暂定定义,但效果与拥有一个非暂定外部定义相同(例如,类似int a = 0;)。在程序中具有多个具有外部链接的对象定义违反了 6.9/5。

请注意,允许对象的多个外部定义是一种“通用扩展”,只要最多只有一个被初始化并且定义一致(参见 J.5.11)。

于 2011-02-14T09:14:39.437 回答