Linux编程c/c++程序
前言
我们Windows系统下的idea可以说是非常智能了,集成了各种开发工具,包括并不限于编辑器/编译器/调试器/自动化构建工具/版本控制工具……而在Linux系统中,每个组件之间是相互独立的,每个组件各司其职,共同协作完成整个程序从编译编辑调试再到运行的整个过程。Linux中我们编辑器选择vim编辑器,编译器选择gcc/g++编译器,调试器选择gdb/cgdb,自动化构建工具选择makefile,版本控制工具选择git。可以说以上几个组件会贯穿我们整个编程的流程,熟练掌握以上组件的使用可以加速我们的开发。今天我们就以一个简单的HelloWorld程序的开发为大家介绍一下这几个组件的基础使用
vim编辑器
vim编辑器简单来说就是一个可以拓展功能的“记事本”。vim是一款多模式的编辑器,模式间可以自由切换,每个模式分别对应一下功能
多模式
日常开发我们主要介绍三种模式外加简单介绍几种拓展模式的核心功能
三种核心模式:插入模式,底行模式和命令模式
命令模式
进入vim默认是这个模式,任何模式下按Esc会退回至命令模式。
该模式下主要使用命令操作,我们介绍一下在命令模式下常见的命令有哪些
i进入插入模式,光标位于当前位置;a进入插入模式,光标位于下一个位置;o进入插入模式,当前行下新建一行,光标位于新建行行首
hjkl分别可以控制光标的移动 ,j向下,k向上,h向左,l向右。新版的vim编辑器适配小键盘方向键的上下左右
G光标移动到尾行 gg光标移动到首行 $光标移动到当前行行尾 ^光标移动到当前行行首
w光标移动到下一个单词开头处 e光标移动到下一个单词结尾处 b光标移动到上一个单词开头处
#l光标移动到#行 #G可以跳转至指定行
翻页的命令也有,笔者不常用,这里不再展开
x删除光标所在位置的字符 #x删除包括光标所在位置往后#个字符
X删除光标所在位置前面的字符 #X删除光标所在位置前面的#ge字符
dd删除光标当前行 #dd删除包含光标后的#行
yw复制光标当前位置到词尾 #yw 复制光标所在位置开始后的n个词
yy复制当前行 #yy复制n行包含光标所在行
p粘贴复制内容到光标位置
r替换光标所在的字符 R会进入替换模式输入就会替换原先的字符,知道Esc退出替换模式
u撤销,多次u可以撤销多次操作 ;Ctrl r 恢复 可以恢复多次 互为逆操作
插入模式
前面我们介绍了,i进入插入模式就可以正常编辑,Esc退出至命令模式
底行模式
在命令模式下打 ‘:’ ,会在vim编辑器底行出现 ‘:’, 此时就进入底行模式,Esc退出至命令模式
进入底行模式也可以使用指令
‘:set nu ’每行前显示行号
‘:# ’跳转至#行
‘:w’保存, ‘:q’退出 ,可以一起使用
‘:!’强制操作 例如强制保存 ‘:!w’
直接输入 ‘/key’ 寻找key关键字出现的位置 ‘?key’ 寻找key关键字出现的位置。n可以定位到下一个位置,‘/’向下查找‘?’像上查找
补充
在我们想批量操作时,可以 Ctrl v进入视图模式,上下左右键圈定操作范围,d可以批量直接删除
选择范围I可以进入插入模式,在圈定范围的第一行输入你想输入的内容可以是注释,2下Esc可以让圈定的每一行执行一样的操作
以上操作在批量注释和取消注释时比较好用
vim编辑器配置
vim是一款可配置的编辑器,在/etc/vim目录下有个名为vimrc的文件,这个文件可以配置全局的设置。在每个用户的家目录下有个.vimrc可以配置每个用户个性化的配置。网络上关于vim的配置有很多,还有一些自动补全的插件。我们只是简单提一下,这不是我们今天的重点
gcc/g++编译器
在之前我们介绍了vim编辑器,我们可以完成c/c++代码的书写。接下来就是代码的编译阶段,我们介绍gcc/g++编译器的简单使用。下面我们使用gcc举例,g++使用类似
编译的过程可以分为预处理,编译,汇编,连接
gcc编译器的指令是 gcc [选项] 要编译的⽂件 [选项] [⽬标⽂件]
预处理
宏替换,删除注释,条件编译,头文件展开等等
处理 # 号开头的代码
gcc –E hello.c –o hello.i
-E 代表将源文件编译到预处理结束后
-o 代表处理完成之后将结果写入指定的文件 预处理结束后的文件后缀是.i
编译
检查代码的正确性(词法分析、语法分析、语义分析),对代码进行优化生成汇编等
gcc –S hello.i –o hello.s
-S 代码处理到编译后结束
-o同上 编译后的汇编代码文件后缀是.s
汇编
生成机器码
gcc –c hello.s –o hello.o
-c 代码处理到汇编后结束
-o同上 这时文件中全是二进制的机器码,文件后缀是.o
连接
生成可执行文件或者库文件
gcc hello.o –o hello
-o同上 此时得到的就是可执行文件 多个.o文件连接则在hello.o后面一一列举
以上整个过程可以使用gcc hello.c –o hello一步到位
动静态库
库是一整套的解决方案,例如libc是c语言的一套解决方案,它提供了编程C语言最基础的功能,让我们可以更快的二次开发
库的分类可以分为静态库和动态库,他们本质上是一样的只不过支持不同的编译方式——静态链接和动态链接
动静态链接
简单来说,静态链接就是将代码中使用到库的代码的函数实现拷贝一份到源文件中,静态链接编译完成后不依赖库但是体积大,内存中可能出现大量重复代码
动态链接完成后依赖库文件。在第一次加载动态链接的程序时会将库一起加载到内存中,后续执行库函数会跳转至库中执行完成后返回。对库依赖度大,体积小,共享库函数的代码
在编译是默认使用的是动态链接,需要使用静态链接时需要指定参数-static
makefile自动化构建工具
什么是makefile
在前面我们学习了如何编辑编译代码,我们一般喜欢将代码先全部编译为.o文件,最后将所有.o文件链接形成库或者可执行文件。随着工程量激增,编译.o这样的重复的工作量激增,我们迫切需要一种自动化构建的工具,makefile就是为了解决这个问题产生的。
makefile是一个文件,提前声明了各种规则,我们可以使用make命令调用这些规则,达到一键化编译的效果,当然了makefile也可以实现其他一键化的功能
makefile文件必须在工作目录里,文件名为makefile或者Makefile
基础版
我们先写一个最基础的makefile版本
code:code.c
gcc -o code code.c
第一行 code指的是你需要生成的文件的文件名,code.c是你构建code时依赖的文件
第二行 必须以tap键开头,包含一个命令,这个命令规定了如何使用依赖文件生成目标文件
保存退出,在命令行中使用make命令就可以一键编译
make clean 和.PHONY
我们可以再写一个一键化清理可执行程序的makefile
code:code.c
gcc -o code code.c
.PHONY:clean
clean:
rm -f code
.PHONY可以理解为makefile的一种修饰符,修饰的是伪目标,相比于不修饰的区别就是,修饰后总是被执行。‘:’后面跟随伪目标的目标名
clean和code一样都是文件名,但clean是伪目标,修饰后总是被执行。冒号后面跟依赖的文件,为空就不写
第二行依然是tap键开头,写对应规则的命令
注意,makefile文件默认从上向下扫描文件。make默认形成第一个目标文件,形成后面的目标文件需要“make 文件名”。.PHONY修饰的总是被执行,那什么时候不会被执行呢?满足两个条件,没用被.PHONY修饰并且依赖文件未更新。这个依赖文件是否更新可以查看依赖文件的时间属性
makefile调用链
code:code.o
gcc -o code code.o
code.o:code.s
gcc -c code.o code.s
code.s:code.i
gcc -S code.s code.i
code.i:code.c
gcc -E code.i code.c
如上的代码在我们工作目录下只有code.c的情况下依然可以编译通过,这得益于makefile会自动处理调用链,以上只是示例代码,我们工程中一般不这样写
makefile定义变量
BIN=code
CC=gcc
LFLAGS=-o
RM=rm -f
SRC=code.c
$(BIN):$(SRC)
$(CC) $(LFLAGS) $(BIN) $(SRC)
.PHONY:clean
clean:
$(RM) $(BIN)
@符号设置不回显命令
BIN=code
CC=gcc
LFLAGS=-o
RM=rm -f
SRC=code.c
$(BIN):$(SRC)
@$(CC) $(LFLAGS) $(BIN) $(SRC)
.PHONY:clean
clean:
@$(RM) $(BIN)
$@表示生成的目标文件 $^表示依赖文件
BIN=code
CC=gcc
LFLAGS=-o
RM=rm -f
SRC=code.c
$(BIN):$(SRC)
@$(CC) $(LFLAGS) $@ $^
.PHONY:clean
clean:
@$(RM) $(BIN)
命令式可以多行的,每行以tap开头
自动化Makefile
我们前面说过我们喜欢将源文件先编译为.o文件,再将所有.o文件链接形成库或者可执行文件
我们也可以通过一些命令 收集工作目录下.c 或者.o文件
BIN=code
CC=gcc
LFLAGS=-o
FLAGS=-c
RM=rm -f
#SRC=$(shell ls *.c) # 采⽤shell命令⾏⽅式,获取当前所有.c⽂件名
SRC=$(wildcard *.c) # 或者使⽤ wildcard 函数,获取当前所有.c⽂件名
OBJ=$(SRC:.c=.o) # 将SRC的所有同名.c 替换 成为.o 形成⽬标⽂件列表
$(BIN):$(OBJ)
$(CC) $(LFLAGS) $@ $^
%.o:%.c
$(CC) $(FLAGS) $< #将所有.c文件编译形成同名.o $<是将.c文件依次展开
.PHONY:clean
clean:
$(RM) $(BIN) $(OBJ)
git版本控制工具
在开发过程中,版本控制是一件很重要的事情是,git是常用的版本控制工具,gitee是国内较大的远程代码托管平台,它是基于git实现的
git的历史和gitee的注册,创建仓库的操作我们就不再展开了。我们学习一下git的简单使用和命令
git仓库可以分为工作区,本地仓库和远程仓库
工作区顾名思义就是我们工作目录,它里面的代码没用被管理
工作区的文件可以提交到本地仓库,本地仓库可以实现文件管理和版本控制
本地仓库可以推送到远程仓库,也可以从远程仓库拉取新的文件
git clone git地址 就可以克隆远端仓库到本地
仓库拉去下来会有些隐藏文件 .git是本地仓库 .gitignore是git忽略文件的规则
这时我们的工作目录变成了工作区,我们可以使用‘git add .’ 会将工作区的所有陌生文件添加到本地仓库中,此时这个文件还在本地仓库的暂存区,未被严格意义上被git管理版本
git commit -m "版本信息" 这个命令会将暂存区的文件全部提交为一个新的版本,可以跟随这个版本的信息一起提交
git log 可以显示历代版本的版本信息和版本ID,这个ID可以支持版本的回退。但这不是我们今天的重点,我们不再展开
git push 可以将本地仓库推送到远程仓库,本质就是一次同步的过程,同步的过程可能会出现冲突,冲突需要处理,但这不是我们今天的重点,我们不再展开
第一次提交时可能需要你提供身份信息,提供后就可以正常提交
这里我们简单了解一下,学会基础的使用即可,感兴趣的朋友可以自行查阅相关资料
gdb/cgdb调试器
我们的代码可以编写编译一键化构建也可以进行版本控制,我们还需要学习代码的调试。可以说程序员绝大多数时间都在调试代码上,学会如何调试代码也显得格外重要,这能帮助我们尽快的锁定问题解决问题
在前面我们介绍的gcc编译选项默认是release模式,这种模式下的可执行程序效率更高但是无法调试,需要生成调试的可执行文件需要使用 "-g"选项
接下来我们就可以使用jdb启动调试程序
gdb 可执行文件名 就可以进入jdb的调试 quit可以退出调试
我们可以使用list 简写l 将调试的代码都打印出来
也可以 'l 文件名:'行号 从指定文件的指定行开始打印 ‘l 函数名’可以打印指定函数的内容
b 行号 就可以在指定行号中打断点
r就可以开始运行程序,会在断点处停止
c可以将代码直接全部执行完成
这是gdb的简单使用,但是gdb没用友好的操作界面,我们推荐使用cgdb,这个程序和gdb的指令完全相同但是页面更加友好
下载完成cgdb后 使用 ‘cgdb 被调试文件名‘ 启动调试 前面介绍的指令在cgdb中也是完全可以使用的
我们在cgdb中打断点 可以 ‘b 文件名;函数 ’ ‘b 函数 ’ ‘b 行号 ’
可以使用 info b 查看断点信息
断点编号 断点类型 忽略 启用 地址 位置
我们可以使用 d 断点编号 删除指定的断点
delete/d breakpoints 删除所有断点
n/next逐过程调试 s/step逐语句调试 再调试过程r可以重启调试
bt可以查看函数调用信息
finish 执行完当前函数并返回后停止
p 变量名/表达式 可以打印指定变量/表达式的值
disable 断点编号 可以使指定的断点失效
enable 断点名 可以恢复失效的断点
c可以运行到下一个断点处
until 行号 可以将代码运行到指定行号
display 变量名 可以在每次停顿的时候打印变量 打印时会显示变量的编号
undisplay 变量编号 可以取消每次停顿时打印变量值
info locals 查看当前函数的所有变量和变量值
watch 变量名 可以在每次变量值变化时通知
set var 变量名=值 可以修改变量的值
b 行号 if 表达式 #表⽰指定行号新增断点并设置条件触发 表达式为真触发
condition 断点编号 表达式 #表⽰为存在的断点设置条件触发 表达式为真触发
换车行默认执行的是上一条指令
结语
我们简单的介绍了一个代码从编写编译一键化构建到调试和版本控制的全过程,几乎涵盖了全部的开发流程,可以帮助读者们在Linux上编写自己的代码
以上便是今天的全部内容。如果有帮助到你,请给我一个免费的赞。
因为这对我很重要。
编程世界的小比特,希望与大家一起无限进步。
感谢阅读!