调试是每个程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度, 是大家共同面对的问题. 可能Windows用户顺口就会说出:用VC呗 :-) , 它提供了设置断点, 单步跟踪等的图形界面, 使调试起来直观易用. 但Linux用户可能要生闷气了 O:-) : 难道我们Linux程序员就只能使用原始的调试方法, 在代码中加入printf信息吗?难道Linux下就没有好的C语言调试工具吗?

当然不是了. GNU早就组织开发了一套C语言编译器(Gcc)和调试工具(Gdb). Gdb虽然没有图形化的友好界面, 但是它强大的功能也足以与微软的VC工具相媲美, 给Linux程序员带来了福音. 下面通过一个简单的例子, 演示一下Gdb的使用流程:

示例文件 demo.c 的源代码如下:

#include <stdio.h>

int sum(int, int);

    int
main()
{
    int result;
    int a = 1, b = 2;
    result = sum(a, b);
    printf("%d + %d = %d\n", a, b, result);
    return 0;
}     int
sum(int a, int b)
{
    return a + b;
}

编译源文件, 生成可执行文件

$ gcc -g -Wall -o demo demo.c

虽然这段程序没有错误, 但调试完全正确的程序可以更加了解Gdb的使用流程. 接下来就启动Gdb进行调试.

注意:

  • Gdb进行调试的是可执行文件, 而不是”.c”源文件, 因此, 需要先通过Gcc编译生成可执行文件才能用Gdb进行调试.
  • 一定要加上选项”-g”, 这样编译出的可执行代码中才包含调试信息, 否则Gdb无法载入该可执行文件.
  • 不能使用 -O2选项对可执行文件进行优化, 因为优化之后可执行文件里的符号表信息将被删除, 这样Gdb就无法找到使可执行文件与源文件之间的关联了, 也就不能调试了.

(1) 启动Gdb

$ gdb demo
GNU gdb (GDB) 7.0-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/wangsheng/tmp/demo/gdb/demo...done.

可以看出, 在Gdb的启动画面中指出了Gdb的版本号, 使用的库文件等头信息, 接下来就进入了由”(gdb)”开头的命令行界面了.

(2) 查看源文件

在Gdb中键入”l”(list的缩写)可以查看所载入的文件, 如下所示:

(gdb) l
1       #include
2
3       int sum(int, int);
4
5           int
6       main()
7       {
8           int result;
9           int a = 1, b = 2;
10          result = sum(a, b);
(gdb) l
11          printf("%d + %d = %d\n", a, b, result);
12          return 0;
13      }
14
15          int
16      sum(int a, int b)
17      {
18          return a + b;
19      }
(gdb) l
Line number 20 out of range; demo.c has 19 lines.

可以看出, Gdb列出的源代码中明确地给出了对应的行号, 这样就可以大大地方便代码的定位.

(3) 设置断点

设置断点是调试程序中一个非常重要的手段, 它可以使程序到一定位置暂停运行. 因此,可以在该位置方便地查看变量的值, 堆栈情况等, 从而找出代码的症结所在.

在Gdb中设置断点非常简单, 只需在”b”后加入对应的行号即可(这是最常用的方式). 如下所示:

(gdb) b 9
Breakpoint 2 at 0x4004f4: file demo.c, line 9.

注意: 该断点的作用是当程序运行到第 9 行时暂停(第 8 行执行完毕, 第 9 行未执行)

(4) 查看断点信息

(gdb) info b
Num     Type           Disp Enb Address            What
2       breakpoint     keep y   0x00000000004004f4 in main at demo.c:9

(5) 运行代码

接下来就可运行代码了, Gdb默认从首行开始运行代码, 可键入”r”(run的缩写)即可. 若想从程序中指定的行开始运行, 可在r后面加上行号.

(gdb) r
Starting program: /home/wangsheng/tmp/demo/gdb/demo Breakpoint 2, main () at demo.c:9
9           int a = 1, b = 2;

可以看到程序运行到断点处就停止了.

(6) 查看变量值
键入p(print的缩写)+变量名即可查看该变量在此时的值

(gdb) p a
$1 = 1
(gdb) p b
$2 = 2
(gdb) p result
$3 = 32767

注意: 这里之所以result是一个莫名其妙的值, 是因为声明result是没有初始化, 其值是不固定的。

(7) 单步执行

单步运行可以使用n(next的缩写)或者s(step的缩写), 它们之间的区别在于: 若有函数调用的时候, s会进入该函数而n不会. 因此, s就类似于VC等工具中的”step in”, n就类似于VC等工具中的”step over”.

如果使用n命令显示如下:

(gdb) n
10          result = sum(a, b);

下面使用 s 命令,跟踪进入 sum 函数:

(gdb) s
sum (a=1, b=2) at demo.c:18
18          return a + b;

可以看出执行 s 命令时进入了sum函数内部, 如果用 n 命令则跳过函数的调用部分

(8) 恢复程序运行

在查看变量值以及堆栈之后, 就可以使用命令c(continue)恢复程序的正常运行了. 这时, 它会把剩余还未执行的程序执行完, 并显示剩余程序的执行结果.

(gdb) c
Continuing.
1 + 2 = 3 Program exited normally.

