以下是标准的相关部分。请看下面标准文本的解释:
§6.9.2/2 外部对象定义
具有文件作用域且没有初始化程序、没有存储类说明符或具有静态存储类说明符的标识符的声明构成了一种试探性定义。如果一个翻译单元包含一个或多个标识符的试探性定义,并且该翻译单元不包含该标识符的外部定义,则行为就像该翻译单元包含该标识符的文件作用域声明,其组合类型为翻译单元末尾的组合类型,初始化程序等于0。
ISO C99 §6.9/5 外部定义
外部定义是函数(而不是内联定义)或对象的外部声明,也是定义。如果使用具有外部链接的标识符在表达式中(除了作为sizeof运算符的操作数之一,其结果是整数常量),那么整个程序中必须恰好有一个标识符的外部定义;否则,不能有多个。
使用C版本时,“g”全局变量将“合并”为一个,因此最终您只会有一个被声明两次的变量。这是可以的,因为在extern不需要的时候,或者可能不存在的情况下。因此,这是为了构建旧代码的历史和兼容性原因。这是gcc的一个扩展,用于此遗留功能。
它基本上使gcc为名为'a'的变量分配内存,因此可以有多个声明,但只有一个定义。这就是为什么即使使用gcc,下面的代码也不起作用。
这也称为试探性定义。C++中没有这样的东西,这就是为什么它可以编译的原因。C++没有试探性声明的概念。
试探性定义是任何没有存储类说明符和初始化程序的外部数据声明。如果到达翻译单元的末尾并且没有出现具有标识符的初始化程序的定义,则试探性定义成为完整定义。在这种情况下,编译器为定义的对象保留未初始化的空间。
请注意,即使使用gcc,以下代码也无法编译,因为这不再是试探性定义/声明,并且已分配值:
int a = 1
在文件“b.c/b.cpp”中
int a = 2;
int main() { return 0; }
让我们通过更多的例子来深入了解。以下语句显示正常定义和暂定定义。请注意,静态将使其有所不同,因为它是文件范围,并且不再是外部的。
int i1 = 10;
static int i2 = 20;
extern int i3 = 30;
int i4;
static int i5;
int i1;
int i2;
int i3;
int i4;
int i5;
进一步的详细信息可以在以下页面找到:
http://c0x.coding-guidelines.com/6.9.2.html
此外,关于更多详情,请查看此博客文章:
http://ninjalj.blogspot.co.uk/2011/10/tentative-definitions-in-c.html
b.c/b.cpp
中进行声明extern int a
,而不是int a
,它就可以工作了。 - Uchia Itachi