本文参考文章,侵删
C++学习之extern关键字
extern关键字的用法
extern关键字的用法
extern 与头文件(*.h)的区别和联系
在看cjson源码的时候碰到了extern,之前也碰到过,没有太注意,这次再仔细学了下。
用途:
- 可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义,这里起到的是声明作用范围的用处,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或者其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在连接时从模块A生成的目标代码中找到此函数。
- extern还可以与”C”连用,作为链接指示,当它与”C”一起连用时,如: extern “C” void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的”脾气”了(不同的编译器采用的方法不一样)
C++名字修饰
这就要从C++的重载说起了,在C++中函数重载指的是几个函数的函数名相同,参数列表不同。那么当生成obj中间文件/目标文件的时候,C++编译器如何区分这几个重载函数呢?——通过把原函数名与参数信息结合,产生一个独特的内部名字,这种技术叫做名字修饰。名字修饰规则没有一个标准,所以不同的编译器的名字修饰规则也不一样。
int f (void) { return 1; }
int f (int) { return 0; }
void g (void) { int i = f(), j = f(0); }
f(void)和f(int)是不同的函数,除了函数名相同以外没有任何关系。当生成obj目标文件时,为了区分它们,C++编译器根据参数信息进行了名字修饰:
int __f_v (void) { return 1; }
int __f_i (int) { return 0; }
void __g_v (void) { int i = __f_v(), j = __f_i(0); }
注意g()也被名字修饰了,虽然没有任何名字冲突。名字修饰应用于C++的任何符号。
为何使用extern “C”
C语言中没有名字修饰,因为C语言不支持函数重载。但是如果C++中含有C代码,在编译时C++编译器对C代码的函数也会进行名字修饰,函数名变了以后,将导致在C运行库中找不到对应函数,发生链接错误。
// 将下面的代码保存为.cpp文件,并用C++编译器编译
int printf(const char *format,...);
int main()
{
printf("GeeksforGeeks");
return 0;
}
结果:
/tmp/ccQBO9Im.o:在函数‘main’中:
test.cpp:(.text+0xf):对‘printf(char const*, ...)’未定义的引用
collect2: 错误:ld 返回 1
为了防止C++编译器对C代码进行名字修饰,我们将C代码用extern "C"
进行链接指定,告诉编译器,在生成中间文件时,不要对这部分代码进行名字修饰,而是生成符合C规则的中间符号名。
extern "C"
{
int printf(const char *format,...);
}
int main()
{
printf("Hello!");
return 0;
}
添加了extern “C”链接指示后,上面的代码就能够正常运行了。
所以,所有的C风格的头文件(stdio.h, string.h, … 等等)都有在extern “C”下声明,形式如下:
#ifdef __cplusplus
extern "C" {
#endif
/* Declarations of this file */
#ifdef __cplusplus
}
#endif
另:声明和定义的区别
例子:
头文件:state.h
源文件:state.cpp
其它源文件:t1.cpp t2.cpp t3.cpp
这些源文件都包含头文件state.h。
需要定义一个全局变量供这些源文件中使用:方法如下
1、在 state.h
声明全局变量: extern int a
;
2、在state.cpp
中定义该全局变量:int a = 10
;
这样其它源文件就可以使用该变量。
这里需要的是“声明”,不是“定义”!根据C++标准的规定,一个变量声明必须同时满足两个条件,否则就是定义:
(1)声明必须使用extern关键字;(2)不能给变量赋初值
extern int a; //声明
int a; //定义
int a = 0; //定义
extern int a =0; //定义
头文件中应使用extern 关键字声明全局变量(不定义),如果这个变量有多个文件用到,可以新建一个cpp,在其中定义,把这个cpp加入工程即可。头文件请不要定义任何变量,那是非常业余的行为……
一般在头文件中申明,用extern, 在cpp中定义。 如果在头文件中定义,如果这个头文件被多个cpp引用,会造成重复定义的链接错误。
头文件只能申明全局变量(extern),不可定义(不推荐使用) .cpp里,在最外层定义即可(int gi),直接引用
如果在.cpp里使用static定义,则该变量只在当前cpp文件中有效,在别的文件中无效
在.h里使用static定义,不会进行编译(.h文件不编译),只会在其每个include的cpp文件中包含编译,相当于在.cpp里使用static 定义。