C程序中段错误的几种原因总结

Posted by Dandan on November 12, 2022

前言

是不是都被程序中的段错误吓一跳,Segmentation fault (core dumped),这种报错一看吓一跳, 今儿就把遇到的坑 加上网上查到的资料整理一下。

常见的原因

  • 空指针解引用: 当你尝试使用未初始化或者已经释放的指针时,会导致段错误。

  • 数组越界: 访问超出数组边界的元素,或者使用负数作为数组索引会导致段错误。

  • 栈溢出: 递归函数调用层数过多,导致栈空间耗尽,会触发段错误。

  • 访问未分配的内存: 使用malloc、calloc 或 new 分配内存后,如果没有释放或者访问了已经释放的内存,会导致段错误。

  • 字符串操作错误: C中的字符串是以null终止的字符数组,如果没有正确终止字符串(缺少\0),或者操作字符串时越界,会导致段错误。

  • 访问已经释放的内存: 在使用free释放内存后,再次访问已释放的内存块会导致段错误。

  • 函数指针问题: 使用未初始化或者无效的函数指针调用函数会导致段错误。

  • 栈和堆混淆: 将栈上的局部变量的地址返回给调用者,然后在函数返回后继续使用这个地址,可能导致段错误。

  • 非法的内存访问: 例如将一个整数当作函数指针或者对象指针进行访问。

  • 缓冲区溢出: 输入的数据超过了程序为其分配的缓冲区大小,可能会覆盖其他内存区域,引发段错误。

  • 使用未初始化的变量: 读取未初始化的变量的值可能导致不可预测的结果,有时候也会触发段错误。

  • 多线程同步问题: 如果多个线程同时访问共享内存而没有适当的同步机制,可能会导致段错误。

常见的排查手段:

  • printf; 加打印,通过语句中加打印,如果一句输出,下一句没有输出,出现在段错误,说明是在那一句产生了短错误, 这种只适用于代码量小的项目中。

  • gdb调试;通过gdb和core dump结合使用找到引起段错误的位置;

  • Valgrind内存检测工具; 该工具可以检测由于内存问题因为的段错误。

  • code review;同行评审也是一个不错的办法 。

详细分类是以下几种:

  • 核实错误位置: 首先,确定段错误发生的具体位置。你可以使用调试器(如GDB)来执行程序并查看在哪一行代码触发了段错误。

  • 查看核心转储: 如果你的程序崩溃了,可以使用coredumpctl(Linux)或者ulimit -c unlimited来启用核心转储文件的生成。然后,通过GDB来分析核心转储文件,以了解崩溃时的程序状态。

  • 检查内存访问: 使用内存检测工具,如Valgrind,来检测内存访问错误,包括未初始化的变量、内存泄漏和缓冲区溢出。

  • 检查指针和数组: 确保指针没有被解引用前未初始化或者被释放。同时,检查数组的索引是否越界。

  • 检查函数调用: 确保函数指针的合法性,避免调用已释放的函数指针。

  • 查找空指针: 检查是否有未初始化的指针或者指针已经被释放但仍然被访问的情况。

  • 验证字符串操作: 确保字符串以null终止,并避免越界访问或者使用不安全的字符串函数。

  • 检查内存分配和释放: 确保使用malloc、calloc、free等函数正确分配和释放内存,并避免多次释放同一块内存。

  • 栈溢出检查: 如果怀疑栈溢出,可以增加栈大小或者优化递归算法,同时确保没有无限递归。

  • 多线程问题排查: 如果程序使用多线程,确保线程之间的同步和互斥正确,避免竞争条件。

  • 编译选项: 检查编译选项,确保没有关闭关键的安全检查或开启了不安全的选项。

  • 代码审查: 请其他开发者检查你的代码,有时候一个新的视角可以快速找到问题。

  • 日志和调试输出: 在代码中添加详细的日志和调试输出,以便在出现问题时更容易追踪错误。

gdb + coredump方法

首先查看本机的core 文件数量(core file size)不能为0:

~/work/test ❯ ulimit -a                                                                                                                                                                                   
-t: cpu time (seconds)              unlimited
-f: file size (blocks)              unlimited
-d: data seg size (kbytes)          unlimited
-s: stack size (kbytes)             8192
-c: core file size (blocks)         0
-m: resident set size (kbytes)      unlimited
-u: processes                       7606
-n: file descriptors                1024
-l: locked-in-memory size (kbytes)  65536
-v: address space (kbytes)          unlimited
-x: file locks                      unlimited
-i: pending signals                 7606
-q: bytes in POSIX msg queues       819200
-e: max nice                        0
-r: max rt priority                 0
-N 15:                              unlimited

以下命令设置成无限制:

ulimit -c unlimited

查看设置成功:

ulimit -c                                                                                                                                                                                 
unlimited

修改core文件位置, 文件/etc/sysctl.conf添加配置:

kernel.core_pattern=core-%e-%p-%t

sysctl -p 让配置立刻生效。
代码如下:

#include <stdio.h>  

#include <stdlib.h>

int main(void)
{
    char *ptr;
    *ptr = "dandan""";

    return 0;
}

编译生成core dump并调试

gcc -ggdb -o0 core.c  -o core_dump

运行:

./core_dump                                                                                                                                                                                
[1]    3848 segmentation fault (core dumped)  ./core_dump

可以看到会生成一个core文件。
gdb分析:

 gdb  core_dump core-core_dump-8071-1695087419                                                                                                                                               ldy@ubuntu 09:41:36
GNU gdb (Ubuntu 8.1.1-0ubuntu1) 8.1.1
Copyright (C) 2018 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 core_dump...done.
[New LWP 8071]
Core was generated by `./core_dump'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x000055f5453a35e6 in main () at core.c:7
7          *ptr = "dandan";
(gdb)

可以看到gdb定位到第7行ptr = “dandan”引起的段错误。