为什么’extern’存储类在函数中的工作方式不同?

以下代码段正常

extern int i; int i; int main(){ return 0; } 

我得到的是,’i’被声明然后被定义。 由于只有一个定义,所以完全没问题。

 int main(){ extern int i; int i; return 0; } 

现在,上面的一个给出了以下错误

 new.cpp: In function 'int main()': new.cpp:5:6: error: redeclaration of 'int i' int i; ^ new.cpp:4:13: note: previous declaration 'int i' extern int i; 

问题出在这里? 这里也有’i’的单一定义。

要理解这种差异,您需要熟悉C中称为暂定定义的概念。引用C标准:

C11,draft,§6.9.2,外部对象定义

具有文件范围而没有初始化程序且没有存储类说明符或存储类说明符为静态的对象的标识符声明构成暂定定义。 如果翻译单元包含一个或多个标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,那么行为就像翻译单元包含该标识符的文件范围声明一样,复合类型为翻译单元的结尾,初始化程序等于0。

您在第一个片段中所拥有的只是i的暂定定义。 您可以根据需要为对象设置尽可能多的暂定定义(但只允许一个定义):

 int i; // tentative definition int i; // tentative definition int i; // tentative definition int main(void) { return 0; } 

已validation。

在这里, i有外部联系和暂定。 如果i在同一个翻译单元的某个地方定义,那么那将是i的实际定义。 如果在翻译单元中找不到i的其他定义,那么这将成为完整定义,就好像它被定义为:

 int i = 0; int main(void) { return 0; } 

但是我的第二个片段int i; 不是暂定的定义。 只能暂时定义具有外部链接的对象。 在第二个片段中,声明extern int i;i在其他地方定义了外部联系。 但下一行int i;i没有链接定义(本地自动变量没有任何链接 – 这不是一个暂定的定义)。 所以i的定义存在冲突。 因此,第一个片段是好的,但第二个不是。

在第二种情况下,在一个范围内有两个i声明。 一个人说“ i在这个函数之外定义了一个变量”; 另一个说“ i在这个函数中定义了一个变量”。 没有新的范围,这是不允许的。

内部和外部function的规则是不同的。

请注意,您可以使用:

 #include  int i = 21; int main(void) { extern int i; i = 37; { int i = 57; printf("%d\n", i); } printf("%d\n", i); return 0; } 

这个编译好(除非你在使用GCC或Clang时在编译选项中包含-Wshadow ),并在输出上产生5737 (如注释中的CoffeeAndCode所指出的)。

另请参阅如何使用extern在源文件之间共享变量?