我一直在测试全局变量、定义和声明,然后我就停在了这种情况下:
main.c:
#include "stdio.h"
void func(void);
int a;
int main(void) {
a = 20;
printf("in main: %d\n", a);
func();
return 0;
}
添加.c:
#include <stdio.h>
void func(void);
int a;
void func() {
printf("in add: %d\n", a);
}
所以在 C 中
int a;
既表示声明又表示定义,但我们知道不允许多次定义一个变量。那么,如果我们有两个
a
的定义和声明,为什么这段代码能够编译呢?
我正在 CLion 中工作,当我在 main 中的 a
上按“转到定义/声明”时,它将指针移动到 add.c 中的 a
,当我在 add.c 中执行相同操作时,它会向后移动到 main.c,所以我无法理解这里发生了什么。
在任何函数之外,
int x;
是一个暂定定义,一些编译器和链接器将它们视为一种“协作定义”,其中可以在多个文件中以这种方式声明标识符,并且将导致仅定义一个对象。
由于历史原因,C 的外部声明(函数之外的声明)规则有点复杂 - C 是随着不同的人开发和实验而成长的,而不是根据我们今天所拥有的知识进行设计的。
定义:
int x = 3;
是一个定义。它既声明了标识符 x
,又为 int
保留了内存,并将 int
初始化为 3。
声明:
extern int x;
是声明,而不是定义。它声明了标识符 x
但不为其保留内存。
extern int x;
提供 x
外部链接,如果 int x = 3;
出现在函数外部,则它也是如此。外部链接意味着,当它们出现在不同的源文件中时,标识符的两个实例将被链接以引用内存中的同一事物。
C 标准规定,具有外部链接的标识符“应该”最多有一个定义 (C 2018 6.9 5)。 (如果程序中使用了标识符,则必须有定义。如果表达式中没有使用标识符,则不需要定义。)
暂定定义:
int x;
是一个混合体。在函数之外,它是一种特殊的声明,称为“暂定定义”。 C 标准规定,如果翻译单元(正在编译的源文件及其包含的所有文件)中有暂定定义,并且没有常规定义,则会创建常规定义。
现在,如果你违反了“最多应该有”一个定义的规则,会发生什么?事情是这样的:这不是程序必须遵守的规则。当 C 标准说“应该”时,意味着如果程序遵守此规则,则行为将如 C 标准所述。如果程序违反此规则,C 标准不会定义该行为 (C 2018 4 2)。相反,我们让编译器和链接器定义行为,如果他们的设计者选择这样做的话。
当程序违反最多一个定义的规则时,编译器和链接器中的一种常见行为(但不是唯一的可能性)是:
链接时如果有多个正则定义,报错。
-fcommon
。