先写一段代码演示
1 #include<stdio.h>
2 #define M 100
3 int main()
4 {
5 printf("hello linux");
6 printf("hello linux");
7 //printf("hello linux");
8 //printf("hello linux");
9 //printf("hello linux");
10 //printf("hello linux");
11 //printf("hello linux");
12 //printf("hello linux");
13 //printf("hello linux");
14 //printf("hello linux");
15 //printf("hello linux");
16 //printf("hello linux");
17 printf("hello linux");
18
19 printf("hello M");
20 printf("%d",M);
21 }
1.预处理
gcc -E test.c -o test.i//这条指令-E的含义是从现在开始进行程序翻译,预处理完成,就停下.
生成test.i
vim test.i//打开预处理生成的文件
1.头文件展开
我们发现多了800多行代码
stdio.h库中库函数都在这个路径下面
我们使用vim打开该头文件下的库
我们发现代码中多的八百多行代码,其实就是头文件展开了,去注释后两者一样.
2.宏替换
vim再次打开test.i
对比test.c 和test.i 发现宏定义消失,宏定义的M被直接替换成了100,而字符串中出现的M不会被替换掉.
3.去掉注释
再次对比发现test.c中的注释也被去掉了
4.条件编译
在预处理阶段,还会处理条件编译,在公司推出一款软件时,往往会出现社区版,以及个人版等,实现功能不太语言,那公司是由一套代码维护还是多套呢?
不同的条件编译(这里宏定义M2)就可以实现社区版和个人版用同一套代码维护啦!!
我们也可以通过命令行来进行不同的条件编译
gcc -DM2=1 test1.c
这条指令条件编译,宏定义 M2 1
总结:预处理完成宏替换,去注释,头文件展开,条件编译
2.编译
gcc -S test.i -o test.s//这条指令-S代表从现在开始进行程序的编译,编译完成就停下来
生成了test.s文件
vim打开test.s
发现代码已经变成汇编代码,里面都是汇编指令
总结:编译过程将c语言代码变成汇编语言,此时如果出现变量名没写对会出现编译错误,会进行语法检查
3. 汇编
gcc -c test.s -o test.o//-c选项从现在开始进行程序的翻译,汇编完成就停下来.
生成了test.o
vim打开test.o
我们在vs中也会生成这种obj目标文件
总结:汇编过程是将汇编语言变成二进制目标文件
4.链接
gcc test.o -o test.exe//形成可执行程序
形成可执行程序
总结:链接生成可执行程序
5.c++中的函数重载
1.创建一个.cpp文件
add函数满足参数类型不同构成重载函数
2.g++编译代码
g++ -o my.exe add.cpp// -o后面接着是生成文件的别名
3.查看汇编代码
objdump -S my.exe
为什么c++支持函数重载,而c语言不支持函数冲重载,c语言依靠函数名来区分不同函数,而c++会对重载的函数名加以修饰。
6.编译器的自居过程
当我们没有c语言时,我们用的是汇编语言编程,当没有汇编语言时,我们用的是二进制编程.
而我们c语言的翻译过程就是历史推演的逆过程.我们想一想图中1过程简单,还是2过程简单呢?
当然是2过程了,因为前人已经可以将汇编语言转成二进制了,就是我们的汇编过程.
那是先有编译器呢?还是先有语言呢?第一个汇编编译器是怎么得到的呢?第一个c语言编译器又是怎么得到的呢?
首先编译器是个软件,软件由编译器写的,我们使用二进制编程可以写一个汇编的编译器,然后通过汇编的编译器就可以编写汇编的语言了,然后我们可以用汇编的语言写一个c语言的编译器,然后通过c语言的编译器就可以写c语言了,这就是编译器的自居过程.
7.链接精讲
链接是我们的程序和库结合的过程,语言一定要有自己的标准库,比方说我们写了一个程序,需要使用printf函数,这个printf函数就是标准库里面的.
链接分为动态链接和静态链接,链接可以让我们提高开发效率,比如说我们要实现人物的移动通过键盘,我们可以使用图形库中的函数GetAsyncKeyState来实现,这样是将我们的程序和图形库链接起来.
1.在Linux系统下利用ldd指令查看动态库依赖关系;
2.我们将所依赖的动态库在上述路径下,我们ls -l 这个路径下,发现有个库文件
linux
动态库:.so
静态库:.a
windows
动态库:.dll
静态库: .lib
链接时,动态库进行动态链接,静态库进行静态链接
1.如何理解动态链接和静态链接?
讲一个小故事帮助理解,假如说你在一个高中上学,你在学校里要有你自己的作息,相当于任务,假如说上网是你的作息的一种,你的老学长会告诉你网吧的地址,网吧的机子是你上网的方法。这里你的老学长就相当于你的编译器,会告诉你目标库的地址,然后你的作息是你要执行的程序,包括上网,网吧就是你的动态库,动态库里面的机子就是你的方法,也就相当于你要调用标准库里面的printf函数,假如说有人向派出所举报了这个网吧,也就相当于共享的动态库被删除,要使用这个动态库的函数就不行了,如果将电脑机子搬回你的学校寝室,也就相当于在编译的时候,把库中的方法拷贝到我自己的程序中了.
2.c动态库是默认提供的,gcc默认形成可执行程序,默认采用动态链接
3.动态库,动态链接地优缺点
不能丢失,也就是不能删库
节省资源
静态库,静态链接的优缺点
一旦形成,和库无关
浪费资源
优点 | 缺点 | |
---|---|---|
动态库 | 节省资源 | 不能丢失,也就是不能删库,一旦缺失,所有程序都无法运行 |
静态库 | 一旦形成,和库无关,可移植性高 | 浪费资源,静态链接生成程序体积大 |
gcc编译默认生成动态库
如果要静态链接,输入指令( 比动态多-static)
gcc he.c -o he.exe -static
会出现下面这个情况,默认情况,linux一般静态库都是没有安装的
静态库安装(c语言)
sudo yum install glibc-static
静态库安装(c++)
sudo yum install -y libstdc++-static
gcc he.c -o he3.exe -static//静态链接指令
静态比动态大好多,是动态一百倍.
但是如果我们向别人发送一个可执行程序时,对方没有安装对应的标准库,动态库对对方来说是编不了的,虽然静态库大,但是他会把库里面的方法都拷贝你的可执行程序中,这样就可以在别人的电脑里跑了,这也是静态链接产生的程序为什么容量大的原因了