用命令行编译C文件我们已经知道了
简单谈一下他的编译过程..
首先我们创建了test.c的C文件 也就是源文件, 接着预编译,再由编译器编译为汇编代码,
汇编器把汇编代码编译为目标文件(.o),最后由连接器把所有目标文件与库文件连接起来生成可执行文件
源文件->预编译文件->汇编代码文件->目标文件->链接后生成可执行文件;
下面学习头文件与实现文件的意义
我们都知道C语言编程第一行是什么呢
1 #include <stdio.h> 2 int main(){ 3 printf("Hellow Word"); 4 }
是的,是#include导入头文件 大家可以试试把第一行去掉
gcc 编译是不是error了?错误提示告诉你printf函数不存在 是的,这个打印输出只是<stdio.h>库文件里的一个方法,所以必须导入库文件。
如果我们自己编译的程序有几千行甚至更多..都写在一个test.c那么维护起来是不是很麻烦?代码重用性也很低..
对于这种问题头文件和实现文件就能很好地解决了
头文件是以.h结尾的文件 它的作用相当于其他语言的说明文档..头文件内只是声明属性,变量或方法并不去实现
那么如何实现这些方法呢?都写在实现文件.c里面 当然 两个文件的文件名是相同的 只是后缀不同
我们来做个例子..
创建头文件testA.h代码只有一行 声明一个sum方法;
1 int sum(int,int);
创建实现文件testA.c实现这个方法;
1 int sum(int i,int j) 2 { 3 return i+j; 4 }
创建测试文件test.c
1 #include <stdio.h> 2 #include "testA.h" 3 int main(){ 4 int m = 5; 5 int n = 6; 6 int result = sum(m,n); 7 printf("%d\n",result); 8 }
编译运行 gcc -o test test.c testA.c
头文件是不需要编译的 但是实现文件需要跟测试文件一起进行连接 也可以先把实现文件编译成目标文件
gcc -c testA.c//得到目标文件testA.o
gcc -o test test.c testA.o
运行test.exe可以看到result输出了sum方法的返回值11;
可能有人会问 有实现文件实现方法就可以了 为什么要头文件呢 直接导入实现文件testA.c不可以吗?
当然不是不可以,直接#include "testA.c"导入实现文件不用头文件也是可以的;但头文件的意义何在呢?
这里就要讲到一个依赖倒转的理念了..依赖倒转是指:高层如果依赖于底层的话底层改变,高层也不得不改变,
那么代码维护和重用重用性就大大降低了,所以高层都应该依赖于抽象而不是底层这是依赖倒转原则。
只是这样说的话很多人可能不理解..我们来做个例子..
假设有两个实现文件testA.c,testB.c,各自对应头文件testA.h,testB.h
且testA.h依赖于testB.h
testA.c
int sum(int i,int j) { return i+j; } int MULT (int i,int j) { return MULTB(i,j); }
testA.h
#include "testB.h" int sum(int,int); int MULT (int,int);
testB.c
1 int MULTB(int i,int j) 2 { 3 return i*j; 4 }
testB.h
int MULTB(int,int);
test.c
1 #include <stdio.h> 2 #include "testA.c" 3 int main(){ 4 int m = 5; 5 int n = 6; 6 int result = MULT(m,n); 7 printf("%d\n",result); 8 }
可以看到testA.c的MULT()方法是依赖于testB.c的MULTB()方法实现的;
我们先来把testA.c,testB.c都编译为目标文件
gcc -c testA.c testB.c//得到testA.o,testB.o两个目标文件
再把目标文件与测试文件链接
gcc test.c testA.o testB.o//生成可执行文件
可以看到testA.c的头文件依赖于testB.c的头文件实现了MULT()方法
如果对testB.c的方法进行修改呢
testB.c
1 int MULTB(int i,int j) 2 { 3 return i+j; 4 }
我们把乘法改成了除法 重新把testB.c编译成目标文件:gcc -c testB.c
再次链接生成可执行文件:gcc test.c testA.o testB.o
可以看到testA.o并没有重新编译 但testA.c的MULT()方法又是依赖于testB.c 为什么testA.c不用重新编译呢?
因为依赖所写是在头文件 程序通过头文件找到.o文件..头文件相当于声明了一个抽象方法..具体实现要看实现类怎么写
也就是说testA.c的MULT()方法是依赖于testB.h内的一个抽象方法,所以即使testB.c改变了,testA.c依然能使用而不用重新编译。
下面大家做一个测试
有A.c,B.c两个实现文件(内容随意)分别对应A.h,B.h两个头文件..
文件A.h依赖于文件B.h,文件B.h又依赖于文件A.h
test.c测试#include导入A.h 编译运行