我在test2.h
#ifndef TEST2_H
#define TEST2_H
int test_var;
void use_it(void);
#endif
并在两个不同的文件test.c
#include <stdio.h>
#include "test2.h"
int test_var;
int main() {
printf("The test_var is: %d\n", ++test_var); // prints 1
use_it(); // prints 2
}
和test2.c
#include <stdio.h>
#include "test2.h"
int test_var;
void use_it() {
printf("The test_var is: %d", ++test_var);
}
我将test_var
的定义替换为extern int test_var
并得到了相同的结果。也就是说,在这两种情况下,test.c
和test2.c
这两个文件都可以访问全局变量test_var
。我的印象是,如果没有extern
,每个文件都会有自己的test_var
副本。观察表明情况并非如此。那么extern
什么时候真的做了什么呢?
答案 0 :(得分:5)
您最终会得到test_var
的两个副本,这是未定义的行为。
(C99,6.9p5)“如果在表达式中使用通过外部链接声明的标识符(除了作为sizeof运算符的操作数的一部分,其结果是整数常量), 在整个程序的某个地方,应该只有一个外部 标识符的定义;否则,不得超过一个“
在你的情况下,链接器可能对你很好并且合并两个符号但是这仍然不可移植并且是未定义的行为。如果您使用的是GNU链接器,则可以使用--warn-common
来获取警告(如果您想要出错,还可以--fatal-warnings
。)
要解决您的问题,请将extern
说明符放在test_var
文件中的.h
声明中,然后删除test_var
的一个定义(例如在test.c
文件中。
答案 1 :(得分:4)
这是未定义的行为,正如其他人所指出的那样,但你在这里看到的是C99规范的附录J.5.11中描述的公共扩展,其中只要没有或只允许不同编译单元中的多个外部定义其中一个被初始化,所有类型都是相同的。
在这种情况下,使用扩展名,定义将在链接时合并为单个定义。
当您在全局范围内使用extern
关键字与声明和定义的外部链接无关时,您似乎也感到困惑。全局范围内的所有声明都具有外部链接,除非具有static
或inline
关键字。 extern
关键字用于将此类声明仅声明。如果没有extern
关键字,全局变量声明也是一个定义,这是extern
关键字在全局范围内的唯一影响。
答案 2 :(得分:2)
如果您在2个不同的文件中声明了与int test_var
相同的变量,例如:
file1.c中
int test_var;
file2.c中
int test_var;
两个变量都有自己的内存地址,因此它们是两个具有相同名称的不同变量。
如果有,则在声明为extern int test_var
的2个不同文件中声明两个变量,例如:
file1.c中
extern int test_var; //this is a mistake
file2.c中
extern int test_var; //this is a mistake
当您尝试对该变量执行某些操作时,编译器将返回错误,因为使用关键字extern
您没有为该变量保留任何空间,您只使用该关键字来表示已定义变量(通常在另一个文件中)。
重点是要理解全局变量是用int test_var
这样的句子定义一次的(当你为它定义一个变量编译器保留空间时),并且在每个需要访问它的文件中声明它extern int test_var
(当您使用extern关键字声明变量时,您说编译器已经定义了变量,并且您希望在声明它的文件中有权访问它。)
所以如何使用全局变量的例子是:
file1.c中
int test_var; //definition
void useit(void);
int main () {
test_var=7;
useit();
return 0;
}
file2.c中
#include <stdio.h>
void useit (void) {
extern int test_var; //declaration
printf ("the variable value is %d",test_var);
}
答案 3 :(得分:1)
回答你的问题:
extern int test_var;
是声明。这宣布“在某处,应该存在test_var
。我们不知道那里还有什么,但是当我们完成编译和链接时,我们会在一个地方找到它。”
因此必须只有一个定义才能匹配。定义用作声明,和也表示“这是test_var
的存储”。
此外,test_var
可以包含内部链接或外部链接。默认行为是外部链接。如果为外部链接变量提供多个定义,则它是未定义的行为。
通过在声明中包含static
来表示内部链接。您可以拥有任意数量的静态变量定义(只要每个文件只有一个具有初始化程序)。
总结一下,我们有:
extern int test_var; // declaration, external linkage
static int test_var; // declaration, definition, internal linkage
int test_var; // declaration, definition, external linkage
注意:最后两种情况实际上是暂定定义:这是C有的东西,但C ++没有;它的工作方式是它起初就像一个声明;但是,对于每个单位,如果没有后来的定义,那么这实际上就是一个定义。
所以你可以写C:
int test_var;
// stuff
int test_var = 5;
答案 4 :(得分:0)
如果您正在使用gcc
以及其他一些编译器,那么您只是偶然发现了一些Unix传统。即未初始化的全局变量放在公共块中,其中在链接期间合并同一变量的多个定义。
gcc
使用选项-fno-common
将未初始化的全局变量放入数据部分。这样,当存在多个相同变量名的定义时,链接器将报告错误。