在C语言中,默认情况下,文件作用域内的变量和函数都具有外部链接。为什么变量需要关键字extern
,而在其他地方定义的函数不需要呢?请注意,这个问题有两个方面:考虑到文件作用域的声明默认具有外部链接:
- 为什么历史上对于函数的声明是否有
extern
没有区别?或者客观地说,为什么不需要这样的区别? - 为什么历史上对于变量的声明是否有
extern
有区别?或者客观地说,为什么需要这样的区别?
我们可以使用以下两个源文件(tu
代表“翻译单元”)来进行最小化示例。
tu1.c
:
extern int i = 123;
tu2.c
:
#include <stdio.h>
extern int i;
int main(void) {
//extern int i;
++i;
printf("%d\n", i);
return 0;
}
我们可以使用GCC编译它们,方法如下:
gcc -c tu1.c
gcc -c tu2.c
gcc -o myprogram tu1.o tu2.o
(GCC在第一条指令中发出警告'i' initialized and declared 'extern'
,因为它错误地认为extern
“应该保留给非定义性声明”。我们可以安全地忽略它。)
让我们比较编译器对源代码稍微不同版本的处理方式:
- 在
tu2.c
的文件作用域中添加extern int i;
(对以上代码无更改):
myprogram
的输出是124
,符合预期。 - 在
main
中(而不是在文件作用域)添加extern int i;
:
这同样有效(但这种风格not recommended:"一个函数不应该需要使用extern
声明变量。")。 - 在
tu2.c
中未声明i
:
这将无法工作;对于行i++;
,我们会得到以下错误:'i' undeclared (first use in this function)
。 - 在
tu2.c
的文件作用域中添加int i;
而没有extern
:
这将失败,并出现错误:multiple definition of `i'
。
extern
)int i;
默认为外部链接,为什么我们需要显式提供关键字extern
?答案似乎在标准中(C99:6.9.2外部对象定义)中,根据该标准,int i;
是一个试探性定义,在编译时实例化为实际定义。逻辑是提供extern
关键字指示编译器将结果构造视为不是定义的声明。但是如果是这样的话:为什么对于函数原型,相同的逻辑不适用,因为众所周知extern
是隐含的?
我有一种感觉,正确的答案与“C中试探性定义背后的理由是什么?”相关或接近,但我想知道如果变量和函数在上述方面被视为相同,会发生什么问题。
有一个关于C++的类似问题和一篇相关的Peter Goldsborough的文章。
针对习惯于使用C++编程的人:
- 在C中,文件作用域(包括
const
)变量和函数默认为外部链接。 - 在C++中,有一个微妙的区别,即
const
全局变量默认为内部链接。
)
后面的;
告诉您正在查看声明,而不是定义。因此,可以假定为extern
。 (对于变量有一个旧的“共同模型”,在其中extern
也基本上是可选项,但它已经不再流行了。) - Steve Summitint x;
将总是定义变量x
。int foo(void)
不定义函数foo
。 - 0___________=
?无论如何,在这种情况下,我假设问题与告诉编译器该符号已在其他地方定义或将要定义有关(也就是说,这涉及内存分配,它区分了定义和声明)。我可能漏掉了一些东西。总之,为什么我们不能默认情况下对变量(在文件范围内)假定为extern
呢?这会给编译器带来不便吗?或者,如果这是关于向后兼容性,具体是什么? - Lover of Structure