段错误是什么意思?是何原因引起的?

描述

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

root@Turbo:linklist# ls
link.c  link.h  main  main.c
root@Turbo:linklist# ./main
链表初始化成功
Segmentation fault (core dumped)
root@Turbo:linklist#
所谓段错误,就是访问了不能访问的内存。

LINUX内核

比如内存不存在,或者受保护等等。 遇到段错误,就得去调试,不管是通过什么手段,一定得先找到哪行代码出现了段错误,然后才能分析修改。

调试段错误的手段很多,对于初学者,我推荐两个,一个是通过打印的方式定位,一个是使用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
");
    if (NULL == h)
    {
        return FAILURE;
    }


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


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


    printf("4444
");
    Node *m = (Node *)malloc(sizeof(Node) * 1);
    if (NULL == m)
    {
        return FAILURE;
    }
    printf("5555
");
    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 
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:
.
Find the GDB manual and other documentation resources online at:
    .


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的调试工具还有很多,如果是初学者,没必要掌握太多,能解决问题就行。

LINUX内核  

 





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分