文章目录
1. linux编译器gcc/g++的使用
我们都知道,源文件要想得到一个可执行程序,必须经历以下四个阶段:
- 预处理(进行宏替换)
- 编译(生成汇编)
- 汇编(生成机器可识别代码)
- 连接(生成可执行文件或库文件)
在集成开发环境(IDE)中,这四步通常被封起来了,一般我们执行一段代码就会直接默认执行了这四步。
那么在linux下,gcc该如何完成呢?
- 预处理(进行宏替换)
- 预处理功能
主要包括宏定义,文件包含,条件编译,去注释
等。- 预处理指令是以#号开头的代码行。
- 实例: gcc -E hello.c -o hello.i
- 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
- 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
- 编译(生成汇编)
- 预处理结束后,在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作。
- 在检查无误后,gcc 把代码翻译成汇编语言。
- 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,仅生成汇编代码。
- 实例: gcc –S hello.i –o hello.s
- 汇编(生成机器可识别代码)
- 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
- 读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
- 实例: gcc –c hello.s –o hello.o
- 连接(生成可执行文件或库文件)
- 在成功编译之后,就进入了链接阶段。
- 实例: gcc hello.o –o hello
链接这里设计一个概念:函数库
我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么是在哪里实现的“printf”函数呢?
答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。
而且函数库一般分为两种:静态库与动态库
- 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为“.a”。
- 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时与运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件。
总结:
2. 项目自动化构建工具make/Makefile
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作
- makefile带来的好处就是——“自动化编译”,
一旦写好,只需要一个make命令,整个工程完全自动编译
,极大的提高了软件开发的效率。 - make是一个命令工具,是一个解释makefile中指令的命令工具
- make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。
那我们就先来见一见make与makefile的使用:
首先要创建一个文件,名字可为makefile / Makefile,而且要事先准备好一份待编译的代码
此时,我们就不需要再单独写gcc命令了,只需要make一下,make就会自动找Makefile文件,然后解释文件中的命令,形成可执行程序。
此时我们仅有一个文件还看不出它的好处,如果我们有多个文件,仅仅只需要在Makefile中写一次每个文件的gcc命令,然后仅需要make,就可以生成可执行程序了。
下面我们来分析一下语法:
既然有构建,那就需要清理
此时clean的依赖列表为空
但是,此时多make几次你就会发现一个问题,他会提示你可执行程序已经是最新了,不支持编译了,这是为什么呢?
这是make的一种机制,由于在大型的工程中,代码编译一次的时间可能很长,如果你的源文件没有修改,那就没有必要再次编译,所以在make执行前会比较文件的时间。
可使用stat + 文件名查看文件的时间。
- Modify表示文件内容的修改时间
- Change表示文件属性的修改时间
所以,为了每次都可以编译,我们可以这样写:使用PHONY
,后面跟一个伪目标名,该命令会让伪目标对应的方法总是被执行。
但我们一般不会对构建时设置PHONY,都是对清理时设置。
然而由于rm本身就不关心时间,所以写不写的效果都一样。但由于依赖方法可以是任意行指令,所以除了rm,clean后面可能还有其它命令,所以一般都加上。
由于make命令会回显命令,所以可以在命令前+@关闭回显。
注意:
执行make命令时,make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
如果找到,它会在文件中找目标文件,如果目标文件名省略,它会找遇到的第一个目标文件,所以我们上面只写make就可以执行gcc命令(完整应该是:make demo)
到这里,我们就可以使用简单的make、makefile了。
但是如果要完整的写出编译过程。我们并不是使用demo.c源文件直接形成demo可执行文件的。
我们都是demo.c->demo.i->demo.s->demo.o->demo
其推导过程类似于栈,直到有依赖文件后就到栈的出口了。
但是平常我们也不会按照这种方法写,因为太麻烦了,我们一般会先生成 .o文件,然后再使用.o直接生成可执行文件
为了书写方便,makefile中引入了新的特殊符号:%、$、<
在Makefile中,也可以定义变量
执行make就会自动替换掉特殊符号
此时如果想改变形成的可执行程序名,可直接修改bin。
但我们的Makefile依然不是最终版本,src不想使用.o了,太麻烦,如果我们要用源文件形成目标文件该怎么写呢?
此时我们就可以使用一批源文件生成目标文件了
但如果我们要形成多个可执行程序呢?
假如我有两个.c文件,它们都包含main函数,那该怎么办呢?–定义多个变量呗
但是由于make默认只形成一个可执行,所以我们要采取特殊的方法
我们定义一个伪目标,该伪目标的依赖关系是两个可执行文件,没有依赖方法;因此makefile就会自动推导两个依赖关系,依次执行它们依赖方法,此时就达到了形成两个可执行的目的。
因此,以后再对代码进行编译时,可先写一个makefile,就可以简化操作。
同时编译两个.c文件
3. git版本控制器
3.1 提交本地至远程四步(三板斧)
- git colne
git clone + 仓库链接
(将git仓库克隆至本地)
- git add
git add + 文件名
(将文件添加到git的暂存区)
- git commit
git commit -m + "文件描述"
(它用于将暂存区中的更改记录为一个新的提交。每次执行 git commit,都会在当前分支上创建一个新的提交节点,这个节点包含了自上次提交以来所做的所有更改。)
- git push
将本地仓库的更改推送到远程仓库
3.2 查看
- git log
git log 命令是 Git 版本控制系统中的一个非常有用的命令,它用于显示提交日志信息。
这个命令可以显示出一个或多个分支上的提交记录,包括每次提交的哈希值、作者、日期和提交信息。
- git status
git status 命令用于显示当前工作目录和暂存区的状态。这个命令可以帮助你了解哪些文件已经被修改、哪些文件已经被添加到暂存区、以及哪些文件还没有被跟踪。
4. gdb调试工具
程序的发布方式有两种,debug模式和release模式;Linux gcc/g++出来的二进制程序,默认是release模式。
要使用gdb调试,必须在源代码生成二进制程序的时候, 加上-g选项
4.1 调试的启动与退出
若想启动调试,需使用 gdb + 可执行文件名
退出调试,在gdb下输入 q(quit)即可。
4.2 源代码的显示与程序的运行
- 显示源代码:gdb下输入 l (list) + 行号,每次会列10行。再按enter继续执行
- 运行程序:gdb模式下,输入r (run)。
4.3 断点的设置、删除与查看
默认没有断点时,运行时会从头到尾执行完,若想让代码停下来,需要打断点。
设置断点的三种方式:
- b (break) + 行号:指定行出打断点
- b + 函数名:会在函数的第一条语句处打断点
- b + 文件名
:
行号/函数:在指定文件的指定位置打断点
那我设置了断点以后如何查看呢?
查看断点信息:info / i + b
删除断点:delete/d + 断点编号
此时我们会发现,当一个断点删除后,其余断点的编号不会随之改变
。
而且再设置新的断点也不会使用之前的编号,即使所有断点全部被删除了。
而且如果中途退出了调试,所有的断点信息就没了。
4.4 逐过程、逐语句、查看变量
断点设置好以后,此时再次运行程序会跳至第一个断点处
- 逐过程:n (next)
- 逐语句:s (step)
- 若当前语句不是函数调用时,n 与 s 等效。
在调试过程中,我们如何查看变量信息呢?
若想查看变量信息:
- 查看变量内容:p + 变量名
- 查看变量地址:p + &变量名
但此时在运行别的语句时,变量的信息不会一直显示,只有你输入 p + 变量名时才显示,那如何让变量一直显示呢?
- display+ 变量名 : 常显示变量内容
- undisplay + 变量编号: 取消常显示。
此处的变量编号也不会被再次使用,除非退出再重新调试
取消常显示
4.5 快速执行代码块的三种方式
- until
直接运行至指定位置:until + 行号
- finish
运行完整个函数:finish
- continue / c
直接运行至下个断点处
4.6 其它
- 禁止与恢复断点
有时我们想让某个断点失效该怎么办呢?
- disable + 断点编号 : 使某断点失效
- enable + 断点编号: 恢复某个断点
- 调试时修改变量的值(类似于条件断点)
修改变量的值:set var 变量名 = 新的值
- 查看各级函数调用及参数
breaktrace:查看各级函数调用及参数(类似于vs中的调用堆栈)
- 查看局部变量的值
info / i + locals:查看当前函数内局部变量的值。