可以看出, 程序在运行完后退出, 之后程序处于”停止状态”.
说明: 在Gdb中, 程序的运行状态有”运行”,”暂停”和”停止”3种.
其中”暂停”状态是程序遇到了断点或者观察点, 程序暂时停止运行, 而此时函数的地址, 函数参数,
函数内的局部变量都会被压入”栈(Stack)中. 故在这种状态下可以查看函数的变量值等各种属性. 但在函数处于”停止”状态之后,
“栈”就会自动撤销, 它也就无法查看各种信息了

关于Gdb的更多命令, 你可以在启用Gdb后, 输入help命令查看.

Linux下C语言的调试--转的更多相关文章

  1. Linux下C语言的调试 - gdb

    调试是每个程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度, 是大家共同面对的问题. 可能Windows用户顺口就会说出:用VC呗 :-) , 它提 ...

  2. Linux下C语言的调试

    调试是每个程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度, 是大家共同面对的问题. 可能Windows用户顺口就会说出:用VC呗 :-) , 它提 ...

  3. linux 下C语言学习路线

    UNIX/Linux下C语言的学习路线.一.工具篇“公欲善其事,必先利其器”.编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工 ...

  4. Linux基础与Linux下C语言编程基础

    Linux基础 1 Linux命令 如果使用GUI,Linux和Windows没有什么区别.Linux学习应用的一个特点是通过命令行进行使用. 登录Linux后,我们就可以在#或$符后面去输入命令,有 ...

  5. LINUX下C语言编程基础

    实验二 Linux下C语言编程基础 一.实验目的 1. 熟悉Linux系统下的开发环境 2. 熟悉vi的基本操作 3. 熟悉gcc编译器的基本原理 4. 熟练使用gcc编译器的常用选项 5 .熟练使用 ...

  6. [转] linux下的c/c++调试器gdb

    PS:1. 断点C++类函数,用b 命名空间::类名::方法名 2. 编译参数一定要加-g,才可断点调试 http://www.cnblogs.com/xd502djj/archive/2012/08 ...

  7. Unix和Linux下C语言学习指南

    转自:http://www.linuxdiyf.com/viewarticle.php?id=174074 Unix和Linux下C语言学习指南 引言 尽管 C 语言问世已近 30 年,但它的魅力仍未 ...

  8. Linux下C语言编程基础学习记录

    VIM的基本使用  LINUX下C语言编程 用gcc命令编译运行C语言文件 预处理阶段:将*.c文件转化为*.i预处理过的C程序. 编译阶段:将*.i文件编译为汇编代码*.s文件. 汇编阶段:将*.s ...

  9. 【转】Linux基础与Linux下C语言编程基础

    原文:https://www.cnblogs.com/huyufeng/p/4841232.html ------------------------------------------------- ...

随机推荐

  1. Elasticsearch 的一些关键概念

    我更喜欢把 Elasticsearch 作为一种 nosql 去理解,它的一些开发概念和 MongoDB 以及 Redis 没有太大的区别,不过了解 Elasticsearch 中的一些核心概念对于你 ...

  2. 用CSS3把列表项目反转显示

    忘了哪儿的一个题目来着,说是把 一个列表 给翻转序列显示,比如 : 有一个列表如图: 翻转为  回复里面有人机智的使用 CSS3 的 transform:rotate(180deg); 实现了,引发众 ...

  3. Android studio项目预览的时候提示错误ActionBarOverlayLayout

    android studio打开项目(别人的demo),提示页面没法预览.截图如下 根据查询,是主题没法正常显示,需要修改样式.样式文件的路径为res\values\styles.xml,截图如下. ...

  4. 构建NetCore应用框架之实战篇(七):BitAdminCore框架登录功能源码解读

    本篇承接上篇内容,如果你不小心点击进来,建议从第一篇开始完整阅读,文章内容继承性连贯性. 构建NetCore应用框架之实战篇系列 一.简介 1.登录功能完成后,框架的雏形已经形成,有必要进行复习. 2 ...

  5. NRF52840相对于之前的NRF52系列、NRF51系列增加了什么功能

    现在广大客户的蓝牙采用NORDIC越来越多了,NORDIC一直在不断进行技术改进更好的满足市场需求 推出了新款NRF52840.NRF52840更为先进些,支持的功能也多点,比如IEEE802.15. ...

  6. KeyChainWrapper - keychain简单使用

    1 keyChainWrapper是MRC代码,要禁用ARC -fno-objc-arc 2 要导入Security.framework框架 3 获得一个不变的UUID - (BOOL)applica ...

  7. 石头剪刀布Java实现

    java实现石头剪刀布过程 首先来看石头剪刀布的所有可能情况,具体如下图 第一种思路是穷举所有可能,使用if条件语句,略显呆板和麻烦. 第二种思路,因为计算机存的是数字,所以我们可以从数字角度来找规律 ...

  8. 分布式ehcache缓存

    今天在这里了记录一下学习ehcache分布式集群的过程. ehcache的三种最为常用集群方式,分别是 RMI.JGroups 以及 EhCache Server . 这里主要讲一下rmi方式. 1. ...

  9. jvm高级特性(1)(内存泄漏实例)

    jvm内存结构回顾: .8同1.7比,最大的差别就是:元数据区取代了永久代.元空间的本质和永久代类似,都是对JVM规范中方法区的实现. 不过元空间与永久代之间最大的区别在于:元数据空间并不在虚拟机中, ...

  10. Using Time Profiler in Instruments

    要用 release 版本来profile 概述 time profile 是使用采样的方法来统计,而不是记录每一个方法调用的起始和结束,采样间隔是 1 ms.  在上图中,main 函数被采样了 ...