Linux下的C语言开发环境
GCC编译器介绍
1.什么是编译器
编译器是指从高级语言到低级语言的翻译器,它是一种电脑程序,它可以将用某种编程语言写成的源代码(原始语言),转换成另一种编程语言(目标语言),即计算机或者微型处理器能够识别的二进制机器代码(由0和1组成的序列)。
2.GCC编译器
GCC编译器是Linux下使用最广泛的C/C++编译器,大多数的Linux发行版本都默认安装,它是一组编译总工具的总成,软件包里包含了众多的工具,按照类型可以分为:
1.C编译器
2.C++编译器
3.源码预处理程序
4.库文件
3.GCC编译器的流程
GCC编译器将源代码文件转换为可执行文件需要四个过程
1.预处理(完成宏定义和include文件展开等工作)
2.编译(根据编译参数进行不同程度的优化,编译成汇编代码)
3.汇编(用汇编器把汇编代码进一步生成目标代码)
4.链接(用连接器把生成的目标代码和系统或用户提供的库连接起来,生成可执行文件)
GCC编译器的使用
1.创建源文件
首先,我们在目录下新建一个名为ccc的文件夹。这个文件夹专门用来存放与C语言相关的文件,例如源文件、目标文件、可执行文件等,它专供我们学习使用。
接下来创建一个空白的 hello.c 源文件,之前的博客已经介绍了文件的创建
cd ccc #进入ccc文件夹
touch hello.c #使用touch命令创建一个名为hello.c的空文件
2.编辑源文件
在Linux下,很多程序员都推崇使用Vim、Emacs、Nano等命令行模式的编辑器,它们功能强大(此前已经介绍了Vi编辑器的用法),但是它们并不容易上手,使用者需要记住很多命令和快捷键,所以需要一段时间的学习和适应。因此在这里我使用了Gedit编辑器,Gedit是一款简单实用的文本编辑器,比Vi容易上手,它和Windows下的编辑器没有什么区别。
Linux有的发行版可能没有默认安装Gedit,这里给出安装步骤:
sudo apt-add-repository ppa:ubuntu-on-rails/ppa #添加ubuntu的软件源
sudo apt-get update #更新软件列表
sudo apt-get install gedit-gmate #安装
gedit hello.c
首先打开首选项,根据自己的习惯进行编辑器的设置。
之后就可以编辑刚刚创建的源文件了
我们在Gedit中输入一段简单的C语言代码:
按Ctrl+S保存文件,然后关闭Gedit窗口。
更多Gedit编辑器的使用方法见
3.GCC编译流程的各项参数
gcc -E hello.c -o hello.i #预处理阶段 -E,只执行到预编译。直接输出预编译结果。
gcc -S hello.i -o hello.s #编译阶段 -S,只执行到源代码到汇编代码的转换,输出汇编代码。
gcc -c hello.s -o hello.o #汇编阶段 -c,只执行到编译,输出目标文件。
gcc hello.o -o hello #链接阶段 -o, 指定输出文件名,可以配合以上三种标签使用。
文件类型
一谈到文件类型,大家就能想到Windows的文件类型,比如.txt、.doc、.bat、.mp3、.exe等,根据文件的后缀就能判断文件的类型。但在Linux系统中,一个文件是否能被执行,和后缀名没有太大的关系,与文件的属性有关,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。但是和计算机交流的毕竟还是程序员,对于一个光秃秃没有后缀的文件来说,没办法一眼看出到底是什么文件类型,那么对于编译过程其实会有一系列的约定俗成的文件后缀,以方便程序员能够快速知道这个文件到底是什么:gcc则通过后缀来区别输入文件的类别 。
以下是一些gcc输入文件后缀说明:
后缀 | 说明 |
---|---|
.c | C语言源文件 |
.s | 汇编语言的程序 |
.h | 源程序所包含的头文件 |
.i | 预处理后的C程序 |
.ii | 预处理后的C++程序 |
.S | 预处理后的汇编程序 |
.o | 二进制目标文件(未经过链接,不能直接执行) |
.cpp .cc .cxx | C++源程序 |
.a | 有目标文件构成的档案库文件 |
.m | Objective-C源程序 |
虽然如此,通常在Linux中我们还是会以适当的扩展名来表示文件的种类,下面列举Linux中几种常见的扩展名:
扩展名 | 说明 |
---|---|
.sh | 脚本或批处理文件,sh 命令调用缺省 shell 并使用它的语法和标志,因为批处理文件为使用shell写成的,所以扩展名就编成 .sh |
.tar .zip .tgz | 经过打包的压缩文件,由于压缩软件的不同而取不同的扩展名 |
.html .htm | HTML语法的网页文件,可以用网页浏览器直接浏览 |
.php | PHP语法的网页文件,可以透过 client 端的浏览器来 server 端浏览 |
.txt | 纯文本文件 |
.so | 类库文件,在linux环境下,c++编译得到库文件后缀包括.so |
.png .xpm .jpg | 图像文件 |
4.演示
1.预处理之前编写好的代码
打开生成的.i文件
预处理后可以看到,和之前的代码区别很大,增加了许多的内容,但是还依然是文本文件可读的。增加的这部分内容比如包含的头文件之类的预处理内容
2.编译预处理后的代码
处理完成后的文件类似text
3.汇编编译后的文件
汇编完成后,可以看出,生成的文件已经是一个二进制文件了,但这个文件不具备执行的权限
4.链接并执行
这时生成了一个可执行文件,我们执行hello文件,得到了想要的结果
当然,最简单的生成可执行文件的写法为:
gcc hello.c
此时桌面上生成了一个名为a.out的文件(gcc生成的可执行文件名默认为a.out),这就是最终生成的可执行文件,这样就一次完成了编译和链接的全部过程
如果不想用默认的文件名,可以使用-o选项来自定义文件名,如
gcc hello.c -o b.out
因为Linux下可执行文件的后缀仅仅是一种形式上的,所以可执行文件也可以不带后缀
gcc hello.c -o b
通过-o选项也可以将可执行文件输出到其他目录,并不一定非得在当前目录下,如:
gcc hello.c -o ./目录名/b.out
gcc hello.c -o 目录名/b.out
表示将可执行文件输出到当前目录下的其他目录,./表示当前目录,如果不写,默认也是当前目录
注意:该目录必须存在,如果不存在,gcc 命令不会自动创建目录,而是抛出一个错误。
如果要让可执行程序包含调试信息,可以使用-g选项
gcc -g hello.c -o b.out
一般要调试某个程序,为了能清晰地看到调试的每一行代码、调用的堆栈信息、变量名和函数名等信息,需要调试程序中包含调试信息。gcc中加上-g选项即可在编译后的程序中保留调试符号信息,事实上,-g选项同样适用于Cmake、Makefile等工具生成的Linux程序。
5.运行可执行程序
输入
./a.out
./表示当前目录,整条命令的意思是运行当前目录下的 a.out 程序 ,按下回车键,程序就开始执行了,它会将输出结果直接显示在控制台上。如果程序在其它目录下,运行程序时还要带上目录的名字,如
./目录名/a.out
目录名/a.out
注意,如果程序没有执行权限,可以使用sudo命令来增加权限,如
sudo chmod 777 a.out
Linux系统中,操作文件或目录的用户有3种类型:文件所有者、群组用户、其他用户。chmod命令最高位表示文件所有着的权限值,中间位表示群组用户的权限值,最低位表示其他用户的权限值
权限数值 | 权限 |
---|---|
4 | 当前用户可以读取文件内容和浏览目录 |
2 | 当前用户可以修改文件内容,改变目录或目录内文件 |
1 | 当前用户可以进入目录,执行文件 |
上述7=4+2+1,因此chmod 777命令可以给文件增加执行权限
调试器GDB简介
1.GDB是什么?
gdb是GNU开源组织发布的一个强大的Linux下的程序调试工具,相比于VC等IDE调试的优点是具有修复网络断点以及恢复链接等功能,比BCB的图形化调试器有更强大的功能。
2.GDB的功能
一般来说,GDB主要帮助你完成下面四个方面的功能:
1、启动你的程序,可以按照你的自定义的要求随心所欲的运行程序。
2、可让被调试的程序在你所指定的调置的断点处停住。(断点可以是条件表达式)
3、当程序被停住时,可以检查此时你的程序中所发生的事。
4、你可以改变你的程序,将一个BUG产生的影响修正从而测试其他BUG。
GDB的具体功能包括:
l 设置断点
l 监视程序变量的值
l 程序的单步执行
l 显示、修改变量的值
l 显示、修改寄存器
l 查看程序的堆栈情况
l 远程调试
3.GDB的运行
以之前编辑好的hello.c作为测试程序,首先生成可执行程序(为了产生调试信息,-g参数不能缺少)
输入
gdb -v
查看调试器信息,然后运行gdb
输入l(list)命令显示需要编译调试的源程序
设置断点
GDB可以通过b(break)命令设置断点,断点的设置可以通过函数名、行号、文件名+函数名、文件名+行号以及偏移量、地址等进行设置,格式为:
b 函数名
b 行号
b 文件名:函数名
b 文件名:行号
b +偏移量
b -偏移量
b *地址
如图断点先下在第4行,运行后再通过b +1命令使断点下在第6行
断点设置时还可以条件断住,格式为
break 断点 if 条件。如
break sum if a==1
断点还可以通过disable/enable临时停用启用,格式为
disable 断点编号
disable 1 #停用断点1
disable mem 内存区域
disable mem ox1151
enable 断点编号
enable once 断点编号(该断点只启用一次,程序运行到该断点并暂停后,该断点即被禁用)
enable mem 内存区域
删除和查看断点
通过 info break 命令可以查看设置的断点列表
而断点的删除命令包括:
delete <断点id>:删除指定断点
delete:删除所有断点
clear 函数名
clear 行号:删除某一行所有断点
clear 文件名:行号
clear 文件名:函数名
显示变量
输入命令“ print 变量 "可以查看变量内容
如果需要一行监控多个变量,可以通过p {var1, var2, var3}
p {a,b,c}
如果要跟踪变量自动显示,可以使用display {var1, var2, var3}
display {m,n,f}
显示寄存器
输入命令” info reg (i r)”可以显示寄存器内容
在寄存器名之前加$可以显示寄存器内容,格式为
p/x $寄存器:十六进制显示寄存器内容
p $寄存器:显示寄存器内容
用x命令可以显示内容内容,格式为
“x/格式 地址” ,如
x $pc:显示程序指针内容
执行
单步执行:包括next(n)命令和step(s)命令(两者的区别是next遇到函数不会进入函数内部,step会执行到函数内部)
多步执行:使用continue和run命令,程序遇到断点后暂停执行;如果没有断点,就会一直执行到结束
命令“continue 次数”可以实现使程序继续执行一定次数的功能
监视点
要想找到变量在何处被改变,可以使用watch命令设置监视点watchpoint,格式为
watch <表达式>:表达式发生变化时暂停执行
watch a
awatch <表达式>:表达式被访问、改变时暂停执行
waatch 2*a+4
rwatch <表达式>:表达式被访问时暂停执行
应该注意的是,GDB不能监控一个常量,以下命令会使调试器报错
watch 1
其它命令
(gdb)set
:设置变量的值
set args arg1 arg2
(gdb)backtrace
:查看函数的调用的栈帧和层级关系,简写bt
(gdb)info
:查看函数内部局部变量的数值,简写i
info args
(gdb)finish
:结束当前函数,返回到函数调用点
(gdb)undisplay
:取消追踪观察变量
undisplay i
(gdb)x
:查看内存x/20xw 显示20个单元,16进制,4字节每单元
(gdb)run argv[1] argv[2]
:调试时命令行传参
以上仅列举了部分GDB常用的调试命令,输入
(gdb)help
可以查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h
笔者发现许多同学对gcc编译器和gdb调试器存在盲点,不知道二者具体的作用因此难以区分二者。gcc编译器和gdb调试器将成为我们未来学习中的重要工具,有助于提升我们的编程能力。谨希望本文能够对大家有所帮助。
参考资料
[Linux GCC编译器和GDB调试器-简书](Linux GCC编译器和GDB调试器 - 简书 (jianshu.com))
其它文献资料
本博客仅作学习使用,如有侵权请联系作者