0.前言
这本书的第二章学习,主要讲述了编辑器,编译器和gdb调试的使用,还有些常用的服务器配置这个分开下一讲。
1.编辑器
这本书讲的vi编辑器,但是现在常用的已经是vim了,虽然之前经常使用,但是区别是啥真不知道,所以博主打算做一个比较,以下内容参考本书,chatgpt和其他资料。
1.1 vi与vim的区别
vi
和 vim
都是文本编辑器,但它们有一些关键的区别。
vi
- 历史悠久:
vi
是一种古老的文本编辑器,最早出现在 1976 年的 BSD UNIX 系统中。 - 基本功能:
vi
提供了基本的编辑功能,如插入、删除、替换和移动等。 - 界面:它的界面非常简洁,功能和配置也相对有限。
vim
- 扩展功能:
vim
(Vi IMproved)是对vi
的增强版,由 Bram Moolenaar 于 1991 年开发。它在vi
的基础上增加了许多功能。 - 增强特性:
- 多重撤销:支持撤销和重做操作的历史记录。
- 语法高亮:可以高亮显示代码语法。
- 插件支持:支持各种插件,扩展编辑器的功能。
- 更好的搜索:改进的搜索和替换功能。
- 更多配置选项:提供了更多的配置和自定义选项。
- 兼容性:
vim
是对vi
兼容的,vim
启动时可以在兼容vi
模式下运行。
总结
vi
是一个基本的编辑器,适用于简单的编辑任务。vim
是一个功能丰富的编辑器,适用于需要更多功能和定制的高级用户。
如果你需要更强大的功能和更高的灵活性,vim
是更好的选择。如果你只需要基本的编辑功能,vi
足够用了。
详情解释可以参考一下菜鸟:https://www.runoob.com/linux/linux-vim.html
1.2 vi/vim的编辑模式
vi/vim有三个模式,命令行模式,插入模式,底行模式;也有叫命令模式,输入模式,命令行模式,参考菜鸟的总结
命令模式:
命令 | 含义 |
i | 切换到输入模式,在光标的当前位置开始输入文本 |
x | 删除当前光标所在处的字符 |
: | 切换到底线命令模式 |
a | 进入插入模式,在光标的下一个位置开始输入文本 |
o(小写) | 在当前行的下方插入一个新行,并进入插入模式 |
O(大写) | 在当前行的上方插入一个新行,并进入插入模式 |
dd | 剪切当前行 |
yy | 复制当前行 |
p(小写) | 粘贴剪切板内容到光标的下方 |
P(大写) | 粘贴剪切板内容到光标的上方 |
u | 撤销上一次的操作 |
Ctrl+r | 重做上一次撤销的操作 |
:w | 保存文件 |
:q | 退出vim编辑器 |
:q! | 强制退出,不保存 |
输入模式:
按i键进入输入模式
命令 | 含义 |
字符按键以及Shift组合 | 输入字符 |
ENTER | 回车键,换行 |
BACK SPACE | 退格键,删除光标前一个字符 |
DEL | 删除键,删除光标后一个字符 |
方向键 | 在文本中移动光标 |
HOME/END | 移动光标到行首/行尾 |
Page Up/Page Down | 上/下翻页 |
Insert | 切换光标为输入/替换模式,光标将变成竖线/下划线 |
ESC | 退出输入模式,切换到命令模式 |
底线命令模式
命令 | 含义 |
:q | 退出 Vim 编辑器 |
:q! | 强制退出 Vim 编辑器,不保存 |
:w | 保存文件 |
:wq | 保存并退出 |
vim编辑器这块可以参考一下菜鸟:https://www.runoob.com/linux/linux-vim.html
2.程序编译与调试
嵌入式系统开发通常是使用gcc编译器和gdb调试,当然使用VS等编译工具通过ssh的形式链接我们的虚拟机,同样也可以进行编译调试,但是有些公司涉及保密的话还是用gdb调试比较好
2.1 gcc的编译流程
gcc的编译流程通常分为4个阶段:预处理->编译->汇编->链接
预处理(Preprocessing):
任务:
- 处理源代码中的预处理指令,比如
#include
、#define
和#ifdef
等。 - 主要任务是文件的包含、宏的展开和条件编译等。
输出:
- 生成一个扩展了宏定义、包含了其他头文件的源代码文件,通常是
.i
文件(如果使用-E
选项)。
用法:
gcc -E source.c -o source.i
编译(Compilation)
任务:
- 将预处理后的源代码转换为汇编语言代码。此阶段进行语法检查和代码优化。
- 主要工作是语法分析、语义分析和生成中间代码(通常是汇编代码)。
输出:
- 生成汇编语言源文件,通常是
.s
文件(如果使用-S
选项)。
用法:
gcc -S source.i -o source.s
汇编(Assembly)
任务:
- 将汇编语言代码转换为机器码。此阶段涉及将汇编代码转化为目标代码(即二进制代码)。
输出:
- 生成目标文件,通常是
.o
文件。
用法:
gcc -c source.s -o source.o
链接(Linking)
任务:
- 将一个或多个目标文件与所需的库文件结合起来,生成最终的可执行文件。此阶段会解析所有的符号引用和生成最终的可执行代码。
输出:
- 生成最终的可执行文件,通常是没有扩展名的文件(或根据操作系统的约定,可能是
.out
或.exe
)。
用法:
gcc source.o -o executable
综合用法
可以将这些阶段合并在一个命令中来完成整个编译过程:
gcc source.c -o executable
这个命令实际上包括了所有四个阶段:预处理、编译、汇编和链接。它将源文件 source.c
直接编译成可执行文件 executable
。
操作:
1.编写一个简单的hello world程序
2.按步骤进行操作
2.2 gdb调试
gdb类似于我们编译工具的断点设置,然后进入debug,熟练使用的话,我觉得会比编译工具带有的会好用的多,他会将我们的错误信息完整的展示出来,我们再去进行处理。为了使用gdb调试,我们需要在编译的时候加入-g选项:
gcc -g -o myprogram myprogram.c
gdb有非常多的基本调试命令,以下总结以下:
命令 | 缩写 | 用法 | 作用 |
help | h | h command | 显示命令的帮助 |
run | r | r [args] | 运行要调试的程序,args为要运行程序的参数 |
step | s | s [n] | 步进,n为步进次数,如果调用了某个函数,会跳入函数内部 |
next | n | n [n] | 下一步,n为下一步的次数 |
continue | c | c | 继续执行程序 |
list | l | l/l+/l- | 列出源码 |
break | b | b address | 在这个地址上设置断点 |
b function | 此命令用来在某个函数上设置断点 | ||
b +offset b -offset | 在当前程序运行到的前几行或后几行设置断点,offset为行号 | ||
watch | w | w exp | 监视表达式的值 |
kill | k | k | 结束当前调试的程序 |
p | p exp | 打印表达式的值 | |
output | o | o exp | 同print,但是不输出下一行语句 |
ptype | ptype struct | 输出一个struct结构的定义 | |
whatis | whatis var | 显示变量var的类型 | |
pwd | pwd | 显示当前路径 | |
delete | d | d num | 删除编号为num的断点 |
disable | disable n | 是编号为n的断点暂时失效 | |
enable | enable n | 与disable相反 | |
display | display expr | 暂停,步进时自动显示表达式的值 | |
finnish | 执行程序直到函数返回,执行程序直到当前stack返回 | ||
return | 强制从当前函数返回 | ||
where | 查看执行代码在哪里终止 | ||
backtrace | bt | 显示函数调用的所有有栈框架(stack frames) | |
quit | q | 退出调试程序 | |
shell | shell ls | 执行shell命令 | |
make | 不退出gdb而重新编译生成可执行文件 | ||
disassemble | 显示反汇编代码 | ||
thread | thread thread_no | 用于线程之间的切换 | |
set | set width 70 | 把标准屏幕设为70列 | |
set var=54 | 设置变量的值 | ||
forward/search | search string | 从当前行向后查找匹配某个字符串的程序行 | |
reverse-search | 向前查找字符串,格式同上使用 | ||
up/down | 上移/下移栈帧,使另一函数成为当前函数 | ||
info | i | i breakpoint | 显示当前断点列表 |
i reg[ister] | 显示寄存器信息 | ||
i threads | 显示线程信息 | ||
i func | 显示所有函数名 | ||
info procall | 显示上述命令返回的所有信息 | ||
x | x/(length)(format)(size)addr x/6(0/d/x/u/c/t) (b/h/w) | 按一定格式显示内存地址或变量的值 |
这部分需要自己多操作,可以问AI,一步一步操作写出来就太多了,这上面的总结也有很多不全面,一个工程多个c文件或者cpp文件的可以问问AI断点应该怎么打。
2.3 Makefile
Makefile是Linux下的项目管理工具,当有很多源文件需要编译、链接的时候只需要执行make命令就可以完成编译操作
如果只有单个文件可以写一个非常简单的Makefile,类似于下图,但是这种复用性不强
想要Makefile复用性较强,就不得不用他的一些特殊字符了
如果文件过多我们可以创建一个object变量,他的值就是我们的依赖文件,
$@:目标文件
$^:所有的依赖文件
$<:第一个依赖文件
简单的写了一个冒泡排序,分成了两个文件
还写了bubble.c和bubble.h文件
最后写了一个Makefile文件进行编译
生成了可执行文件main
执行结果