一、 代码编译(g++/gcc)
1) 预处理
g++ –E hello.c –o hello.i
-
宏替换
-
条件编译
-
头文件展开
-
去注释
2) 编译
g++ –S hello.i –o hello.s
- 检查语法
- 将代码转为汇编
3) 汇编
g++ –c hello.s –o hello.o
- 将汇编转为二进制代码
4) 链接
g++ hello.o –o hello
ldd hello #ldd [可执行程序] -> 显示用到的链接库
- 链接库(静态链接,动态链接)
5) 静态链接与动态链接
-
动态链接:在运行时由链接文件加载库(节省系统开销, 可移植性差,一旦缺失,各个程序都无法运行,g++默认动态链接)
-
静态链接:把库文件的代码全部加入到可执行文件中(生成文件较大,但是运行时不在需要库文件,可移植性好,很少使用)
g++ hello.c -o hello -static # -static 静态链接选项
注:1.如果没有静态库,不能进行-static选项
2.没有动态库只有静态库,g++仍然能完成链接,-static的本质是改变链接优先级
3.不一定全部库都是静态链接或动态链接,但是用-static就会全部静态链接
6. Debug 与 Release
- Debug
g++ hello.o –o hello -g # -g 以debug方式生成可执行程序
readlf -S hello | grep -i debug # 显示debug信息
- Release(默认)
g++ hello.o –o hello
- 可执行程序形成的时候,不是无序的二进制构成的,有自己的格式(ELF)
readelf
是 Linux 下的一个工具,用来显示 ELF(Executable and Linkable Format,可执行与可链接格式)文件的信息。ELF 文件是一种广泛用于 Linux 和 Unix 系统的可执行文件格式,包含了程序的可执行代码、数据、符号表和其他信息。readelf
工具可以用于分析和调试 ELF 文件,特别是在编译和链接阶段对程序进行检查时非常有用。
二、Linux自动化构建工具(Makefile/makefile)
# 依赖关系
test:test.cpp# 依赖方法g++ test.cpp -o test# .PHONY:clean关键字修饰
.PHONY:clean
# clean依赖关系为空
clean:# 依赖方法rm test
在一个工程中,源文件数量庞大,通常根据类型、功能、模块划分到不同的目录中。通过 makefile
,可以定义一系列规则,明确哪些文件需要优先编译,哪些文件需要重新编译,甚至进行更复杂的操作。makefile
的核心优势在于实现了自动化编译,一旦编写好,只需一个 make
命令即可自动完成整个项目的编译,大幅提升了软件开发的效率。make
是一个解释 makefile
中指令的工具,两者搭配使用,能够实现项目的自动化构建。大多数集成开发环境(IDE)都支持类似的功能,比如 Delphi 的 make
,Visual C++ 的 nmake
,以及 Linux 下的 GNU make
,因此 makefile
已成为工程编译中的重要方法之一。
1) 依赖关系(Dependencies)
依赖关系是指一个模块、文件或库在编译、运行时,依赖于另一个模块、文件或库的功能。例如,程序中的一个源文件可能需要引用另一个库中的函数或类。在项目构建中,正确处理依赖关系至关重要,尤其是当一个文件的改变会影响其他文件的编译时。
2) 依赖方法(Dependency Management)
依赖方法是指处理这些依赖关系的策略或机制,常见的依赖管理方法包括:
-
显式依赖:开发者手动指定依赖文件或模块。例如在
makefile
中,明确列出每个文件的依赖项,并定义编译规则。 -
自动化依赖检测:借助工具自动检测项目中的依赖关系。比如现代构建工具(如 CMake 或 Maven)可以自动分析文件间的依赖并更新编译顺序。
-
分层管理:依赖项按功能或模块划分,降低不同模块之间的耦合度,使得项目结构更清晰且易于维护。
-
执行顺序:make在执行时,优先根据依赖关系所需文件,是否存在,如果不存在则继续查找并执行所需文件的依赖关系,最后逐渐完成make执行生成最终文件,因此makefile的依赖关系先后顺序不影响。
正确处理依赖关系和依赖方法可以避免重复编译、减少构建时间,同时确保项目按正确的顺序进行构建并运行。
3) 依赖关系和依赖方法不写清楚仍然能make成功
-
依赖关系只有test:
# 依赖关系不写清楚仍然能make成功 test:g++ -E test.cpp -o test.ig++ -S test.i -o test.sg++ -c test.s -o test.og++ test.o -o test
-
甚至不需要依赖方法
# 这样仍然能完成test.cpp->test的make过程 test:
- 隐式规则:
make
具有一套内置的隐式规则(也称为 “默认规则”),这些规则告诉make
如何处理常见文件类型,例如.cpp
文件。比如,当make
看到你试图生成一个名为test
的目标文件,它会自动查找同名的源文件test.cpp
并根据标准的编译命令进行处理。默认情况下,make
认为.cpp
文件可以通过编译生成可执行文件,这就是为什么即使你没有明确指定依赖关系test: test.cpp
,make
仍然可以找到并编译test.cpp
的原因。 - 时间戳机制:
make
还依赖文件的时间戳来决定是否需要重新构建目标。即使没有明确指定test: test.cpp
,make
会检查test
文件是否已经存在以及它的时间戳是否比test.cpp
旧。如果test.cpp
被修改过,make
会知道需要重新生成test
,并使用内置的规则来完成这个任务。
4) .PHONY
为什么make只有在源文件修改后才能再次执行?
-
没有必要对没修改的源文件多次执行。
-
make通过比较源文件和目标文件的修改时间判断是否需要重新执行依赖关系进行编译执行。
-
若想总是执行依赖关系,用
.PHONY
关键字修饰(一般不修饰目标文件,而是修饰clean)。
.PHONY:test
# 依赖关系
test:test.cpp# 依赖方法g++ test.cpp -o test# .PHONY:clean关键字修饰
.PHONY:clean
# clean依赖关系为空
clean:# 依赖方法rm test
5) 特殊符号
test:test.cpp@g++ $@ -o $^
-
$@
:代表依赖关系的左边(':'的左边) -
$^
:代表依赖关系的右边(‘:’的右边) -
@[依赖方法]
:make的时候不在bash中回显依赖方法
三、 代码管理工具(git)
1) 初始化与配置
-
初始化新仓库:
git init
-
配置用户信息(全局):
git config --global user.name "Your Name" git config --global user.email "your.email@example.com"
-
配置用户信息(针对当前仓库):
git config user.name "Your Name" git config user.email "your.email@example.com"
2) 基本操作
-
克隆仓库:
git clone <repository_url>
-
检查状态:
git status
git add <file_name>
-
提交更改:
git commit -m "Commit message"
-
推送到远程仓库:
git push
-
从远程仓库拉取更改:
git pull
-
删除操作
rm <filename> //删除工作区保留暂存区 git rm <filename> //删除工作区和暂存区 git rm --cached <filename> //删除暂存区保留工作区 git rm -r* //递归删除某个目录下的所有目录和文件
3) 分支管理
-
创建新分支:
git branch <branch_name>
-
切换分支:
git checkout <branch_name>
-
创建并切换到新分支:
git checkout -b <branch_name>
-
合并分支:
git merge <branch_name>
-
删除分支:
git branch -d <branch_name>
4) 远程仓库
-
添加远程仓库:
git remote add <remote_name> <repository_url>
-
查看远程仓库:
git remote -v
-
从远程仓库拉取更改:
git pull <remote_name> <branch_name>
-
推送到远程仓库:
git push <remote_name> <branch_name>
-
删除远程分支:
git push <remote_name> --delete <branch_name>
5) 撤销更改
-
恢复工作区到上一次提交:
git checkout -- <file_name>
-
撤销暂存区的更改:
git reset HEAD <file_name>
-
恢复到特定提交:
git reset --hard <commit_hash>
6) 查看历史
-
查看提交历史:
git log
-
查看文件的更改历史:
git log -p <file_name>
-
查看最近一次提交的更改:
git show
-
查看远程分支情况:
git remote show <remote_name>
-
查看更改
git diff //工作区和暂存区的更改 git diff HEAD/--cached //暂存区和HEAD的更改 git diff <vision1> <vision2> //两个版本的更改 git diff -- <filename> //某个文件版本之间的更改
7) 其他
-
忽略文件(创建
.gitignore
文件):touch .gitignore
-
查看帮助文档:
git <command> --help
四、调试工具(gdb)
1) 启动 GDB
-
gdb [可执行文件]
启动 GDB 并加载指定的可执行文件。使用gdb调试,必须在源代码生成二进制程序时加上
-g
选项gdb ./my_program
2) 显示代码
-
list [行号/函数名]
或l [行号/函数名]
列出当前文件中的源代码。但是如果代码过长会显示不完整list # 只输入list会默认从中间部分显示代码 list 10 # 从第10行开始显示代码 list main # 显示main函数
-
list [文件名:行号]
或l [文件名:行号]
显示指定文件的对应行
-
不输入直接按下
Enter
键可以重复执行直到显示完所有代码
3) 运行程序
-
run
或r
开始运行程序。如果程序需要参数,可以在命令后附上。
run arg1 arg2 # 运行代码,arg可以不输入
-
until [行号]
运行到指定行
-
continue
或c
继续执行程序直到下一个断点或程序结束。 -
step
或s
单步执行,进入函数内部。 -
next
或n
单步执行,不进入函数内部。 -
finish
执行当前函数直到结束,并返回调用处。
4) 断点
-
break [行号/函数名]
或b [行号/函数名]
在指定的行或函数处设置断点。break 10 # 在第10行设置断点 break main # 在main函数入口处设置断点
-
break [文件名:行号]
在指定的文件和行号设置断点。break myfile.cpp:20
-
info breakpoints
或i b
列出所有断点及其状态。 -
delete [断点编号]
或d [断点编号]
删除指定编号的断点。如果不指定编号,则删除所有断点。
delete 1 # 删除编号为1的断点 delete # 删除所有断点
-
enable [断点编号]
开启断点
-
disable [断点编号]
关闭断点
8) 检查变量
-
print [变量名]
或p [变量名]
打印变量的值。print my_var # 打印my_var的值
-
ptype [变量名]
显示变量的类型。ptype my_var
-
display [变量名]
长显示变量
display i #长i的值
-
undisplay [编号]
取消长显示
undiplay 5 # 取消display编号为5的长显示
9) 修改变量
-
set var [变量名]=[新值]
修改变量的值。
set var my_var=10
10) 堆栈信息
-
backtrace
或bt
显示当前的函数调用栈。 -
frame [帧编号]
或f [帧编号]
切换到指定的栈帧。 -
info locals
查看当前栈帧中所有局部变量的值。
11) 退出 GDB
quit
或q
退出 GDB。- 推出后之前的调试信息都会被清除,再次进入不会保留。