用gdb来学习c语言(linux环境下)

本文转自:http://blog.csdn.net/physicsdandan/article/details/8076446

gdb的使用介绍

先创建一个如下的C程序,minimal.c

int main()
{
    int i = 1337;
    return0;
}

这个程序不做任何事情并且没有一个printf语句。现在我们要鼓足勇气来通过gdb学习C语言了!

编译这个程序时加上-g参数,这样gdb就可以调试信息了。然后执行下面命令。

$ gcc -g minimal.c -o minimal
$ gdb minimal

你会发现你已经看到了gdb的prompt。我许诺过给你一个REPL,这里就是

(gdb) print 1 + 2
$1 = 3

真令人惊讶!GDB建立了一个print,并且打印出了一个C的表达式。如果你不能确定一个GDB指令到底的功能,可以试着在GDB prompt下执行help name-of-the-command。

这里有一些更有趣的示例:

(gbd) print (int) 2147483648
$2 = -2147483648

我们先忽略为什么2147483648 == -2147483648。这里的重点是甚至一个计算在C中都可能会非常棘手,而GDB了解C的计算。

现在我们来在main函数中设置一个断点,然后开始运行程序:

(gdb) break main
(gdb) run

这个程序在第3行,变量i被初始化的时候时候暂停了。尽管i还没有被初始化,我们仍然可以用print命令看到它的值。

(gdb) print i
$3 = 32767

在C中,没有被初始化的局部变量的值是被定义的,所以你的GDB可能会打印出不同的值来!

我们可以用next 命令来执行当前行:

(gdb) next
(gdb) print i
$4 = 1337

用x命令查看内存

变量在c内存标签连续块。一个变量的内存块是由两个数字来决定的——块的第一个byte的地址和块的大小(以bytes为单位)。变量的大小是由变量的类型决定的。

一个C的特性是你需要直接访问变量的内存块。&运算符可以计算出一个变量的地址,而sizeof运算符可一个计算出变量占用的内存大小。

你可以在GDB中玩转这两个概念:

(gdb) print &i
$5 = (int *) 0x7fff5fbff584
(gdb) print sizeof(i)
$6 = 4

总而言之,i的内存块开始于0x7fff5fbff5b4,占用4个字节。我上面提到过一个变量的大小是由变量类型决定的。其实sizeof 操作可以直接作用在类型上:

(gdb) print sizeof(int)
$7 = 4
(gdb) print sizeof(double)
$8 = 8

这意味着,至少在你的机器上,int变量占用4个字节的空间,double占用8个。
使用GDB中的x命令,将会使它变成一个直接测试内存的强大的工具。X命令测量内存开始与一个特定的地址。它配备了一些格式化命令,提供精确的控制来实现你想检查多少字节,以及如何你想将它们打印出来。如果有疑问,在GDB prompt中执行help x。

&操作可以得到一个变量的地址,所以这意味着我们可以用x来执行&i并且来观察i值的原始字节。

(gdb) x/4xb &i
0x7fff5fbff584: 0x39    0x05    0x00    0x00

这个标志表示我想以4 个16进制数,一次一个字节的格式来显示。我已经选择查看4个字节,因为i的内存大小就是4个字节。输出信息中逐字节显示i的原始地址。

一个需要紧记于心的细节是如果在intel机器上的逐字节测试,其字节是按“little-endian”排序的。

一种更能清楚认识到这个特性的方法是赋予i一个更有趣的值,然后重新测试它的内存块:

(gdb) set var i = 0x12345678
(gdb) x/4xb &i
0x7fff5fbff584: 0x78    0x56    0x34    0x12

用ptype命令查看类型

Ptype也许是我最喜欢的命令了。它告诉你一个C表达式的类型:

(gdb) ptype i
type = int
(gdb) ptype &i
type = int *
(gdb) ptype main
type = int (void)

类型在C中可以会非常复杂,但是ptype可以让你时时的查看它们。

指针和数组

数组是C中一个很难以捉摸的概念。这个章节的目的就是写一个简单的程序,然后在GDB中运行、测试好让你开始理解数组。

Arrays.c的代码如下:

int main()
{
    int a[] = {1,2,3};
    return0;
}

编译时代上-g参数,然后在GDB下运行它,执行next命令来完成初始化:

$ gcc -g arrays.c -o arrays
$ gdb arrays
(gdb) break main
(gdb) run
(gdb) next

在这时候你应该能打印变量a的内容和查看它的类型:

