Linux是用C写的,提供的很多接口也是C的
1.编译原理
编译型语言,安装开发包,也会下载安装对应的头文件、库文件等
我们为什么能够在windows或者Linux上或者其他形式的开发呢?因为系统中提前或后续装上C/C++开发相关的头文件、库文件
C/C++开发环境不仅仅指的是vs, gcc, g++,更重要的是,语言本身的头文件、库文件;在安装VS2022等的过程中,选择对应的开发包,同步下载c的头文件和库文件,也在windows上存在,C++标准库头文件在Windows下路径(VS2022)
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.44.35207\include
1.1 预处理
gcc -E test.c -o test.i -E是说从现在开始进行程序的翻译,进行完预处理就停止,指令也可以是这样gcc -E -o test.i test.c,-o后面跟的是存放预处理结果的文件名,如果没有,直接打在终端上

预处理做四个事情
-
头文件展开
头文件只是类和方法的声明,在库里定义
Linux下的头文件

-
宏替换
-
条件编译
条件编译的应用场景,比如我们下载软件的时候有Community和Profession版本,提供软件的公司并不是维护两份代码,而是根据用户不同身份进行条件编译,比如裁剪掉社区版不配备的功能,包括是否提供VIP服务等
-
去注释

gcc -E test.c -o test.i -DDEBUG 因为预处理是可以对.c文件做修改的,所以在指令中加入宏定义也可以

亦或是在.c文件直接定义


预处理过后的文件还是C语言的
1.2 编译(生成汇编)
语法检查主要是在编译的时候做的,预处理进行宏替换等,进行少量的语法检查

gcc -S test.c -o test.s 可以从.c文件开始从头进行预处理、编译;-S是说从现在开始进行程序的翻译,进行完编译停止
gcc -S test.i -o test.s 也可以从.i文件直接进行编译

1.3 汇编
生成可重定位目标二进制文件,.obj文件但不可独立执行
gcc -c test.s -o test.o -c是说从现在开始进行程序的翻译,进行完汇编工作就停下来打开是乱码,因为vim是文本编辑器

可以通过od进行二进制文件查看,虽然我也看不懂O(∩_∩)O哈哈~

1.4 链接
将汇编生成的可重定位二进制目标文件和库文件(提供方法实现)链接起来,形成可执行程序
库文件把源文件(.c)经过一定的翻译,打包,只给开发者提供一个文件,也可以达到隐藏源文件的目的;我们可以站在巨人的肩膀上,不再做重复工作,做一个"CV"工程师嘿嘿(* ^ ▽^ *)
那么我们最后的程序=自己编写的代码+头文件+库文件
C语言标准库本质是一个文件,库有自己的命名规则,libname.so.xxx,除了name,其它都是前后缀
文件名后缀如下表所示

分为两种,动态链接和静态链接,默认是动态链接优先,如果没有动态库,进行静态链接,加-static选项是指定进行静态链接,如果没有静态库,无法链接
默认是没有安装静态库的

普通用户需要进行sudo提权才能安装静态库
但前提是普通用户在sudoers中,如果不在,测试如下

配置文件路径及vim打开指令如下,vim /etc/sudoers,给普通用户配置和root一样的如下权限即可

因为sudoers文件root只有读权限,进行强制写
之后安装静态库
sudo yum install -y glibc-static 安装C静态库
sudo yum install -y libstdc++-static 安装C++静态库
ls /usr/lib64/libc.a* 静态库查询
查系统中的动态库,系统中默认支持动态库
ls /usr/lib64/libc.so* 动态库查询

ls /usr/lib64/libc*

ldd 查询可执行文件依赖的动态库

gcc test.o -o test_static -static 加-static进行静态编译,静态编译得到可执行程序体积几乎是动态编译得到可执行程序体积的100倍,因为静态编译是把程序中用到的方法实现直接从库文件拷贝到可执行文件中,因为磁盘空间是有限的,而且程序运行要加载到内存,并且进行网络通信时,体积较大影响效率
好处是不依赖于库,程序链接完成后,即使有一天库丢失了,不影响程序

如果是动态链接,因为里面给到的是编译器中的链接器给到的库文件的链接,在链接的时候直接去找库就行了,之后回到调用处接着往下运行,所以生成的可执行程序体积较小,节省资源,缺点就是,依赖动态库,因为动态库被很多程序所依赖,所以动态库也是共享库,如果有一天库被删了或者出故障了,程序也不能执行了
但是可执行程序并不是完全静态或动态,可以混合链接
gcc test.c -o test_debug -g默认是release,加-g选项是debug,debug模式程序可以被追踪调试,添加了debug信息,我们看到debug比release模式得到的可执行程序体积大一些

file 查动态链接使用的库

readelf -S test 读取可执行程序对应二进制构成

-i 忽略大小写

