c/c++中extern关键字详解
.h作用:
头文件.h用于编写变量和函数的声明,对用户来说起到一个接口手册和说明的作用。在编译过程的预编译阶段,会将所有的#include原封不动的在原地展开。因此在利用makefile的make时候如果只是修改了.h头文件,利用make编译的时候不会进行增量编译,因为.h对应的.cpp或者.c并没有改变。需要单独删除.h对应的.o文件再进行make。
编译过程:
编译过程分为:
- 预编译阶段:作用:对源文件中的宏进行展开,对#include展开等等。命令:gcc -E test.c -o test.i
- 编译:将展开后的源文件编译成汇编文件(汇编语言)命令:gcc -S test.i -o test.s
- 汇编:将汇编文件编译成01机器码的.o文件 命令:as test.s -o test.o
- 链接:将所有的.o文件链接(链接引用库等等)成一个可执行程序.exe文件。gcc test.o test2.o -o test
编译过程示例:
//lib.h int addTwo(int a, int b); //lib.c int addTwo(int a, int b) { return a+b; }
//main.c #include <stdio.h> #include "lib.h" int main() { int sum = addTwo(1, 2); printf("sum=%d\n", sum); return 0; }
gcc -E main.c -o main.i进行预编译:
可以看到stdio.h展开了好几百行到main.c文件中,lib.h将函数的声明展开到了源文件中。
最后进行链接的命令是:
gcc main.o lib.o -o main
生成可执行程序。编译命令中没有.h文件,因为在预编译过程中已经将所有的.h文件都展开到了源文件中了。
extern简述:
extern是计算机语言中的一个关键字,可置于变量或者函数前,以表示变量或者函数的定义在别的文件中。提示编译器遇到此变量或函数时,在其它模块中寻找其定义。
每一个c文件或者cpp文件最后都会编译为一个.o文件或者.obj文件,最后将所有的.o或.obj文件链接起来组成一个可执行.exe文件。在其他模块的意思是指在链接的时候从其他.o或者.obj文件中找extern变量的定义。
extern代码示例:
对变量而言,如果你想在本源文件(例如文件名A)中使用另一个源文件(例如文件名lib)的全局变量(假设为int gVar),方法有2种:
一、在文件A中声明extern int gVar,且不包含lib的全局变量gVar。表示编译器在链接的时候在另外模块中寻找gVar的定义。即在lib.o中寻找。
//lib.h int gVar = 3; //lib.c #include "lib.h"
//A.c int main() { extern int gVar; printf("gVar=%d\n", gVar); }
程序正常输出3;
编译链接的时候需要将lib.o加上:gcc main.c lib.c -o main
二、在lib.h文件中声明全局变量extern int gVar,在lib.c中定义变量gVar。文件A中包含这个lib.h,即可直接使用gVar变量了。
考虑下面这种情况:
//lib.h int gVar=3; int addTwo(int a, int b); //lib.c #include "lib.h" int addTwo(int a, int b) { return a+b; }
//A.h #include "lib.h" //A.c #include "A.h" int main() { int sum = addTwo(1,2); printf("sum=%d\n", sum); printf("%d\n", gVar); return 0; }
当进行gcc lib.c A.c -o A的时候会报错。
原因是在链接的过程中发现重复定义了变量gVar:
修改如下:
//lib.h extern int gVar; //声明全局变量 extern int gVar = 3;是定义 int addTwo(int a, int b); //lib.c #include "lib.h" int gVar = 3; //定义变量 int addTwo(int a, int b) { return a+b;
}
//A.h #include "lib.h" //A.c #include "A.h" int main() { int sum = addTwo(1,2); printf("sum=%d\n", sum); printf("%d\n", gVar); return 0; }
程序正常输出。
因为在lib.h中只是声明了gVar且为extern,所以在A.o和lib.o的链接过程中,A.o使用的gVar会到其他模块lib.o中寻找gVar的定义,找到为3。
实际代码会更加复杂,此处几个实例简单地剥离来讨论。