(gdb) print a
$1 = {1, 2, 3}
(gdb) ptype a
type = int [3]
现在我们的程序在gdb中已经设置正确,我们现在首先要做的事是用x命令来查看a是什么样子:
(gdb) x/12xb &a
0x7fff5fbff56c: 0x01  0x00  0x00  0x00  0x02  0x00  0x00  0x00
0x7fff5fbff574: 0x03  0x00  0x00  0x00
这里是的说明x的内存块是从地址 0x7fff5fbff5dc开始的。a[0]储存在开始的四个字节中,下一个元素a[1]储存在紧随其后的四个字节中。最后四个字节储存a[2]。其实你可以用sizeof来查看a的内存大小,它占用了12个字节。
(gdb) print sizeof(a)
$2 = 12
这这里,数组似乎相当数组。他们有自己的数组类型,在内存以中连续储存他们的元素。然而,在有些情况下,数组表现的和指针非常相似!举个例子,我们可以对a进行指针运算:
 (gdb) print a + 1
$3 = (int *) 0x7fff5fbff570
a+1的结果是一个指向int型的指针并且其内存地址是0x7fff5fbff570是。此时,你应该条件反射地用x命令来查看此指针:
(gdb) x/4xb a + 1
0x7fff5fbff570: 0x02  0x00  0x00  0x00

注意, 0x7fff5fbff570比 a的首字节地址0x7fff5fbff56c大4。考虑到int占用4个字节,这意味着a+1是一个指向int型的指针。

实际上在C语言中,数组索引是指针运算的语法糖。a[i]和 *(a+i)等价。你可以在gdb中试试:

(gdb) print a[0]
$4 = 1
(gdb) print *(a + 0)
$5 = 1
(gdb) print a[1]
$6 = 2
(gdb) print *(a + 1)
$7 = 2
(gdb) print a[2]
$8 = 3
(gdb) print *(a + 2)
$9 = 3
我们看到在某些情况下它像一个数组,有些情况它又像一个指向第一个元素的指针。到底发生了什么?
答案就是当一个数组的名字被用在C的表达式中,它退化成了一个指向头元素的指针。但两种情况除外:当一个数组名被传递给了sizeof和当一个数组名被用在&操作下。
这是一个很有趣的问题,当一个数组名传递给&操作的时候,它没有退化成一个指针:指针和退化的指针和&a有什么不同吗?
从数值上看,他们都代表同一个地址:
 (gdb) x/4xb a
0x7fff5fbff56c: 0x01  0x00  0x00  0x00
(gdb) x/4xb &a
0x7fff5fbff56c: 0x01  0x00  0x00  0x00

然而他们的类型是不同的。我们已经看到一个a的退化指针是指向它的头元素,所以个它的类型肯定是int*。而对于&a,我们可以直接问gdb:

 (gdb) ptype &a
type = int (*)[3]

&a是一个指向拥有3个整型元素的数组指针。这里意味着:a在&操作下并没有退化,a的类型是int[3]。

你可以个通过指针运算来观察a的退化指针和&a的区别:

 (gdb) print a + 1
$10 = (int *) 0x7fff5fbff570
(gdb) print &a + 1
$11 = (int (*)[3]) 0x7fff5fbff578

注意到a加1的结果是向后加4个字节,相对的&a加1的结果是向后加12!

一个退化指针实际上是指向&a[0]的:

 (gdb) print &a[0]
$11 = (int *) 0x7fff5fbff56c

总结

希望我能使你明白gdb是学习C的一个便捷式的工具。你可以打印出表达式的值,查看内存的原始字节,还可以使用ptype查看变量类型。.

如果你打算用gdb来学习C,我有以下几点建议:

1.     使用gdb来学习 Ksplicepointer challenge.

2.     探索结构体是怎么储存在内存中的,它和数组又有什么不同?

3.     用gdb的disassemble来学习汇编语言编程!一个最有趣的练习是探索函数调用过程的栈的工作方式。

4.     看看gdb的“tui”模式,它提供一个在常规gdb上的ncurses视图层。在OS X,你可能要从gdb的源码安装。

 

补充(gdb窗口模式)

进入gdb窗口模式:list命令,或者layout src命令,或者tui命令

其他代码窗口相关命令:

info win    显示窗口的大小
layout next 切换到下一个布局模式
layout prev 切换到上一个布局模式
layout src   只显示源代码
layout asm   只显示汇编代码
layout split 显示源代码和汇编代码
layout regs   增加寄存器内容显示
focus cmd/src/asm/regs/next/prev 切换当前窗口
refresh     刷新所有窗口
tui reg next 显示下一组寄存器
tui reg system 显示系统寄存器
update      更新源代码窗口和当前执行点
winheight name +/- line 调整name窗口的高度
tabset nchar 设置tab为nchar个字符

 

info win    显示窗口的大小

layout next 切换到下一个布局模式
layout prev 切换到上一个布局模式
layout src   只显示源代码
layout asm   只显示汇编代码
layout split 显示源代码和汇编代码
layout regs   增加寄存器内容显示
focus cmd/src/asm/regs/next/prev 切换当前窗口
refresh     刷新所有窗口
tui reg next 显示下一组寄存器
tui reg system 显示系统寄存器
update      更新源代码窗口和当前执行点
winheight name +/- line 调整name窗口的高度
tabset nchar 设置tab为nchar个字符

