段错误如何调试

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

文章目录


前言

嵌入式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,那么需要先安装,然后再使用。

总结

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

相关推荐
斗转星移311 分钟前
解决ubuntu20.04无法唤醒的问题的一种方法
linux·ubuntu·电脑
lyh134443 分钟前
【Ubuntu崩溃修复】
linux·运维·服务器
什么半岛铁盒2 小时前
【Linux系统】Linux环境变量:系统配置的隐形指挥官
linux
Lw老王要学习2 小时前
Linux容器篇、第一章_02Rocky9.5 系统下 Docker 的持久化操作与 Dockerfile 指令详解
linux·运维·docker·容器·云计算
橙子小哥的代码世界3 小时前
【大模型RAG】Docker 一键部署 Milvus 完整攻略
linux·docker·大模型·milvus·向量数据库·rag
倔强的石头1064 小时前
【Linux指南】用户与系统基础操作
linux·运维·服务器
云上艺旅4 小时前
centos升级内核
linux·运维·centos
kaikaile19954 小时前
centos开启samba服务
linux·运维·centos
云上艺旅4 小时前
centos部署k8s v1.33版本
linux·云原生·kubernetes·centos
好多知识都想学4 小时前
Centos 7 服务器部署多网站
linux·服务器·centos