extern作用
1、声明变量
2、指定编译和链接的规则
声明变量
怎么C++中,怎么声明一个变量了,或许很多人都不知道
int a; //错误
int b = 1; //错误
extern val; //正确, 声明一个变量val
很多人可能觉得 like: int a; 这样的语句,就是声明一个变量,其实不是,声明一个变量,表示这个变量是没有内存的,只是告诉编译器,我即将要有一个变量,但是不是现在就需要。
如果是int a 这样的语句,其实a已经有内存地址了,编译器已经给他分配内存了,但是a的值是不确定的,所以需要初始化。
对于extern val 这样的语句,是声明变量的语句,表示这个变量在外部被定义。即在其他文件中提供了这个val变量的定义,我这个文件需要使用val , 就可以使用extern val来声明并使用这个变量。
source1.cpp
int val;
//
source2.cpp
extern val;
可以对这个变量进行操作,对val的修改会影响到source1.cpp的val
即他们是同一个变量.
疑问: 这样的使用场景是什么 ?
总所周知,头文件中是最好不要出现全局变量(no static),不然很容易出现重复定义,因为当这个头文件被一个源文件include 可能没问题,但是当有多个源文件incldue的时候,每个源文件都有这个非static全局变量,所以就会报重定义的错误。
当然,也可以使用条件编译来规避,但是不推荐这样做。特别是做成动态库的时候,头文件全局变量会出现你意想不到的BUG。做法就是将全局变量放在源文件中,所以就使用extern 来声明一个变量吧。
编译和链接的规则
在CPP文件中,只要没有显示的指定(extern "c"),后者都是按照cpp的编译和链接规则来处理的。
什么意思了?
source.cpp
int sum(int a, int b) {
return a + b;
}
使用C的编译规则,就会生成 一个 sum 的符号函数和变量名就是符号的名称。
如果按照CPP的规则,就会生成一个_Z3sumii的符号。为什么会这样,因为CPP支持函数重载。
当我们看C语言库的时候,就会发现里面的头文件,都会出现extern "c" { ...... }这样的代码,这是什么原因了。让我们分析一下,当C库被cpp文件所使用会出现什么?
上面可知,C库是按照C语言的规则来编译代码的,当C库某一个头文件被CPP文件所include 的时候,就会原地展开,编译的时候,对这些函数的声明等就会按照CPP的规则来编译,当在链接阶段的时候,就会去寻找符号,但是发现找不到,一个是CPP的符号,一个是C的符号,不对应,所以就会报错。那怎么办了,可以在头文件中使用这样
#ifdef __cpluscplus //是C++宏,在C++文件中有效
extern "C" {
int sum(int a, int b);
}
#endif
这样就会编译器就知道,当前的头文件是被CPP文件还是C文件所include的,这样当CPP文件所include 并且使用的时候,就会按照C的编译规则来编译出符号啦,这样就能对应C库的sum函数生成的符号,就不会报找不到符号的错误了。
疑问解答:
.h文件
extern "c" {
int sum(int a, int b);
int inc(int a) {
return a + 1;
}
}
.cpp文件
int sum(int a, int b) {
return a + b;
}
上面两个函数都是按照C的编译规则,尽管sum函数的定义在cpp文件中。结论就是,只要函数声明是extern "c"的,就得按照C来做。
可以使用nm来查看符号。
2、static 能和extern 来搭配使用吗 ?
不能,static具有跨文件的不可见性,而extern 则希望变量在外部定义,有冲突。