时间: 2024-10-31 00:22:27

用gdb来学习c语言(linux环境下)的相关文章

Linux环境下段错误的产生原因及调试方法小结_C 语言

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的"段错误"(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个

file-c语言在linux环境下进行文件复制的代码,使用时出错,求各位大神帮忙解答下

问题描述 c语言在linux环境下进行文件复制的代码,使用时出错,求各位大神帮忙解答下 #include#include#include#include#include#include#include#define BUFSIZE 4096#define COPYMODE 0644 void oops(char *char *);void *emalloc(size_t);void do_copy(char *char *);void copydir(char *char *);int isdi

linux 环境下 isap 环境如何搭建 (开发 语言 java ,b/s模式)

问题描述 linux环境下isap环境如何搭建(开发语言java,b/s模式)谢了 解决方案 解决方案二:该回复于2011-04-19 15:05:21被版主删除

LINUX学习(三)在Linux环境下运行DOS命令

          Linux系统提供了一组称为mtools的可移植工具,可以让用户轻松地从标准的DOS软盘上读.写文件和目录.它们对DOS和Linux环境之间交换文件非常有用.它们是不具备共同的文件系统格式的系统之间交换文件的有力手段.             对于一个MS-DOS的软盘,只要把软盘放在软驱中,就可以利用mtools提供的命令来访问软盘上的文件. mtools的主要命令如下: mcd 目录名 改变MSDOS目录: mcopy 源文件 目标文件 在MSDOS和Unix之间复制文件

Linux环境下段错误的产生原因及调试方法小结

最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的"段错误"(Segmentation Fault).借此机会系统学习了一下,这里对Linux环境下的段错误做个小结,方便以后同类问题的排查与解决. 1. 段错误是什么 一句话来说,段错误是指访问的内存超出了系统给这个程序所设定的内存空间,例如访问了不存在的内存地址.访问了系统保护的内存地址.访问了只读的内存地址等等情况.这里贴一个

【转】linux环境下的c++编程

linux环境下的c++编程        就C++开发工具而言,与Windows下微软(VC, VS2005等)一统天下相比,Linux/Unix下C++开发,可谓五花八门,各式各样.Emacs, vi, eclipse, anjuta,kdevelop等层出不穷.         Windows下,开发工具多以集成开发环境IDE的形式展现给最终用户.例如,VS2005集成了编辑器,宏汇编ml,C /C++编译器cl,资源编译器rc,调试器,文档生成工具, nmake.它们以集成方式提供给最终

Linux环境下mysql5.7.13安装教程_Mysql

本文实例为大家分享了Linux mysql5.7.13安装教程,供大家参考,具体内容如下 1.准备cmake-3.6.0.tar.gz  bison-3.0.4.tar.gz  mysql-5.7.13.tar.gz (http://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.13.tar.gz)  2.安装cmake 和 bison首先可以查看下是否安装了 cmake  # rpm -qa |grep cmake # tar zxvf cma

【OS】Linux环境下配置固定IP地址

[OS]Linux环境下配置固定IP地址 ---red hat重启网卡service network restart/etc/rc.d/init.d/network restart ---suse重启网卡service network restartrcnetwork restart/etc/rc.d/init.d/network restart ----卸载网卡ifconfig eth0 down --- 单独重启网卡ifdown eth0 && ifup eth0ifconfig et

【云和恩墨】性能优化:Linux环境下合理配置大内存页(HugePage)

原创 2016-09-12 熊军  [云和恩墨]性能优化:Linux环境下合理配置大内存页(HugePage) 熊军(老熊) 云和恩墨西区总经理 Oracle ACED,ACOUG核心会员 PC Server发展到今天,在性能方面有着长足的进步.64位的CPU在数年前都已经进入到寻常的家用PC之中,更别说是更高端的PC Server:在Intel和AMD两大处理器巨头的努力下,x86 CPU在处理能力上不断提升:同时随着制造工艺的发展,在PC Server上能够安装的内存容量也越来越大,现在随处

.NetCore~Linux环境下部署

NetCore正式版已经出现有段时候了,Windows下使用vs2015开发.netCore应用程序,然后通过dotnet程序开启WEB服务,用着很像node.js,当然我们不会于只局限于windows,.netCore自出生以来,就是为了跨平台而引入了,Linux是一个更稳定和通用的生产环境,下面我们一步一步在centos(Linux系统的一种)搭建我们的.NetCore运行环境! 连接Linux(Centos)操作系统工具SSH 可以下载一个windows环境的SSH工具,这样操作linux