Linux下C开发——gcc,gdb的使用
作者:zccst
我们学习的过程应该是一个积累的过程,有无到有,又少到多,而不能像下山的猴子。9月份的时候是第一次系统学习gcc, gdb, makefile(详见前面博客)。如今作为嵌入式系统的一个组成部分,决定重学一次,感觉理解加深了一些。
gcc编译器
前提编辑工具的使用:vi vim Emacs等。
理论篇
gcc可以使程序员灵活地控制编译过程。编译过程一般可以分为下面四个阶段,每个阶段分别调用不同的工具进行处理,如图9-18所示。
Linux系统中可执行文件有两种格式。第一种格式是a.out格式,这种格式用于早期的Linux系统以及 Unix系统的原始格式。a.out来自于Unix C编译程序默认的可执行文件名。当使用共享库时,a.out格式就会发生问题。把a.out格式调整为共享库是一种非常复杂的操作,由于这个原因,一种新的文件格式被引入Unix系统5的第四版本和Solaris系统中。它被称为可执行和连接的格式(ELF)。这种格式很容易实现共享库。
ELF格式已经被Linux系统作为标准的格式采用。gcc编译程序产生的所有的二进制文件都是ELF格式的文件(即使可执行文件的默认名仍然是a.out)。较旧的a.out格式的程序仍然可以运行在支持ELF格式的系统上。
注:GCC 支持数种调试和剖析选项。在这些选项里,最常用的是-g和-pg选项。
实践篇
gcc的使用格式:gcc[options][filenames]
其中filenames为所要编译的程序源文件。options见下文gcc的主要参数。
当使用gcc时,gcc会完成预处理、编译、汇编和连接。前三步分别生成目标文件,连接时,把生成的目标文件链接成可执行文件。gcc可以针对支持不同的源程序文件进行不同处理,文件格式以文件的后缀来识别。
vi hello.c
一、常见步骤:
对于只有一个源文件的简单程序,常常只有编译,运行两步。
1,gcc hello.c -o hello
2,./hello
二、gcc编译流程
gcc and g++分别是gnu的c & c++编译器 gcc/g++在执行编译工作的时候,总共需要4步
hello.c (源码)
1,hello.i 生成预处理文件,
参数是“-E”,把hello.c -> hello.i。完整命令为gcc hello.c -o hello.i -E
2,hello.s 编译生成汇编文件,
参数是“-S”,把hello.i -> hello.s。完整命令为gcc hello.i -o hello.s -S
3,hello.o 将汇编文件变为目标代码,
参数是“-c”,把hello.s -> hello.o。完整命令为gcc hello.s -o hello.o -c
4,hello 链接目标代码,生成可执行程序,
参数无, 把hello.o-> hello。 完整命令为gcchello.o -o hello
./hello (运行)
三、gcc的主要参数
1,总体参数
-E 只进行预编译,不做其他处理
-S 只是编译不汇编,生成汇编代码
-c 只是编译不链接,生成目标文件“.o”
-o file 把输出文件输出到file里
-g 在可执行程序中包含标准调试信息
-v 打印出编译器内部编译各过程的命令行信息和编译器的版本
-I dir 在头文件的搜索路径列表中添加dir目录
-L dir 在库文件的搜索路径列表中添加dir目录
-static 链接静态库
-llibrary 连接名为library的库文件
2,警告和出错参数。
-w 关闭警告
-ansi 显示不符合ANSI C标准语法的警告信息
-pedantic
-Wall 跟踪调试的有力工具,最后养成使用此参数的习惯。
3,查找选项
gcc一般使用默认路径查找头文件和库文件。如果文件所用的头文件或库文件不在缺省目录下,则编译时要指定它们的查找路径。
-I选项:指定头文件的搜索目录
例:gcc –I/export/home/st–o test1 test1.c
-L选项:指定库文件的搜索目录
例:gcc –L/usr/X11/R6/lib –o test1 test1.c
4,优化参数。
通过参数“-On”来生成优化代码。其中n是一个代表优化级别的整数,较典型范围是从0到2或3.数字越大优化的等级越高,程序运行速度越快。常用-O2,因为它在优化长度,编译时间和代码大学之间取得一个比较理想的平衡点。比较:1-8.c(代码略)
gcc 1-8.c -o 1-8
time ./1-8
gcc 1-8.c -o 1-8 -O2
time ./1-8
注:如下场合应避免优化代码。
(1)程序开发时。只有到软件发行或开发结束时,才考虑对最终生成的代码进行优化。
(2)资源受限时。如内存资源非常紧张时(一些实时嵌入式设备)。
(3)跟踪调试时。优化可能会删除、改写或重组代码,从而使跟踪调试变得异常困难。
gdb调试器
理论篇
gdb调试的不是.c源文件而是可执行文件,然而,并不是所有的可执行文件都可以用gdb调试。如果要让产生的可执行文件可以用来调试,需在执行gcc指令编译程序时,加上-g参数,指定程序在编译时包含调试信息。调试信息包含程序里的每个变量的类型和在可执行文件里的地址映射以及源代码的行号。gdb 利用这些信息使源代码和机器码相关联。
实践篇
gdb使用格式:gdb filename
其中,filename是要调试的可执行文件。用这种方式运行gdb可以直接指定想要调试的程序。这和启动gdb后执行file filename(file命令:装入想要调试的可执行文件)命令效果完全一样。也可以用gdb去检查一个因程序异常终止而产生的core文件,或者与一个正在运行的程序相连。
gdb支持很多的命令且能实现不同的功能,这些命令从简单的文件装入到允许你检查所调用的堆栈内容的复杂命令。
1,编辑源文件。
例如,vi 1-9
添加如下内容
#include <stdio.h>
int min(int x, int y);
int main()
{
inta1, a2, min_int;
printf("please input the frist int number:/n");
scanf("%d", &a1);
printf("please input the second int number:/n");
scanf("%d", &a2);
min_int = min(a1, a2);
printf("the min number is:%d/n", min_int);
return 0;
}
int min(int x, int y)
{
if(x < y)
return x;
else
return y;
}
2,编译时要加上选项“-g”,这样编译出的可执行代码才包含调试信息。
gcc 1-9.c -o 1-9 -g
3,进入gdb调试环境。
gdb 1-9
回车就进入了gdb调试模式。在gdb的调试环境中,提示符是“(gdb)”。
4,用gdb调试程序。
(1)查看源文件
语法:'l'是list缩写。list<行号>|<函数名>。查看源代码,一次显示10行
命令 (gdb)l
(2)设置断点
语法:break 行号|函数名<条件表达式>
本例可以输入命令
(gdb)b min 在自定义的min函数出设置断点。
(gdb)b 17 功能同上
(3)查看断点信息
语法:info break # info命令是显示XX信息,常用有files,func,local,proc.
命令 (gdb)info b
(4)运行程序
语法:run # 执行当前被调试的程序
命令 (gdb)r
注:gdb默认从第一行开始运行,如果要从出现中指定行开始运行,只需输入“r”+行号。
(5)查看变量值
语法:p 变量名。程序运行到断点处会自动暂停,此时可查看指定变量的值。
本例命令
(gdb)p a1
(gdb)p a2
(gdb)p min_int
调试程序时,如需修改变量值,可在程序运行至断点处是,输入“set 变量=设定值”。
例,给变量“a2”赋值11,输入“set a2=11”。
(6)单步调试
语法:
“n”(next),若有函数,不进入函数调用。
“s”(step),若有函数,则进入函数调用。
(7)继续运行程序
语法:continue
命令 (gdb)c
(8)退出gdb调试环境。
语法:quit
命令 (gdb)q
其他常用命令还有:
Tbreak命令:设置临时断点。它的语法与break相同。区别在于用tbreak设置的断点执行一次之后立即消失。
watch命令:设置监视点,监视表达式的变化。
awatch命令:设置读写监视点。当要监视的表达式被读或写时将应用程序挂起。它的语法与watch命令相同。
rwatch命令:设置读监视点,当监视表达式被读时将程序挂起,等侍调试。此命令的语法与watch相同。
display命令:在应用程序每次停止运行时显示表达式的值。
print命令:显示表达式的值。
delete命令:删除断点。指定一个断点号码,则删除指定断点。不指定参数则删除所有的断点。
Shell命令:执行Linux Shell命令。
make命令:不退出gdb而重新编译生成可执行文件。
注:这些指令可以在以后使用过程中查,不必死记硬背。以后用得多了,自然就会记住。