用C编译器编译源文件:gcc 源文件 -o 可执行文件名
详细步骤:
gcc -E a.c -o a.i
预处理器将头文件展开,宏替换,去掉注释gcc -S a.i -o a.s
编译器将C文件变成汇编文件gcc -c a.s -o a.o
汇编器将会变文件变成二进制文件gcc a.o -o a
链接器进行链接
ESc+链接
如果不使用参数-o
则自动生成a.out
制定头文件的路径gcc a.c -I 头文件的目录 -o 生成可执行文件的名字
在比较旧的版本上-I和头文件目录之间不能有空格,现版本无所谓
指定宏-D 宏名
优化-O1/O2/O3
O3优化速度最高
输出警告信息-Wall
生成调试信息-g
静态库
- 命名规则
lib+库的名字+.a
- 制作步骤
生成对应的.o
文件,直接使用参数-c
得到.o
文件,记得不要使用-o
参数,否则将会直接生成可执行文件
将生成的.o
文件进行打包,需要使用软件ar
将.o
打包为.a
ar rcs 静态库名称 所有的.o
- 发布和使用静态库
打包静态库和头文件,将所有的静态库放到lib文件夹中,将所有的头文件放到include文件夹中,制作好静态库以后可以发送这两个文件夹。
用户通过头文件知道有哪些函数接口。通过直接和源文件编译静态库使用。
gcc 源文件 静态库 -o 可执行文件 -Iinclude
gcc 源文件 -Iinclude -L lib -l 静态库文件名(去掉头部lib和尾部.a) -o 可执行文件
- 静态库的优缺点
nm 静态库文件 //查看静态库
打包的最小单元为.o
优点:
- 库已经打包在程序中,不需要再提供
- 库的加载速度比较快
缺点: - 可执行文件会比较大
- 如果库发生了改变需要重新编译
动态库/共享库
- 命名规则
lib+名字+.so - 制作步骤
(1) 生成与位置无关的代码(.o)
gcc -fPIC -c *.c -Iinlcude
(2) 将.o打包成共享库
Linux每一个运行的程序操作系统都会为其分配一个0-4G的地址空间。Linux下可执行文件格式:ELF
gcc -shared -o libname.so *.o -Iinclude
再讲lib中的.so文件和include中的头文件发送给用户
- 用户使用
gcc main.c lib/libname.so -o main -Iinclude
gcc main.c -Iinclude -L lib -l name -o main//需要注意的是这里的name是不包含lib和后缀.so的
使用ldd
查看可执行文件所依赖的所有的共享库的名字
动态库的使用需要动态链接器帮助。我们需要让动态链接器找到我们自己的动态链接库
正常情况下使用会出现动态库无法找到的问题。然后就需要我们去解决。
可是我自己尝试的时候没有遇到这个情况。。。我也不清楚为什么,可能是现在版本的gcc已经智能地解决这个问题了。
(1)将动态库放到系统的lib文件夹中
(2) 配置环境变量LD_LIBRARY_PATH
(如果你的动态库没有在默认的环境变量中,会先在这个环境变量中查找)
用于临时测试
echo $LD_LIBRARY_PATH //$从环境变量中取值 查看环境变量
export LD_LIBRARY_PATH=./lib //将当前目录下的lib文件夹导入到环境变量中
关掉终端后失效
(3)
ls -a
vi .bashrc
在里面加上export LD_LIBRARY_PATH=动态库目录
重启终端
永久
(4)工作中更加常用的方法
-
找到动态链接器的配置文件
/etc/ld.so.conf
需要使用管理员权限 -
将动态库的路径写到配置文件中
-
更新
sudo ldconfig -v
-v是提示信息
动态库没有加载到源文件中,而是在需要使用的位置加上了一个标记,在使用的时候才进行访问。
优点:
- 执行程序很小
- 动态库更新方便
缺点: - 需要将动态库发布给用户
- 加载库的时候速度较慢
gdb 调试
l
默认展示包含main()的文件l 文件名:行号
展示以行号为中心上下文件的内容l 文件名:函数名
展示文件中的函数,输入l
继续展示后面的内容,然后后面再直接按回车,会继续向下展示文件,一次展示10行break 行号
在某一行打断点b 行号
b 行号 if 条件
条件断点,条件断点只能设置在循环内部,如果设置在边界不会停止b filename:行号
在某个文件的某一行设置断点info break
i b
查看断点信息start
gdb开始运行程序,每次运行一行,n
单步调试,c
继续执行到断点s
下一步,会进入函数体内部(step)n
下一步,不会进入函数内部run
直接运行到断点,如果没有断点直接运行结束p 变量
展示某个变量的值ptype 变量
展示变量的类型display 变量
追踪某个变量的值undisplay 变量编号
取消追踪某个变量info display
打印所有追踪变量的信息u
将循环运行结束,结束循环finish
跳出当前函数,需要将函数中的断点消除d 断点编号
删除断点(del)set var 变量=x
直接运行到变量为x的时候quit
退出gdb
makefile
makefile
项目管理工具,用于管理源代码
简单makefile文件
-
命名规则
(1)makefile
(2)Makefile
-
编写规则
makefile 一般情况下要和.c
文件在一个文件夹中,如果不在需要绝对路径
三要素:
目标:依赖 //目标就是想要生成文件的名字 依赖:`.c`文件命令(必须要有Tab缩进):gcc a.c b.c c.c -o 目标
- 使用
make
进阶makefile文件
当一些文件修改以后需要重新编译,为了解决这个问题:
例如:
app:main.o add.o sub.o mul.o //终极目标,默认第一条语句是终极目标gcc main.o add.o sub.o mul.o -o app
//如果依赖中的文件没有发现,就去下面的子目标中查找有没有相关的规则用于生成文件
main.o:main.c gcc main.c -c
add.o:add.cgcc add.c -c
sub.o:sub.cgcc sub.c -c
mul.o:mul.cgcc mul.c -c
上面的写法会自动查找文件是否修改,如果没有修改就不会编译,从而提高效率
工作原理:通过比较修改时间,目标应该比依赖的修改时间迟,如果发现目标比依赖的修改时间早则重新生成目标。
进进阶makefile文件
上面的写法有些冗余,通过变量来将写法变得简洁
obj=main.o add.o sub.o mul.o
target=app
$(target):$(obj) //$取obj变量中的值gcc $^ -o $@
//模式规则
%.o:%.cgcc -c $< -o &@
makefile中的自动变量:
$<
规则中的第一个依赖$@
规则中的目标$^
规则中的所有依赖- 只能够在规则中的命令来使用
makefile自己维护的变量: - 都是大写,例如
CC,CPPFLAGS,CFLAGS,LDFLAGS
进进进阶makefile文件
在makefile中的函数都是有返回值的
- 获取指定目录下所有的
.c
文件
src=$(wildcard 所需要查找的目录/*.c)
- 获取指定目录下所有的
.o
文件
obj=$(patsubst ./%.c , ./%.o , $(src)) #模式匹配
- 自动删除以前生成的目标文件
clean:rm $(target) -f #如果不存在也会强制删除,不会弹出提示信息
生成文件 时候:
make clean #会删除目标文件,只会执行生成clean的语句
如果目录中真的存在clean
目标,则会提示目标是最新的而不会运行。
.PHONY:clean #声明clean为伪目标,不会与目录中的目标进行比较
在命令前面加上-
,则命令执行失败以后也不会停下来,继续向后执行