可执行程序形成的时候不是杂乱无章的,有自己的二进制格式------ELF格式,与编译原理有关,推荐《程序员的自我修养》一书或者《深入理解计算机系统》进行深究
如果是C++文件,将gcc改为g++即可

2. make/makefile
2.1 概念
项目自动化构建工具
因为如果我们有很多.c源文件,每次编译都要用指令以及删除生成的可执行文件很麻烦,所以用makefile进行管理
make是一条指令
makefile/Makefile(首字母不区分大小写) 是一个当前目录下的文件
用于测试的makefile文件内容如下
cpp
1 test.exe:test.cpp//依赖关系,test.exe是依赖于test.cpp的
2 g++ -o test.exe test.cpp //依赖方法,根据依赖方法形成可执行程序,tab开头,而不是四个空格
3 clean://不依赖于任何文件
4 rm -f test.exe
make命令,系统会在当前目录找到makefile文件,执行预制的编译方法,形成可执行程序


默认不可以进行多次make,但可以进行多次make clean

我们来看个详细版的makefile
cpp
1 test.exe:test.cpp
2 g++ -E test.cpp -o test.i
3 g++ -S test.i -o test.s
4 g++ -c test.s -o test.o
5 g++ test.o -o test
6 clean:
7 rm -f test.i test.s test.o test
make会根据依赖关系和依赖方法一步一步往下执行
因为makefile第一个目标文件是test,所以make默认执行的是test,但是和make test执行的结果不尽相同,make test直接把详细步骤合并,一步到位,只生成目标文件test
但是make就是按照test的依赖关系、依赖方法进行执行并生成过程文件

但是make test执行后,make还可以执行,因为make要生成的过程文件.i, .s, .o都不存在;如果make执行后,make test就不能执行了,因为make test要生成的test已经存在了,而且test.cpp没更新,不需要编译

如果顺序颠倒了,我的机子会报错
cpp
1 test.exe:test.cpp
2 g++ -c test.s -o test.o
3 g++ -S test.i -o test.s
4 g++ -E test.cpp -o test.i
5 g++ test.o -o test
6 clean:
7 rm -f test.i test.s test.o test

在命令行这个键会出现上一次使用的指令

按下键取消
如果clean放在最前面,命令行输入make默认执行clean
cpp
1 clean:
2 rm -f test.i test.s test.o test
3
4 test.exe:test.cpp
5 g++ -E test.cpp -o test.i
6 g++ -S test.i -o test.s
7 g++ -c test.s -o test.o
8 g++ test.o -o test

有没有什么办法可以多次make编译呢?加入.PHONY: 目标文件名称,测试结果如下
cpp
1 .PHONY:test//声明是伪目标,不依赖任何文件
2 test:test.cpp
3 g++ -E test.cpp -o test.i
4 g++ -S test.i -o test.s
5 g++ -c test.s -o test.o
6 g++ test.o -o test
7
8 clean:
9 rm -f test.i test.s test.o test

但是不推荐这样写,因为一般情况下是倒逼着去做清理,有些编译器二次编译的时候可能不是重新编译,而是把原来的可执行文件进行增添等,这样原来错的还在,所以不方便我们使用,因此清理完原有可执行程序再编译,是好习惯。
那么我们来探讨一下,为什么默认情况下源文件编译后生成可执行程序,如果源文件不修改,就不允许再编译呢?显然是没有必要,再编译生成的还是一模一样的可执行程序,系统直接不允许,从而提高效率,因为有的程序可能成百万行代码,一跑要几分钟几十分钟等
2.2 stat/touch
那么是怎么做到的呢?答案无疑是时间,如果源文件最近一次修改的时间先于可执行程序最近一次修改的时间,说明源文件在可执行程序生成后没有更改,没有再次编译的必要;但如果源文件最近一次修改的时间后于可执行程序最近一次修改的时间,说明源文件在可执行程序生成后发生更改,有再次编译的必要
stat 文件名,可以查看文件被access(访问)、modify(修改内容)、change(修改文件属性)的时间

也可以手动更改文件amc的时间,touch 已存在文件名可以做到,并且加选项-a/-m/-c可以更改文件a或m或c的时间
但是-a的时候修改访问时间,但是文件属性中最近一次访问时间变了,所以文件属性改变change的时间也变了;-m和-c的时候三个时间都改了

如果更改文件属性,变的只有change时间

打开文件不做修改,三个时间都变了

2.3 特殊符号
cpp
1 test:test.cpp
2 g++ -o $@ $^
3
4 clean:
5 rm -f test
@是目标文件,冒号左侧内容; ^是依赖文件,冒号右侧内容
make时会自动补齐二者对应的内容

如果在依赖关系处加上@,可以不回显具体执行的依赖关系
cpp
1 test:test.cpp
2 @g++ -o $@ $^
3
4 clean:
5 @rm -f test

makefile的注释用·#