编译过程
c语言在编译过程中会经过预处理(需要头文件) -> 编译 -> 汇编 -> 链接(需要库文件); 执行时可能还有动态链接过程。
头文件中有函数的申明,库文件实现函数的定义。
库文件一般是以二进制形式而不是C源文件形式提供给用户使用的。总结起来就是,库文件通过头文件向外导出接口。用户通过头文件找到库文件中。
对头文件的理解
1.为什么要有头文件?
2.头文件中的#ifdef是干什么的?
3.头文件中为什么不能有普通变量函数的定义?
1.为什么要有头文件?
下面举例进行说明
main.cpp
#include <stdio.h>
int main(){
}
demo.cpp
int add(int a,int b){
return a + b;
}
int add2(int a,int b){
return a + b;
}
通过g++ main.cpp demo.cpp -o demo.exe
命令生成可执行文件demo.exe。这个命令完成了将main.cpp和demo.cpp编译成demo.exe。
现在如果main.cpp想调用demo.cpp中的函数,如果用源文件的形式的话只能声明。不能定义,如果定义的话eg.main.cpp
#include <stdio.h>
int add(int a,int b){
return a + b;
}
int main(){
}
此时编译后的打印:
会出现重定义的错误。
因为main.cpp和demo.cpp中都有add()函数。因此只能用声明,声明后的main.cpp
#include <stdio.h>
int add(int a,int b);
int main(){
add(2,2);
}
声明后就可以使用demo.cpp中的函数了。声明后再编译就不会报错了。声明的意义在于:编译器在看到demo.cpp和main.cpp的时候,会把它们编译成二进制文件。在main函数中编译器看到有调用函数,且这个函数有声明的时候就会挖一个坑。这个main.cpp里面没有这个函数,但是它有声明,所以就告诉后面的链接器(两个源文件编译成二进制文件后还要通过链接器进行链接,链接成demo.exe),让链接器来填挖的坑。链接器在拿到demo.cpp编译的二进制文件和main.cpp编译的二进制文件后会在全局里面搜有没有add()函数。搜到后就把坑填上,填上函数的地址是什么。如果搜不到的话,链接器就会报错。
demo.cpp
// int add(int a,int b){
// return a + b;
// }
打印如下:
假设main.cpp还想调用demo.cpp中的add2()函数。此时main.cpp也需要写add2()的声明。
如果有test.cpp也想用demo.cpp中的函数,那么test.cpp中也需要再写一遍这个声明,才能用demo.cpp里面的函数。由此,头文件应运而生。
创建一个demo.h,包含demo.cpp中的函数的声明。
demo.h
int add(int a,int b);
int add2(int a,int b);
在main.cpp中包含该头文件就可以编译成功。
预处理会将demo.h中的内容无脑的粘贴复制到main.cpp下面。
2.头文件中的#ifdef是干什么的?
静态库和动态库
。。。