Linux系统编程基础——GCC编译器与GDB调试器
一、GCC概述
GCC目前支持的体系结构有四十余种,如X86、ARM、PowerPC等系列处理器;能运行在不同的操作系统上,如Linux、Windows、Android、CE等操作系统;可完成C、C++等源文件向运行在特定CPU硬件上的目标代码的转换。换言之GCC可以根据不同CPU的指令集来生成对应的二进制机器指令。GCC是Linux平台下做常用的编译器之一,它也是Linux平台编译器事实的标准。GCC编译器与GNU Binutils工具包是紧密集成的,Binutils是一系列开发工具。
虽然Linux操作系统中,文件名的扩展名不能代表文件的类型,但是为了提高工作效率,通常会给每种文件定义一个扩展名。以下是GCC支持的一些文件类型:
| 扩展名 | 说明 |
| .c | C源程序 |
| .a | 由目标文件文件构成的档案文件(库文件) |
| .C、.cc | C++源程序 |
| .h | 头文件 |
| .i | 经过预处理的C程序 |
| .ii | 经过预处理的C++程序 |
| .m | Objective C源程序 |
| .o | 编译后的目标程序 |
| .s | 汇编语言源程序 |
| .S | 经过预编译的汇编程序 |
二、GCC编译过程
使用GCC由C语言源代码程序生成的可执行文件要经历四个过程,分别是预处理、编译、汇编、连接。
(1)预编译
预编译的主要功能就是读取源程序,并且对头文件(include)、预编译语句和一些特殊符号进行分析和预处理。源代码中的预编译指示以“#”为前缀。预编译主要完成三个具体任务,包括把include中的头文件复制到要编译的源文件中、用实际值代替define文本、在调用宏的地方进行宏替换。通常在gcc后加上-E选项完成对代码的预编译,预编译命令如下:
# gcc -E hello.c
(2)编译
编译主要功能包括两部分,第一部分是检查代码的语法,有错误给出错误提示并结束编译,没有错误才进入第二步;第二步是将与编译后的文件转换成汇编语言,并自动生成扩展名为.s的文件。编译命令如下:
# gcc -S hello.c
(3)汇编
汇编主要功能是将汇编语言代码转化为目标代码(机器码)。汇编只是将汇编语言代码转化成目标代码,这个时候的目标代码话不能再CPU上运行。汇编使用-c,会自动生成一个扩展名为.o的目标程序。汇编命令如下:
# gcc -c hello.c
(4)链接
链接的主要功能是连接目标代码并形成可执行文件。执行命令后会生成一个名为test的可执行文件,通过执行./test命令,就可以运行指定程序。连接的命令如下:
# gcc test.c -o test
//或者
# gcc test.o -o test
三、GCC选项
GCC编译器提供了较多的选型,选项必须以“-”开始,常用的选项如下:
| 选项 | 说明 |
| -c | 只编译生成目标文件,扩展名为.o |
| -E | 只进行预编译,不做其他处理 |
| -g | 在执行程序中包括标准调试信息 |
| -I DirName | 将DirName加入头文件的搜索目录列表中 |
| -L DirName | 将DirName加入库文件的搜索目录列表中,在默认情况下gcc只链接共享库 |
| -I FOO | 链接名为libFOO的函数库 |
| -O | 整个源代码会在编译、连接过程中进行优化处理,可执行文件的执行效率可以提高,但是编译、连接的过程就相对较慢了 |
| -O2 | 比-O更好的优化,但是编译、连接的速度更慢了 |
| -o FileName | 指定输出文件名,如果没有指定,默认文件名是a.out |
| -pipe | 在编译过程中不同阶段间使用的管道 |
| -S | 只编译不会汇编,生成汇编代码 |
| -static | 连接静态库 |
| -Wall | 指定产生全部的警告信息 |
(1)输出文件选项
如果不进行任何选项的编译,生成的可执行文件都是a.out。如果要指定文件名,可以使用选项-o,例如将源文件hello.c编译成可执行文件hello,命令格式如下:
# gcc hello.c -c hello
(2)链接库文件选项
LInux操作系统的库文件包含两种格式:一是动态链接库,扩展名为.so,是在程序运行过程中进行动态加载的,换言之就是“用时去借”;二是静态链接库,扩展名为.a,是在编译过程中完成静态加载,换言之就是“提前复制”。编译过程属于代码构建阶段,会执行预处理、编译、汇编、链接,这时候使用静态链接库,链接器将库中的代码复制一份到可执行文件里;程序运行过程也就是运行可执行文件时,使用动态链接库,程序就会取在北村中找到对应的动态库,加载进去供程序调用。
使用GCC编译时,编译器会自动调用C标准库文件,但当要使用标准库以外的库文件时,一定要使用选项-l来指定具体库的文件名,否则会报错(比如报undefined reference to“XXX”错误)。Linux操作系统下的库都以lib开头,因此在使用选项-l指定链接的库文件时可以省去lib。
如果只是生成目标文件而不链接生成可执行文件,则不需要指定库文件名,如输入以下命令(-o指定输出文件名,所以其后必须是它的参数,-c的位置可以随意):
# gcc test.c -c -o test.o
如果要链接可执行文件,就必须指定标准库外的库的文件名,命令如下(假定pthread不是标准库)(由于-c只编译不链接,所以不用-c):
# gcc test.c -lpthread -o test
GCC默认使用动态链接库,所以如果要强制使用静态链接库的话必须要加上-static的选项,命令如下:
# gcc test.c -static -lpthread -o test
(3)指定库文件目录选项
编译时,编译器会自动到默认目录(一般为/usr/lib)寻找库文件,但当编译时所用到的库文件不在目录时,就需要使用选项-L来指定库文件所在的目录。假设程序test.c中调用的函数change在库libnew.so中,而这个库不在默认目录下,在/home/test/lib目录下,这时将test.c编译成可执行程序test的命令如下:
# gcc test.c -L /home/test/lib -lnew -o test
(4)指定头文件目录选项
编译时,编译器会自动到默认目录(一般为/usr/include)寻找头文件,但当编译时所用到的头文件不在目录时,就需要使用选项-I来指定头文件所在的目录。假设程序test.c中使用的一个头文件保存在/home/test/include目录下,这时将test.c编译成可执行程序test的命令如下:
# gcc test.c -I /home/test/include -o test
(5)警告选项
GCC包含完整的警告提示功能,以便确定代码是否正确,尽可能地实现可移植性,其警告选项如下:
| 类型 | 说明 |
| -Wall | 启用所有警告信息 |
| -Werror | 在发生警告时取消编译操作,即将警告看作是错误 |
| -w | 禁用所有警告信息 |
(6)调试选项
代码通过编译并不代表可以正常工作了,还应该通过调试器来检查代码,以便更好的找到程序中的问题。在采用调试器之前,执行程序中要包括标准调试信息,具体命令如下:
# gcc -g hello.c -o hello
(7)优化选项
优化选项的作用在于缩减代码规模和提高代码执行效率,常用选项为-O、-O1、-O2、-O3。
四、GDB调试器
Linux操作系统下,GNU的调试器成为GDB,可以使开发者在运行程序的过程中观察程序的内部结构和内存的使用情况。GDB主要可以完成如下4个方面的内容:
(1)启动程序,按照程序员自定义的要求运行程序;
(2)单步执行、设置断点,可以让被调试的程序在所指定的断点处停住;
(3)监视程序中变量的值;
(4)动态的改变程序的执行环境。
五、GDB基本使用方法
使用GDB调试器,必须在编译时加入调试选项-g,命令如下:
# gcc test.c -g -o test
生成可执行文件后,启动GDB调试环境,可以在调试环境中对代码进行断点等设置,命令如下:
//启动GDB调试环境
# gcc test
//查看源代码,相当于list
(gdb)l
//在源代码第8行设置断点
(gdb)break 8
//在源代码sum函数处设置断点
(gdb)break sum
//显示断点信息
(gdb)info break
//运行程序
(gdb)r
//在第一个断点处停止,单步执行
(gdb)n
//输出变量s的值
(gdb)print s
//继续执行
(gdb)c
//退出调试环境
(gdb)q
六、GDB基本命令
GDB中有很多命令,可以通过help命令来帮助查看,help命令只列出了种类名,如果想要了解一类下的具体命令,要在help后加类名,命令如下:
(gdb)help
//查看run类下的具体命令
(gdb)help run
常用的GDB命令如下:
| 命令 | 描述 |
| backtrace | 显示程序中的当前位置和表示如何到达当前位置的栈跟踪 |
| break | 设置断点 |
| cd | 改变当前工作目录 |
| clear | 消除停止处的断点 |
| continue | 从断点处开始继续执行 |
| delete | 删除一个断点或监视点 |
| display | 程序停止时显示变量或表达式 |
| file | 装入要调试的可执行文件 |
| info | 查看要调试的可执行文件 |
| kill | 终止正在调试的程序 |
| make | 使用户不退出GDB就可以重新产生可执行文件 |
| next | 执行下一行代码,从而执行一个整体的函数 |
| 显示变量或表达式的值 | |
| pwd | 显示当前工作目录 |
| quit | 退出GDB |
| run | 执行当前被调试的程序 |
| set | 给变量赋值 |
| shell | 不离开GDB就执行UNIX shell命令 |
| step | 执行下一行代码并进入函数内部 |
| watch | 设置监视点,使用户能监视一个变量或表达式的值而不管它何时被变化 |
| list | 列出源文件内容 |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)