段错误如何调试

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

文章目录


前言

嵌入式C开发,或多或少都遇到段错误(segmentation fault )。段错误(Segmentation fault)是一种运行时错误,在程序运行时可能由于某些原因而发生。下面是我对一些典型的段错误总结。


提示:以下是本篇文章正文内容,下面案例可供参考

段错误产生的原因

1、访问不存在的内存地址

2、访问只读的内存地址

3、栈溢出

4、内存越界

注意: 这里我总结了在大学期间遇到的问题,有可能段错误产生的原因不止这些。

问题1:访问不存在的内存地址

c 复制代码
#include <stdio.h>
 
int main(int argc, char **argv)
{
    printf("==================段错误==================\n");
 
    int *p = NULL;
    *p = 2244;
 
    return 0;
}

问题2:访问只读的内存地址

c 复制代码
#include "stdio.h"
#include <stdlib.h>
#include "string.h"
int main(int argc, char **argv)
{
	printf("=================段错误==================\n");
	char Data[] = "123456";
	char *data = NULL;	//即使初始化为空,指针data可能是指向了一个只读的内存:memcpy执行失败,造成空指针解引用(空指针间接引用)
	memcpy (data,Data,sizeof(Data));	//引用空指针
	printf ("data is %s.\nand sizeof(Data) is %d.\n", data, sizeof(Data));
	free(data);
	data = NULL;
}
c 复制代码
#include <stdio.h>
 
int main(int argc, char **argv)
{
    printf("==================段错误==================\n");
 
    char *str = "hello world";
    str[0] = 'H';
 
    return 0;
}

问题3:栈溢出

程序的有效内存还可以再分为多个区域,每个区域都有不同的作用,比如代码区、全局数据区、堆区、栈区、动态链接库区 等。栈就是这些内存分区中的一个。栈的作用就是存储程序的局部数据

对于C/C++来说,局部数据是指在函数内部定义的数据。例如在函数内部定义的变量、指针、参数、结构体、数组、对象、引用等,它们都要保存到栈中。

那么,栈为什么会溢出?

对每个程序来说,栈能使用的内存是有限的,一般是 1M~8M,这在编译时就已经决定了,程序运行期间不能再改变。如果程序使用的栈内存超出最大值,就会发生栈溢出(Stack Overflow)错误,程序就崩溃了。

最常见的栈溢出现象就是递归。每次递归就相当于调用一个函数,函数每次被调用时都会将局部数据(在函数内部定义的变量、参数、数组、对象等)放入栈中。

如果递归1000次,那么就会将1000份这样的数据放入栈中。这些数据占用的内存直到整个递归结束才会被释放,在递归过程中只会累加,不会释放。如果递归次数过多,并且局部数据也多,那么会使用大量的栈内存,很容易就导致栈溢出了。

c 复制代码
#include <stdio.h>
 
//函数名前加static关键字 函数就被定义成为静态函数。
/*
静态函数的好处
1. 静态函数不能被其他文件所用。
2. 其他文件中可以定义相同名字的函数,不会发生冲突。
3. 静态函数会被自动分配在一个一直使用的存储区,直到退出应用程序实例,避免了调用函数时压栈出栈,速度快很多。
*/
static void test(void)
{
    char buf[2048 * 2048] = {0};    //每次递归均会分配存储区
    static int i = 0;
    i++;
    printf("i = %d\n", i);
    test();    //递归
}
 
int main(int argc, char **argv)
{
    printf("==================段错误==================\n");
 
    test();
 
    return 0;
}

问题4:内存越界

c 复制代码
#include <stdio.h>
 
int main(int argc, char **argv)
{
    printf("==================段错误==================\n");
 
    static char arr[8] = {0, 1, 2, 3, 4, 5, 6, 7};
 
    printf("arr[4444] = %d\n", arr[4444]);    //访问下标超出了数组长度
 
    return 0;
}

如何解决段错误

方法一:使用C语言printf函数打印来判断出错位置

有可能会出现的问题:打印不出来信息。如下方实例

c 复制代码
#include <stdio.h>

int main ()
{
    //指针没有初始化,因为野指针写了100,这个代码肯定就会出现段错误问题
	printf("11111");
	int *p;
	printf("2222");
	*p = 100;
	printf("33333");
	return 0;
}

上图的c语言程序,运行结果如下:

出现的问题:无法正常打印

解决问题的方法

解决的方法是要在每个printf函数输出完内容后面加上\n

c 复制代码
#include <stdio.h>

int main ()
{
    //指针没有初始化,因为野指针写了100,这个代码肯定就会出现段错误问题
	printf("11111\n");
	int *p;
	printf("2222\n");
	*p = 100;
	printf("33333\n");
	return 0;
}

修改后的C语言程序运行结果如下图:

为什么可以这样解决

C语言的\n有两个作用。第一个:起到换行作用。第二个:起到刷新缓冲区的作用。这里问题的解决,就是使用了其第二个作用。因此,在日常写程序的时候,使用printf函数的时候加上\n会更加稳妥一些

不然,出现一些玄学问题,肯定又得多掉一些头发了。

方法二:借助linux当中的gdb来调试

第一步:后面加上 -g

第二步:使用gdb来运行test文件

最后:输入run+回车

现在,我们就可以观察到段错误出现在哪里了。
注意:有可能自己下载虚拟机上面没有gdb,那么需要先安装,然后再使用。

总结

遇到错误大家一定不要慌,不着急慢慢来。多找找资料,就能把问题都解决掉。

相关推荐
伴野星辰23 分钟前
小乌龟TortoiseGit 安装和语言包选择
linux·运维·服务器
枫叶丹430 分钟前
【在Linux世界中追寻伟大的One Piece】多线程(一)
java·linux·运维
残念ing31 分钟前
【Linux】—简单实现一个shell(myshell)
linux·运维·服务器
明月心95238 分钟前
linux mount nfs开机自动挂载远程目录
linux·运维·服务器
Ray55051 小时前
bridge-multicast-igmpsnooping
linux·服务器·网络
库库的里昂1 小时前
Linux系统Docker部署开源在线协作笔记Trilium Notes与远程访问详细教程
linux·运维·docker·开源
在下不上天1 小时前
flume-将日志采集到hdfs
大数据·linux·运维·hadoop·hdfs·flume
mit6.8242 小时前
[Redis#3] 通用命令 | 数据类型 | 内部编码 | 单线程 | 快的原因
linux·redis·分布式
^Lim2 小时前
esp32 JTAG 串口 bootload升级
java·linux·网络
小林熬夜学编程3 小时前
【Linux系统编程】第五十弹---构建高效单例模式线程池、详解线程安全与可重入性、解析死锁与避免策略,以及STL与智能指针的线程安全性探究
linux·运维·服务器·c语言·c++·安全·单例模式