Linux下的gdb调试C或者C++程序

GDB调试

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


1 GDB调试的基础指令

使用gdb调试下面的一段c程序:

#include <stdio.h>
#include <stdlib.h>

#define N 10

//使用1~20的随机数初始化数组
void init_arr(int* arr, int n)
{
    for(int i=0;i<n;i++){
        arr[i]=rand()%20+1;
    }
}
//打印数组中的值
void print_arr(int* arr, int n)
{
    for(int i=0;i<n;i++){
        printf("arr[%d]=%d\n",i,arr[i]);
    }
}
//选择排序
void select_sort(int* arr, int n)
{
    int temp=0;
    for(int i=0;i<n;i++){
        int max=arr[i];
        int max_index=i;
        for(int j=i;j<n;j++){
            if(arr[j]>max){
                max=arr[j];
                max_index=j;
            }
        }
        temp=arr[i];
        arr[i]=arr[max_index];
        arr[max_index]=temp;
    }
}
//主函数
int main(void)
{
    int arr[N];
    srand(time(NULL));
    init_arr(arr,N);
    print_arr(arr,N);
    select_sort(arr,N);
    printf("********After sort:********:\n");
    print_arr(arr,N);
    return 0;
}

首先介绍gdb中常用命令及示例:

1)进入gdb调试的方法,首先使用gcc生成带有调试信息的可执行文件,该文件比普通可执行文件要大;使用-g选项

gcc test1.c -o a.out -g

2)使用gdb调试a.out程序,使用命令

gdb a.out

3)在gdb中输入 l或者list命令,可以查看被调试程序的源码,但第一次输入一般显示在内存中尚存的代码;

4)使用list 1或者l 1命令,可以让源码从头开始显示;然后再输入 l 命令,显示后续的数值;

5)在所关注的地方插入断点,使用命令

b 23 #代表在23行插入断点,b是break的简写

6)输入命令 r 代表run,使程序运行,程序命中断点

7)使用n代表执行下一步,使用s代表单步执行,两者在简单的赋值或者运算语句的效果是相同的,但是对于函数语句的执行效果不同

n/next代表跳过函数执行的过程,直接到函数执行完成的下一步;

s/step会进入函数执行过程;

一般在执行系统调用函数的时候,一般会使用n,因为这些函数一般情况下是正确的,而且系统调用有可能内部嵌套函数比较多;执行自定义函数的时候,一般会使用s,因为这些函数是自己写的,可能会有逻辑错误,所以单步执行较为合适。

8)打印调试过程的信息

使用 p/print var #var代表变量名称

例如 p i #代表查看程序中i的值

9)continue是继续执行断点的后续指令;

若后面没有断点,将直接显示程序执行结果;

10)退出gdb当前调试;

gdb使用的大前提:是程序是自己写的,这样才对变量和变量的值的正确性有所判断

2 GDB调试的辅助指令

将上面的程序修改为下面的样式:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 10

//使用1~20的随机数初始化数组
void init_arr(int* arr, int n)
{
    for(int i=0;i<n;i++){
        arr[i]=rand()%20+1;
    }
}
//打印数组中的值
void print_arr(int* arr, int n)
{
    for(int i=0;i<n;i++){
        printf("arr[%d]=%d\n",i,arr[i]);
    }
}
//选择排序
void select_sort(int* arr, int n)
{
    int temp=0;
    for(int i=0;i<n;i++){
        int max=arr[i];
        int max_index=i;
        for(int j=i;j<n;j++){
            if(arr[j]>max){
                max=arr[j];
                max_index=j;
            }
        }
        temp=arr[i];
        arr[i]=arr[max_index];
        arr[max_index]=temp;
    }
}
//主函数
int main(void)
{
    int arr[N];
    char* str="helloworld";
    srand(time(NULL));
    init_arr(arr,N);
    str[5]='W';
    print_arr(arr,N);
    select_sort(arr,N);
    printf("********After sort:********:\n");
    print_arr(arr,N);
    return 0;
}

在主程序中有一行,将str[5]=‘W’,试图修改只读字符,就会发生段错误;对于段错误,在编译中并不会发生任何异常,但是在执行时,会显示“段错误“;

在gdb中调试段错误,无需单步调试,只需要直接运行程序,gdb会显示出现段错误的位置;

命令简写作用
helph按模块列出命令类
help class查看某一具体类型的具体命令
listl查看代码,可跟行号和函数名
quitq退出gdb
runr全速运行程序
start单步执行,运行程序,停在第一行执行语句
nextn逐过程执行,不会跳转到函数内部
steps逐语句执行,遇到函数,会跳转到内部
backtracebt查看函数的调用的栈帧和层级关系
infoi查看GDB内部局部变量的数值,info breakpoints
framef切换函数的栈帧
finish切换当前函数,返回到函数调用点 也可以用于结束主函数
set设置变量的值 set var n=100
run argv[1] argv[2]调试时命令行传参
printp打印变量和地址
breakb设置断点,可根据行号
set args用于给main函数传入参数,也可使用run 参数,可以传入参数
deleted删除断点 d breakpoints num
display设置观察变量(每次执行都重新显示变量的值)
undisplay取消观察变量(使用编号取消)
continuec继续全速运行下面的代码
enable breakpoints启动断点
disable breakpoints禁用断点
x查看内存 x /20xw 显示20个单元,16进制,4字节每单元
watch被设置观察点的变量发生修改时,打印显示
i watch显示观察点
coreulimit -c 1024 开启core文件,调试时 gdb a.out core

b 24 if i=5 (条件断点)

(gdb) b 24 if i=5
Breakpoint 3 at 0x12cb: file gdbtest1.c, line 24.
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x00000000000013d9 in main at gdbtest1.c:42
2       breakpoint     keep y   0x0000000000001441 in main at gdbtest1.c:48
3       breakpoint     keep y   0x00000000000012cb in select_sort at gdbtest1.c:24
        stop only if i=5

此时打印变量i的值,显示 i=5

ptype var 查看变量类型

栈帧:随着函数调用而在stack上开辟的一片内存空间,用于存放函数调用时产生的局部变量和临时值

有多少个函数,就会有多少个栈帧(main函数也有栈帧),函数调用结束之后,栈帧消失。

每个函数只能访问自己的栈帧范围,而不能范围其它函数的栈帧。当然可以访问全局变量和全局函数;

当在一个函数的范围内,试图使用ptype查看另外一个不在这个函数范围内的变量的类型的时候,会出现错误。

此时,可以首先通过bt/backtrace查看栈帧列表,得到目前正在运行的栈帧,及其序号

之后通过 f/frame num切换栈的序号,然后可以再次调用ptype查看数据类型

3 GDB调试的常见错误

1)调试显示没有符号表被读取,请使用“file”命令

原因:说明在使用gcc生成可执行程序的时候,没有添加-g选项

解决方法:添加-g选项,重新生成可执行文件

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值