如何调试段错误?

刚接触指针的时候,经常会遇到段错误。

root@Turbo:linklist# ls
link.c  link.h  main  main.c
root@Turbo:linklist# ./main
链表初始化成功
Segmentation fault (core dumped)
root@Turbo:linklist#

所谓段错误,就是访问了不能访问的内存。

在这里插入图片描述
比如内存不存在,或者受保护等等。

遇到段错误,就得去调试,不管是通过什么手段,一定得先找到哪行代码出现了段错误,然后才能分析修改。

调试段错误的手段很多,对于初学者,我推荐两个,一个是通过打印的方式定位,一个是使用gdb。

打印方法最简单。

root@Turbo:linklist# ls
link.c  link.h  main  main.c
root@Turbo:linklist# ./main
链表初始化成功
Segmentation fault (core dumped)
root@Turbo:linklist#

比如这里有个链表的代码,代码量大概300行,从现象可以看出,链表的初始化成功,紧接着就出现了段错误。

于是我们大概能推测出问题出在了链表的插入操作上。

int insert_link(Node *h, int p, int n)
{
    if (NULL == h)
    {
        return FAILURE;
    }

    //把指针移动到要插入位置的前一个位置
    Node *q = h;
    int k = 1;
    while (k < p)      //q != NULL  防止位置p太大
    {
        q = q->next;
        k++;
    }

    //判断位置是否合法
    if (q == NULL || k > p)    //位置太大  位置太小
    {
        return FAILURE;
    }

    Node *m = (Node *)malloc(sizeof(Node) * 1);
    if (NULL == m)
    {
        return FAILURE;
    }

    m->data = n;
    m->next = q->next;
    q->next = m;

    return SUCCESS;
}

找到链表的插入操作,可以把它分成几个功能,入参判断,移动指针,判断位置是否合法,申请新节点,修改指针域。

在每个功能前面加上一些打印,随便打印什么都行。


int insert_link(Node *h, int p, int n)
{
    //printf("1111\n");
    if (NULL == h)
    {
        return FAILURE;
    }

    //printf("2222\n");
    //把指针移动到要插入位置的前一个位置
    Node *q = h;
    int k = 1;
    while (k < p)      //q != NULL  防止位置p太大
    {
        q = q->next;
        k++;
    }

    printf("3333\n");
    //判断位置是否合法
    if (q == NULL || k > p)    //位置太大  位置太小
    {
        return FAILURE;
    }

    printf("4444\n");
    Node *m = (Node *)malloc(sizeof(Node) * 1);
    if (NULL == m)
    {
        return FAILURE;
    }
    printf("5555\n");
    m->data = n;
    m->next = q->next;
    q->next = m;

    return SUCCESS;
}

再次运行,程序输出了1和2。

root@Turbo:linklist# gcc main.c link.c -o main
root@Turbo:linklist# ./main
链表初始化成功
1111
2222
Segmentation fault (core dumped)
root@Turbo:linklist#

于是,问题再次被缩小,应该是移动指针的时候出了问题。


//把指针移动到要插入位置的前一个位置
    Node *q = h;
    int k = 1;
    while (k < p)      
    {   
        q = q->next;
        k++;
    }

这几行代码,能跟指针扯上关系的,应该就是第 6 行了。当然,这只是发现了段错误在哪,具体怎么修改,还得根据你的业务逻辑继续分析。

通过打印定位问题确实比较慢。如果代码量比较大,又不能大概判断出问题所在,可以借助一些工具,常用的比如GDB


root@Turbo:linklist# gcc main.c link.c -o main -g
root@Turbo:linklist# gdb main
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 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".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from main...
(gdb) run
Starting program: /root/test/linklist/main 
链表初始化成功
1111
2222

Program received signal SIGSEGV, Segmentation fault.
0x0000555555555783 in insert_link (h=0x5555555592a0, p=5, n=9) at link.c:42
42      q = q->next;
(gdb)

编译的时候加上-g选项,直接使用gdb加上文件名,run启动程序,一眼就能看出错误在第42行。

当然了,这个程序是我故意写的段错误,有些段错误发生在库里面,并不能很直观的看出来在哪一行。这就需要借助断点、单步调试等等操作。

类似GDB的调试工具还有很多,如果是初学者,没必要掌握太多,能解决问题就行。

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值