文章目录
- 一.引例:
- C语言
- C++
- 二.程序翻译的过程
- 预处理
- 条件编译
- 编译
- 汇编
- 链接
- 三.链接--动静态链接
- 链接是什么?
- 动静态库
- 为什么要有库?
- 怎么办?
- 证明:
- 优缺点
- 静态链接的应用场景
- 四.make/makefile
- 原理:
- 为什么makefile对最新的可执行程序,默认不想重新生成呢?
- makefile怎么知道我的程序需要被编译了呢?
- makefile中的内置符号
一.引例:
C语言
下面代码可能编不过去
因为编译器的版本没达到,需要手动改版本
默认形成的可执行文件是a.out
也可以更改形成的文件名-o 是object的意思,后缀随便加
gcc test.c -o my.exe -std=c99
C++
C++文件后缀不仅仅有.cpp和.cc,还有很少见到的.cxx
gcc不能编译C++的代码,但g++能编译C的代码
g++ test.cpp -o my.exe -std=c++11
二.程序翻译的过程
预处理
预处理把头文件直接展开,10来行变成800多行
Linux里面有库才能使用头文件,并且预处理之后文件展开的位置能之间看到库存在哪
预处理阶段宏会被替换,注释也被去掉
条件编译
引例:同样一种软件,有社区版,专业版,免费版。
那么维护代码的时候需要维护几份源代码?
维护一份代码即可,怎么做到的?
以以下代码为例
运行就会发现只打印了部分
预处理就被裁了
然后再做一下改变V1随便给个值
#include <stdio.h> 2 #define V1 1 3 4 int main() 5 { 6 #ifdef V1 7 printf("功能1\n"); 8 #elif V2 9 printf("功能1\n"); 10 printf("功能2\n"); 11 #else 12 printf("功能1\n"); 13 printf("功能2\n"); 14 printf("功能3\n"); 15 #endif 16 return 0; 17 }
运行
其中#ifdef #elif 等就是条件编译
这样裁剪可以定义出不同版本的软件
宏也可以在外面定义
gcc -D V1=1 proj.c -o filename
编译
gcc -S test.i -o test.s -std=c99
很明显,就是汇编语言
汇编
gcc -c test.s -o test.o
就是.obj结尾的文件
形成二进制的目标文件
还是运行不了
链接
gcc test.o -o my.exe
就可以直接运行了
三.链接–动静态链接
链接是什么?
程序与库结合的过程
引例:从零开始写代码,假设设计一个printf函数,设计好了大半个月过去了,效率比较低,所以语言设计者把一些公共的方法抽取出来,放进自己的标准库
下面的指令是查看文件掉用了哪些动态库
ldd filename
查看画横线的文件,没形成C语言库之前就是C语言的源代码,被顶级程序员打包成库
我们怎么知道C标准库里面有什么?所以就有一批对应的头文件,方便调用
所以安装开发环境:安装C标准库 + C头文件
动静态库
为什么要有库?
让个开发站在巨人的肩膀
提高开发效率
怎么办?
故事:
故事来到第二阶段:张三去不了网吧了(无法调用动态库),计划不能完整的执行了
第三阶段:
总结:所有动态链接都需要跑到库中执行,然后返回,静态链接不需要
证明:
C动态库,是默认提供的
gcc默认形成的可执行程序,默认采用动态链接
ls命令也是同理
要是删掉的话,就相当于上述故事中的警察查封网吧,大部分指令都运行不了了
静态库的位置,以.a结尾
优缺点
下面的指令是查看文件详细信息
file filename
动态库与动态链接的优缺点:
1.不能丢失(网吧不能去了就玩不了)
2.节省资源(网吧的电脑共用)
静态库与静态库的优缺点:
1.一旦形成就与库无关(自己有电脑就不用去网吧了)
2.浪费资源(自己的电脑自己用)
验证:
查看静态库的指令(mytest-static是形成test.c静态库的名称,名字随便取)
gcc -o mytest-static test.c -static -std=c99
会发现运行不了,找不到C语言的静态库
是因为默认情况下,Linux上一般静态库都是默认没有安装的
yum安装即可
sudo yum install -y glibc-static libstdc++-static
对比一下:
文件大小上静态比动态大很多
静态链接的应用场景
提高可移植性,比如直接把二进制代码从这个A机器到B机器,跨平台性提高,不依赖动态库,不用做过多的环境监测
四.make/makefile
原理:
make是一个命令
makefile是一个文件
创建makefile和创建普通文件的命令是一样的
touch makefile
以下代码为例
效果就是下面
关于依赖关系和依赖方法的理解,这里讲一个小故事:
月底了,身为大学生的你没钱了,拨通了父亲的电话:我是你儿子,然后你挂了电话。
我是你儿子这句话就表明了依赖关系,但是目的没达成,所以只有依赖关系没用
然后又打通电话:我是你儿子,没钱了打钱。你父亲就明白了
这就是表明了依赖关系又表明了依赖方法
发现在前面使用的时候,make是运行的第一段,make clean是第二段,同时make mytest也可以运行第一段
因为make是从上面向下运行的
把makefile反过来验证一下:
至于.PHONY是什么意思这里讲解一下:
以下面的makefile文件为例
mytest:test.cgcc -o mytest test.c
clean:rm -f mytest
会发现第二次make的时候会说:此文件已经是最新的
但如果更改test.c的内容后,就可以再次make。
因为系统认为test.c已经编译过了,且没有最新的更改,所以没必要执行。
加了.PHONY后就可以一直执行
.PHONY:mytest
mytest:test.cgcc -o mytest test.c
clean:rm -f mytest
.PHONY:XXX
表示XXX对应的方法,总要被执行的
一般写makefile的时候,形成可执行程序,源代码没有更新,没有必要编译
清理项目希望是被总是执行的,因为有些文件想要清理干净
为什么makefile对最新的可执行程序,默认不想重新生成呢?
假设一个项目有两千个源文件,很小的一部分需要做改动,如果全部编译效率很慢。
提高编译效率
makefile怎么知道我的程序需要被编译了呢?
源文件的更改时间和可执行程序更改的时间是不一样的
源文件:
可执行文件:
对比可执行文件的最近修改时间和源文件最近的修改时间,判断谁最新?
以此来判断是否需要编译文件
.PHONY的意思是直接编,不用比较
平时在写VS的时候,有时候会出错,清理重新编译后,就可以执行。原因就是不同的环境更新时间的策略不一样。
makefile中的内置符号
make的时候,他会自动替换目标文件
在makefile中注释是#
多个指令的实现也可以
引例: