本文内容大部分出自对传智播客linux课程内容的总结和课堂笔记。

1. gdb调试

1.1 使用场景

  • 程序编译无误,但是有逻辑错误
  • 使用文字终端(shell),实现一个单步调试的功能
  • 生成可执行文件之前必须加参数 -g

    • gcc hello.c -o hello -g

      1.2 启动调试

  • gdb + 可执行文件 (如:gdb hello

    1.3 查看代码

  • l — list

  • l : 默认打开main函数所在文件,显示前10行
  • l n``[或函数名] : 显示当前文件的第n行 [ 或某个函数 ]
    • 如果未打开其他文件,则默认当前文件为main函数所在文件
  • l filename:n[或函数名] : 显示文件filename的第n行 [ 或某个函数 ]

    • Enter(默认执行上次操作)可以一直显示下去直到文件末尾

      1.4 设置断点:

  • b — break

  • 设置当前文件断点:
    • b 行号 [ 或函数名 ]
  • 设置指定文件断点:
    • b fileName:行号 [ 或函数名 ]
  • 设置条件断点:
    • b 10 if value==19
    • 当value=19时,停止
  • 查看断点号:
    • info break [ i b ]
  • 删除指定断点号对应的断点:
    • d(delete/del)+ 断点号
  • 设置断点是否有效

    • disable/enable + 断点号

      1.5 开始执行gdb调试

  • r(run) —> 运行程序

  • start —> 单步执行,运行程序,停在第一行执行语句
  • n(next) —> 下一行(不会进入到函数体内部)
  • s(step) —> 下一步(会进入到函数体内部)
  • c(continue) —> 直接停在断点的位置
  • finish —> 结束当前函数,返回到函数调用点

    • 注意:必须当前函数的循环中没有断点了,否则会失败

      1.6 查看(打印)变量的值

  • p(print) + 变量名

    1.7 查看变量的类型

  • ptyte + 变量名

    1.8 设置变量的值

  • set var 变量名 = 赋值

  • 作用:比如可以通过给循环控制变量i设置某个值,即可查看循环在i为某个值的执行状态

    1.9 设置跟踪变量

  • 追踪变量:display + 变量名

    • 之后每执行一步,该变量值都会被打印出来
  • 取消跟踪:undisplay + 变量名编号

    1.10 退出gdb调试

  • q(quit)

    2. makefile的编写

    本部分内容只是最基础的makefile的知识,如果有扩展学习的需要,可以参考徐海兵老师翻译整理的GNU make中文手册点此下载

2.1 文件的命名规则

  • Makefile
  • makefile

    2.2 用途

  • 项目代码编译管理

  • 节省编译项目的时间
  • 一次编写终身受益

    2.3 基本规则

  • 三要素:目标、依赖、命令

  • 格式:
    • image.png
    • 目标 —> 要生成的目标文件
    • 依赖 —> 生成目标文件需要的一些文件
    • 命令 —> 借助依赖文件生成目标文件的手段
    • tab —> 缩进,有且只有一个
  • 其他语法:
    • **#**是注释
    • **.PHONY**定义伪目标
      • 例子:image.png
      • 执行伪目标:make clearmake clearall
  • Makefile会把规则中的第一个目标作为终极目标
    • app:all —> all指定生成的最终目标为app

image.png

2.4 工作原理

  • 若想生成目标,检查规则中的依赖条件是否存在,如果不存在,寻找是否有规则用来生成该依赖文件
  • 检查规则中的目标是否需要更新,必须检查它的所有依赖,依赖中有任意一个被更新,则目标必须更新
    • 依赖文件比目标文件时间晚,则需要更新
  • 生成终极目标的过程:

image.png
image.png

  • 更新目标的工作原理:
    • 检查目标和依赖的修改时间,若依赖比目标新,则执行命令更新目标文件

image.png

2.5 执行

  • ma``ke —> 通过makefile生成终极目标
    • 直接 make,使用makefile文件
  • make -f mm
    • 指定一个名字不为makefile的文件
  • make clean —> 清除编译生成的中间.o文件和最终目标文件

    • 不生成终极目标,而执行makefile中的目标对应命令
    • 如果当前目录下有同名clean文件,则不执行clean对应的命令
      • image.png
      • 因为在执行make clean时,并不真正生成clean文件,由更新目标的工作原理可知,假如已经存在最新的目标文件,则不执行其命令,因此一旦目录下有同名的clean文件,那么在执行make clean时便会认为已经存在最新的目标文件,而不执行相应命令。
      • 上述问题解决方案 —> 伪目标声明:.PHONY:clean
    • 特殊符号
      • - 命令 :表示此条命令出错,make也会继续执行后续的命令。
        • e.g. -rm a.o b.o 如果删除失败,则继续向下执行其他命令

          2.6 普通变量

  • 普通变量

    • 定义变量:变量 = 是替换 变量 += 是追加 变量 := 是恒等于
    • 使用变量:$( 变量名 ) 取变量值
    • 变量定义及赋值:obj = a.o b.o c.o obj += d.o CC := gcc
    • 变量取值:foo = $(obj) 即foo=a.o b.o c.o d.o
  • 由 Makefile 维护的一些变量

    • 通常格式都是大写
      • CC:默认值 cc
    • 有些有默认值,有些没有
      • CPPFLAGS : 预处理器需要的选项 如:-I
        • 例如:CPPFLAGS=-I/home/jiang/test/include/
      • LDFLAGS : 链接库使用的选项 –L -l
        • LDFLAGS=-L./lib -lmytest<br />
      • CFLAGS : 编译的时候使用的参数 –Wall –g -c
    • 用户可以修改这些变量的默认值
      • CC = gcc

        2.7 自动变量

  • 变量(通配符)

    • e.g. main.o:main.c add.c sub.c mul.c dev.c
    • $@ —> 规则中的目标 main.o
    • $< —> 规则中的第一个依赖条件 main.c
    • $^ —> 规则中的所有依赖条件 main.c add.c sub.c mul.c dev.c
    • 都是在规则中的命令中使用
  • 模式规则(隐含规则)

    • 在规则的目标定义中使用 %
    • 在规则的依赖条件中使用 %
    • %.c %.o 任意的.c 或者 .o
      • 可以理解为照葫芦画瓢,需要什么,只要格式能对上,%就变成什么
      • 比如终极目标依赖./obj/add.o,那么他就会向下查找,找到%.o:%.c后,则%=./obj/add,这条规则就变成了./obj/add.o:``./obj/add.c
      • 再比如:有一条规则./obj/%.o:./src/%.c,则目标依赖./obj/add.o向下查找依赖时,这条规则就变成了./obj/add.o:./src/add.c,具体应用可参看2.9.3例【3】。

        有关和%的疑惑可以参考:[makefile的语法及写法(补充:%和的区别)](https://blog.csdn.net/qq_37688116/article/details/68948289)

  • 示例:

    • %.o:%.c
    • $(CC) –c $< -o $@
      • $< —> 表示依次取出依赖条件
      • $@ —> 表示依次取出目标值
      • % 表示一个或者多个,任意长度的

        2.8 三个函数

  • makefile中所有的函数必须都有返回值

  • wildcard + 目录/*.后缀
    • 查找指定目录下指定类型的文件,一个参数
    • src=$(wildcard ./src/*.c)
      • 找到./src 目录下所有后缀为.c的文件,赋给变量src
  • patsubst 格式1, 格式2, 字符串
    • 匹配替换,将字符串中符合格式1的部分用格式2替换
    • obj = $(patsubst %.c ,%.o ,$(src))
      • 从src中找到所有.c 结尾的文件,并将其替换为.o
    • obj = $(patsubst ./src/%.c, ./obj/%.o, $(src))
      • 指定.o 文件存放的路径 ./obj/%.o
  • dir=$(notdir $(src))
    • 去掉src中的路径
  • 例子:假设./src中有main.c add.c sub.c mul.c dev.c等
    • src=$(wildcard ./src/*.c) —-> src=./src/main.c ./src/add.c ./src/sub.c …
    • dir=$(notdir $(src)) —-> dir=main.c add.c sub.c …
    • obj = $(patsubst ./src/%.c, ./obj/%.o ,$(src)) —-> obj=./obj/main.o ./obj/add.o ./obj/sub.o …
    • image.png

      2.9 例子

      2.9.1 例【1】

      image.png
  1. 像如下图中写的makefile【第一版本】并不好,每次只要有一个.c文件被修改时,都会把所有的.c文件重新编译,会浪费额外的时间,假如项目很大,则效率很低。

image.png

  1. 可以通过添加多条规则解决问题(如下图所示【第二版本】),其中第一条规则中的目标为终极目标,而其他规则中的目标为子目标,子目标是为了生成第一条规则中的依赖条件而存在的。

image.png

  1. 执行make,当要生成第一条规则中的终极目标时,会查找其依赖是否存在;如果不存在,则去下面其他规则中的子目标中查找,看是否有相应子目标可以生成不存在的依赖,有则先执行子目标规则的命令;如果存在,则通过对比目标和所需依赖的修改时间,若所需依赖比目标新,则更新目标文件。
  • 执行make结果如下:

image.png

  • 当只修改add.c文件后,执行make结果如下:

image.png

  1. 利用变量、隐藏规则、通配符对【第二版本】makefile中冗余的部分改进,使其更精简,生成【第三版本】如下图

    image.png

  2. 利用两个函数将【第三版本】中的obj的写法改进为【第四版本】如下图。如果一个项目很大,含有很多源文件,那么生成可执行文件时,中间就需要生成很多目标文件作为依赖,如果像之前版本中一一列出目标文件,e.g. obj=main.o a.o b.o c.o ....很麻烦,可以用查找和匹配替换命令轻松实现。

image.png

2.9.2 例【2】

  • 我的环境说明:

image.png

  • 其中已将动态库libmytest.so所在绝对路径追加入到/etc/ld.so.conf文件中
  • main.c文件中调用位于./lib目录下动态库libmytest.so中的函数add,引用位于./include头文件中的head.h

    • 我编写的makefile文件:

      1. ![image.png](https://cdn.nlark.com/yuque/0/2020/png/1912023/1597720448003-97f72f88-9ab4-487b-b610-e0bb2eec0d6b.png#align=left&display=inline&height=278&margin=%5Bobject%20Object%5D&name=image.png&originHeight=278&originWidth=461&size=21079&status=done&style=none&width=461)

      2.9.3 例【3】

  • 我的环境说明:
    • image.png
  • 问题:想要只把src中四个.c文件编译并生成到./obj目录,不生成最终的可执行文件
    • 借鉴了别人的方法,使用伪指令的方法实现
  • 我编写的makefile文件:
    • image.png

      我也是刚刚接触这个东西,自己做了两个小例子,一定不是最优的写法,但是基本上把makefile的基本语法用了一遍,而且效率绝对比写命令行要高的。

3. 简易makefile总结(分五层)

本部分内容为b站某关于makefile的课程笔记,点此查看课程 Makefile是一种脚本语言,Linux C/C++必须使用的一个编译脚本

3.1 第一层:显示规则

  • 是注释

  • 目标文件 : 依赖文件

[TAB键]指令
说明:第一个目标文件是最终目标,类似递归
例子:
image.png
执行:make

  • 伪目标:.PHONY :

例子:
image.png
执行:make clear 或 make clearall

3.2 第二层:(类似C宏定义)

  • 定义变量:变量 = 是替换 变量 += 是追加 变量 := 是恒等于
  • 使用变量:$( 变量名 ) 替换
  • 示例:

原始makefile:
image.png
使用变量替换后的makefile:
image.png

3.3 第三层:隐含规则

%.c %.o 任意的.c 或者 .o
.c .o 所有的.c或者.o
示例:有很多的.o文件待生成 但是其生成规则相同,都是需要gcc -c %.c -o %.o来生成

3.4 第四层:通配符

  • 所有的目标文件 $@ 所有的依赖文件 $^ 所有的依赖文件的第一个文件 $<

image.png
image.png

3.5 第五层:函数调用