目录
1. gcc编译
1.1 gcc编译过程
gcc在执行编译工作的时,分为以下四个过程及作用:
1.预处理:生成.i的文件
2.编译: 将预处理后的文件转换成汇编语言,生成.s文件
3.汇编: 变为目标代码(机器代码),生成.o的文件
4.连接: 目标代码,生成可执行程序
*****
综上可以在编译时直接执行一条命令
gcc hello.c -o hello //-o的意思是给生成的可执行文件起别名
gcc hello.c //默认编译出以a.out为命名的可执行文件 ./a.out 可以执行可执行文件
****
拓展:
链接的主要作用:
- 链接库文件
- 数据段合并
- 数据回填
1.2 gcc常用参数
-I 指定头文件所在目录位置
gcc -I ./hellodir hello.c -o hello(其中头文件hello.h在文件夹hellodir中)
-c 只做预处理,编译,汇编。得到二进制文件
-g 编译时添加调试文件,用于gdb调试
-Wall 显示所有警告信息 gcc hello.c -o hello -Wall
-D 向程序中“动态”注册宏定义 gcc hello.c -o hello -D MAX=11
程序hello.c中的MAX编译时赋值
-l 指定动态库库名
-L 指定动态库路径
2. 静态库和动态库
静态库在文件中静态展开,所以有多少文件就展开多少次,非常吃内存,100M展开100次,就是1G,但是这样的好处就是静态加载的速度快
使用动态库会将动态库加载到内存,10个文件也只需要加载一次,然后这些文件用到库的时候临时去加载,速度慢一些,但是很省内存
动态库和静态库各有优劣,根据实际情况合理选用即可。但由于计算机的发展,速度可忽略了,使用动态库更多
2.1 静态库
静态库名字以lib开头,以.a结尾
例如:libmylib.a
2.1.1制作静态库:
原文链接:https://blog.csdn.net/zhaojiazb/article/details/129247397
2.2 动态库
- 机制:代码共享(在程序运行时将程序用到的动态库的函数动态加载到内存中)
- 优点:节省内存,易于更新(动态链接)(这两点也是使用场景的原因)
- 缺点:函数调用速度慢
2.2.1 制作动态库
假设有两个文件add.c sub.c需要制作动态库 libmath.so, 然后链接制作的动态库编译hello.c (其中 调用了add 和sub)。
add.c
int add(int a,int b){
return a+b;
}
sub.c
int sub(int a,int b){
return a-b;
}
主函数 test.c
1 # include<stdio.h>
2 # include<stdlib.h>
3 # include<string.h>
4 # include<unistd.h>
5 # include<pthread.h>
6 # include"mymath.h"
7
8 int main() {
9 int a = 15;
10 int b = 5;
11 printf("%d - %d = %d\n",a,b,sub(a,b));
12 printf("%d / %d = %d\n",a,b,div1(a,b));
13 printf("%d + %d = %d\n",a,b,add(a,b));
14 return 0;
15 }
头文件在INC目录下创建ymath.h
1 #ifndef _MYMATH_H_
2 #define _MYMATH_H_
3
4 int add(int,int);
5 int sub(int,int);
6 int div1(int,int);
7
8 #endif
制作过程如下:
四步骤
1.首先 生成位置无关的.o文件 -fPIC 生产动态库的目标文件(.o文件)必须使用该参数
gcc -c add.c sub.c -o add.o sub.o -fPIC
2.其次 使用gcc -shared制作动态库 lib库名.so
gcc -shared -o libmath.so add.o sub.o
3.然后 编译可执行程序时指定所使用的动态库。
-L:指定库路径
-l:指定库名(库名是不带lib和.so的)
-I(大写i)指定头文件所在目录位置
gcc test.c -o a.out -l mymath -L ./lib -l math -I ./incl
4.最后 运行可执行程序
但是这么执行会报错需要,动态链接器(ld-linux.so.2)没有指定的动态库路径(自己制作的动态库的路径)
动态链接器(ld-linux.so.2):在程序运行之后,辅助加载器将动态库加载到内存,这也是动态库节省内存的原因
解决方式:如下修改环境变量配置文件
2.2.2 动态库程序运行修改环境变量配置文件
解决方式【1】 通过环境变量: export LD_LIBRARY_PATH=动态库路径
./a.out 成功!!! (临时生效, 终端重启环境变量失效)
失效的原因是 一个程序会生成一个地址空间,环境变量也在这个空间中,一旦终端关闭,程序失效,地址空间也没了
解决方式【2】 永久生效: 写入 终端配置文件。 .bashrc 建议使用绝对路径。
- 1) vi ~/.bashrc
- 2) 写入 export LD_LIBRARY_PATH=动态库路径 保存
- 3). .bashrc/ source .bashrc / 重启终端 ---> 让修改后的.bashrc生效
- 4)./a.out 成功!!!
解决方式【3】 拷贝自定义动态库 到 /lib(标准C库所在目录位置)
解决方式【4】 配置文件法
- sudo vi /etc/ld.so.conf
- 写入 动态库绝对路径 保存
- sudo ldconfig -v 使配置文件生效。
- ./a.out 成功!!!--- 使用ldd a.out 查看
3.makefile
命名:只能是makefile 或 Makefile --- make 命令执行该文件内容.
makefile 需要掌握:
- 1个规则
- 2个函数
- 3个自动变量
3.1 1个规则
目标:依赖条件
(一个tab缩进)命令
test:test.o add.o sub.o div1.o #目标文件test依赖test.o add.o sub.o div1.o编译
gcc test.o add.o sub.o div1.o -o test
test.o:test.c #test.o依赖于test.c编译
gcc -c test.c -o test.o
add.o:add.c #add.o依赖于add.c编译
gcc -c add.c -o add.o
sub.o:sub.c #sub.o依赖于sub.c编译
gcc -c sub.c -o sub.o
div1.o:div1.c
gcc -c div1.c -o div1.o
该makefile文件路径下make编译之后执行./test
ALL
使用ALL可以最后写文件终极目标的编译
ALL指定终极目标(也就是最终要编译出的文件)
ALL:test #指定终极目标test
test.o:test.c
gcc -c test.c -o test.o
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
div1.o:div1.c
gcc -c div1.c -o div1.o
test:test.o add.o sub.o div1.o #放在最后编写
gcc test.o add.o sub.o div1.o -o test
**********
makefile编译(make)时规则:
- 目标的时间必须晚于依赖条件的时间,否则,更新目标。
- 依赖条件如果不存在,找寻新的规则去产生依赖条件。
修改文件后,文件的修改时间发生变化,会出现目标文件的时间早于作为依赖材料的时间,出现这种情况的文件会重新编译。
例如上面编译后再修改sub.c文件后,sub.o的时间就早于sub.c ,a.out的时间也早于sub.o的时间了,于是重新编译这俩文件了。
**********
3.2 2个函数
src = $(wildcard *.c) $( )代表调用括号内函数的意思
找到当前目录下所有后缀为.c的文件,将文件组成列表赋值给src 例如src=sub.c add.c
obj = $(patsubst %.c,%.o, $(src))
把src变量里所有后缀为.c的文件替换成.o 例如 obj = sub.o add.o
例:
src = $(wildcard *.c) #找到当前目录下所有后缀为.c的文件,将文件组成列表赋值给src
obj = $(patsubst %.c,%.o, $(src)) #把src变量里所有后缀为.c的文件替换成.o
ALL:test
test.o:test.c
gcc -c test.c -o test.o
add.o:add.c
gcc -c add.c -o add.o
sub.o:sub.c
gcc -c sub.c -o sub.o
div1.o:div1.c
gcc -c div1.c -o div1.o
test:$(obj) #$(obj)属于自定义变量 意思是使用obj
gcc $(obj) -o test
clean: #删除.o 文件
-rm -rf $(obj) test
#rm前面的-,代表出错依然执行。
#比如,待删除文件集合是5个,已经手动删除了1个,就只剩下4个
#然而删除命令里面还是5个的集合,就会有删除不存在文件的问题
#不加这-,就会报错,告诉你有一个文件找不到。
#加了-就不会因为这个报错。
3.3 3个自动变量
- $@: 在规则命令中,表示规则中的目标
- $< : 在规则命令中,表示规则中的第一个条件,如果将该变量用在模式规则中,它可以将依赖条件列表中的依赖依次取出,套用模式规则
- $^ : 在规则命令中,表示规则中的所有条件,组成一个列表,以空格隔开,如果这个列表中有重复项,则去重
模式识别
这是的makefile就是一个自动化的了,增加.c文件也不需要更改makefile。
src = $(wildcard *.c) #获取所有.c文件名称 赋值给src
obj = $(patsubst %.c,%.o, $(src)) #src改为.o结尾
ALL:test
%.o:%.c
gcc -c $< -o $@ #$<代表所有依赖文件即上面的冒号右边的%.c(src)每次取出对应的一个依赖文件
#$@代表所有目标文件即冒号左边的%.o(obj)
test:$(obj)
gcc $^ -o $@ # $^ 等价于冒号右边的$(obj),组成一个列表,以空格隔开,自动去重
clean:
-rm -rf $(obj) test
静态模式识别
静态模式识别只能将$(obj)中.o编译出来如果还有其他的obj不会被编译
src = $(wildcard *.c) #获取所有.c文件名称 赋值给src
obj = $(patsubst %.c,%.o, $(src)) #src改为.o结尾
ALL:test
myArgs = -Wall -g #gcc编译的参数
$(obj):%.o:%.c
gcc -c $< -o $@ $(myArgs)
#$<代表所有依赖文件即上面的冒号右边的%.c(src)每次取出对应的一个依赖文件
#$@代表所有目标文件即冒号左边的%.o(obj)
test:$(obj)
gcc $^ -o $@ $(myArgs)
# $^ 等价于冒号右边的$(obj),组成一个列表,以空格隔开,自动去重
clean:
-rm -rf $(obj) test
makefile文件
****************定义变量 beg****************
src = $(wildcard *.c) #获取所有.c文件名称 赋值给src
obj = $(patsubst %.c,%.o, $(src)) #src改为.o结尾
myArgs = -Wall -g #gcc编译的参数
CC = gcc
Target = test
****************定义变量 end****************
****规则语句 程序命令 并且引用变量 beg****
ALL:$(Target)
$(obj):%.o:%.c
$(CC) -c $< -o $@ $(myArgs)
#$<代表所有依赖文件即上面的冒号右边的%.c(src)每次取出对应的一个依赖文件
#$@代表所有目标文件即冒号左边的%.o(obj)
test:$(obj)
$(CC) $^ -o $@ $(myArgs)
# $^ 等价于冒号右边的$(obj),组成一个列表,以空格隔开,自动去重
clean:
-rm -rf $(obj) $(Target)
****规则语句 程序命令 并且引用变量 end****
.PHONY: clean ALL
测试 makefile clean 命令
make clean -n
特别情况makefile名称不是makefile或Makefile,执行命令
make -f m6