【C语言】野指针问题详解及防范方法
文章目录
- 💯前言
- 💯什么是野指针?
- 💯未初始化的指针
- 代码示例
- 问题分析
- 解决方法
- 💯指针越界访问
- 代码示例
- 问题分析
- 解决方法
- 💯指向已释放内存的指针(悬空指针)
- 代码示例
- 问题分析
- 解决方法
- 💯小结
💯前言
- C语言是一门以其高效和灵活著称的编程语言,但与其高效性伴随而来的,是需要开发者非常小心地管理内存。野指针(
Dangling Pointer
)是 C 语言中的一个常见问题,指针的错误使用可能导致程序崩溃、数据泄露,甚至被攻击者利用,成为严重的安全漏洞。
在本文中,我们将详细讲解野指针的三种常见情形,分析它们的成因、危害以及如何防范,并通过代码示例
让大家深入理解这些问题。
C语言
💯什么是野指针?
野指针是指向无法预测的内存地址的指针,其指向的地址往往是随机的、无效的或已失效的内存区域。当程序通过一个野指针去访问内存时,可能引发程序崩溃(如段错误)或者产生未定义行为。
在 C语言 中,指针是基础特性之一,赋予程序员直接操作内存的能力,这也是 C 语言的灵活性和高效性所在。然而,正是由于这种直接操作内存的能力,使得野指针问题在 C 语言中尤为常见且危险。
💯未初始化的指针
未初始化的指针是指在定义指针变量时,未为其赋予初始值的指针。这种指针所包含的地址值是随机的,可能指向程序的任意内存区域,从而导致未定义行为。
代码示例
int main()
{int* p; // 定义了一个指针变量 p,但未初始化*p = 20; // 尝试通过 p 修改它指向的内存return 0;
}
问题分析
- 在
int* p;
这行代码中,指针p
被定义,但并未被初始化,因此它的值是随机的,指向不可预测的内存位置。 - 当执行
*p = 20;
时,程序试图向一个未知内存位置写入数据,这引发未定义行为,可能导致程序崩溃(例如段错误)或引发安全漏洞。
解决方法
- 显式初始化指针:定义指针时,将其初始化为
NULL
,这样可以确保指针不会指向任何有效的内存区域,直到它被显式赋值。int* p = NULL;
- 分配合法的内存:在使用指针之前,确保它指向有效的内存,可以通过动态分配内存或者将其指向已有的变量。
int a = 10; int* p = &a; // 指针指向变量 a 的地址
- 启用编译器警告:现代编译器通常提供一些有用的警告选项,例如
-Wall
,能够帮助开发者检测未初始化的指针并减少潜在的错误。
💯指针越界访问
指针越界访问是指一个指针超出其合法内存范围,从而访问非法区域的情形。这种情况同样可能导致野指针的产生,并引发未定义行为。
代码示例
int main()
{int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 定义数组并初始化int *p = arr; // 指针 p 指向数组首元素int i = 0;int sz = sizeof(arr) / sizeof(arr[0]); // 计算数组大小,sz = 10for (i = 0; i <= sz; i++) // 注意这里的循环条件为 i <= sz{printf("%d ", *p); // 试图打印指针 p 所指向的值p++; // 指针 p 向后移动}return 0;
}
问题分析
- 在
for (i = 0; i <= sz; i++)
这一行中,i <= sz
使得循环多执行了一次,导致i == sz
时,指针p
指向了数组边界之外的内存位置,从而产生了野指针。 - 试图通过
p
访问arr
数组之外的内存是非法操作,可能导致程序崩溃,或者引发不易检测的安全问题。
解决方法
- 修正循环条件:将循环条件改为
i < sz
,确保指针始终在数组的合法范围内。for (i = 0; i < sz; i++)
- 加强边界检查:在操作指针时进行边界检查,确保不会超出合法的数组范围。
- 避免手动递增指针:可以直接使用数组下标访问元素,避免手动管理指针的移动,以降低出错风险。
for (i = 0; i < sz; i++) {printf("%d ", arr[i]); }
💯指向已释放内存的指针(悬空指针)
悬空指针是指向已释放或生命周期结束的内存区域的指针。当函数返回局部变量的地址,或内存被释放后仍继续使用该指针,就会导致悬空指针的问题。
代码示例
int* test() {int a = 10; // 定义局部变量 a,并初始化为 10return &a; // 返回局部变量 a 的地址
}int main() {int* p = test(); // 函数返回的局部变量地址赋值给指针 pprintf("%d\n", *p); // 通过 p 访问无效内存return 0;
}
问题分析
- 在
test
函数中,变量a
是局部变量,存储在栈中。当函数执行完毕后,a
所在的栈帧被释放,其地址变得无效。 - 指针
p
存储了a
的地址,然而此时p
成为了悬空指针,继续通过p
访问该内存区域会导致未定义行为,可能引发程序崩溃或输出错误数据。
解决方法
-
避免返回局部变量的地址
- 可以通过动态内存分配或者静态变量来替代返回局部变量的地址。
示例 1:动态内存分配
int* test() {int* a = (int*)malloc(sizeof(int)); // 动态分配内存*a = 10;return a; // 返回分配的内存地址 }int main() {int* p = test();printf("%d\n", *p); // 正常访问free(p); // 用完后释放内存return 0; }
示例 2:使用静态变量
int* test() {static int a = 10; // 静态变量,生命周期贯穿程序运行return &a; // 返回静态变量的地址 }int main() {int* p = test();printf("%d\n", *p); // 正常访问return 0; }
-
确保指针始终指向有效内存
- 在函数中返回指针时,必须确保返回的指针指向的内存是有效的,且不会在函数执行完毕后失效。
-
使用内存管理工具
- 现代的内存管理工具(如 Valgrind 或 AddressSanitizer)可以有效地检测悬空指针问题,帮助开发者在开发和测试阶段发现和修复内存管理错误。
💯小结
在 C语言编程 中,指针的管理是至关重要的环节。C语言赋予开发者直接操作内存的能力,使得程序能够具备极高的性能,但这种能力也伴随着巨大的责任。
开发者需要掌握 指针的生命周期 以及它们在内存中的行为,从而确保程序的稳定和安全。在大型项目中,内存管理和指针操作尤为重要,团队开发时需要制定明确的标准和代码规范,以避免因个人疏忽导致的指针错误
。
此外,测试和代码审查也应作为内存管理的重要环节,以确保代码在各种边界条件下都能正确运行。
相关文章:

【C语言】野指针问题详解及防范方法
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 💯前言💯什么是野指针?💯未初始化的指针代码示例问题分析解决方法 💯指针越界访问代码示例问题分析解决方法 💯指向已释放内存的…...

【SVN和GIT】版本控制系统详细下载使用教程
文章目录 ** 参考文章一、什么是SVN和GIT二、软件使用介绍1 SVN安装1.1 服务端SVN下载地址1.2 客户端SVN下载地址2 SVN使用2.1 服务端SVN基础使用2.1.1 创建存储库和用户成员2.1.2 为存储库添加访问人员2.2 客户端SVN基础使用2.2.1 在本地下载库中的内容2.2.2 版本文件操作--更…...

【Vue】Vue3.0(二十六)Vue3.0中的作用域插槽
上篇文章 【Vue】Vue3.0(二十五)Vue3.0中的具名插槽 的概念和使用场景 🏡作者主页:点击! 🤖Vue专栏:点击! ⏰️创作时间:2024年11月20日17点30分 文章目录 概念使用场景示…...
神经网络(系统性学习二):单层神经网络(感知机)
此前篇章: 神经网络中常用的激活函数 神经网络(系统性学习一):入门篇 单层神经网络(又叫感知机) 单层网络是最简单的全连接神经网络,它仅有输入层和输出层,没有隐藏层。即&#x…...
CTF之密码学(BF与Ook)
BrainFuck(通常也被称为Brainfuck或BF)和Ook是两种非常特殊且有趣的编程语言。以下是对这两种语言的详细介绍: 一、BrainFuck 简介: BrainFuck是一种极小化的计算机语言,由Urban Mller在1993年创建。由于“fuck”在英…...

【TEST】Apache JMeter + Influxdb + Grafana
介绍 使用Jmeter发起测试,测试结果存入Influxdb,Grafana展示你的测试结果。 环境 windows 10docker desktopJDK17 安装 Apache JMeter 访问官网(Apache JMeter - Apache JMeter™)下载JMeter(目前最新版本5.6.3&a…...
SpringBoot集成多个rabbitmq
1、pom文件 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId><versio…...
从零开始学习数据库 day0(基础)
在当今的信息时代,数据已经成为了企业和组织最重要的资产之一。无论是电子商务平台,社交媒体,还是科研机构,几乎每个地方都离不开数据库。今天,我们将一起走进数据库的世界,学习它的基础知识,帮…...

MongoDB相关问题
视频教程 【GeekHour】20分钟掌握MongoDB Complete MongoDB Tutorial by Net Ninja MongoDB开机后调用缓慢的原因及解决方法 问题分析: MongoDB开机后调用缓慢,通常是由于以下原因导致: 索引重建: MongoDB在启动时会重建索引…...
linux基本命令(1)
1. 文件和目录操作 ls — 列出目录内容 ls # 显示当前目录的文件和目录 ls -l # 显示详细的文件信息(权限、大小、修改时间等) ls -a # 显示所有文件(包括隐藏文件) ls -lh # 显示详细信息并以易读的方式显示文件大小 cd — 改…...
【机器学习】超简明Python基础教程
Python是一种简单易学、功能强大的编程语言,适用于数据分析、人工智能、Web开发、自动化脚本等多个领域。本教程面向零基础学习者,逐步讲解Python的基本概念、语法和操作。 1. 安装与运行 安装Python 从官网 Welcome to Python.org 下载适合自己系统的…...

基于信创环境的信息化系统运行监控及运维需求及策略
随着信息技术的快速发展和国家对信息安全的日益重视,信创环境(信息技术应用创新环境)的建设已成为行业发展的重要趋势。本指南旨在为运维团队在基于信创环境的系统建设及运维过程中提供参考,确保项目顺利实施并满足各项技术指标和…...
【Mysql】视图--介绍和作用 视图的创建
1、介绍 (1)视图(view)是一个虚拟表,非真实存在,其本质是根据SQL语句获取动态的数据集,并为其命名,用户使用时只需使用视图名称既可获取结果集,并可以将其当作表来使用。…...

【JavaEE初阶 — 多线程】定时器的应用及模拟实现
目录 1. 标准库中的定时器 1.1 Timer 的定义 1.2 Timer 的原理 1.3 Timer 的使用 1.4 Timer 的弊端 1.5 ScheduledExecutorService 2. 模拟实现定时器 2.1 实现定时器的步骤 2.1.1 定义类描述任务 定义类描述任务 第一种定义方法 …...

Win10系统开启了文件夹管控(文件夹限制访问)导致软件向系统公共文档目录写入失败的问题排查分享
目录 1、问题说明 2、查看系统是否开启了文件夹管控 3、在未安装杀毒软件的Win10电脑上可能会自动打开文件夹管控 4、到微软官网上查看Windows 安全中心的病毒和威胁防护与文件夹管控的详细说明 5、解决办法探讨 6、最后 C++软件异常排查从入门到精通系列教程(专栏文章列…...
大数据的数据整合
数据整合是对导入的各类源数据进行整合,新进入的源数据匹配到平台上的标准数据,或者成为系统中新的标准数据。数据整合工具对数据关联关系进行设置。经过整合的源数据实现了基本信息的唯一性,同时又保留了与原始数据的关联性。具体功能包括关…...

回溯法经典难题解析
本文将通过几个经典的回溯问题,展示回溯算法的应用及其在解决问题时的核心思想和技巧。这些问题包括全排列、全排列II、N皇后以及数独问题,本文将分别介绍每个问题的思路与实现。 46. 全排列 给定一个不含重复数字的数组 nums ,返回其 所有…...

LLM的原理理解6-10:6、前馈步骤7、使用向量运算进行前馈网络的推理8、注意力层和前馈层有不同的功能9、语言模型的训练方式10、GPT-3的惊人性能
目录 LLM的原理理解6-10: 6、前馈步骤 7、使用向量运算进行前馈网络的推理 8、注意力层和前馈层有不同的功能 注意力:特征提取 前馈层:数据库 9、语言模型的训练方式 10、GPT-3的惊人性能 一个原因是规模 大模型GPT-1。它使用了768维的词向量,共有12层,总共有1.…...

Electron开发构建工具electron-vite(alex8088)添加VueDevTools(VitePlugin)
零、介绍 本文章的electron-vite指的是这个项目👉electron-vite仓库,electron-vite网站 本文章的VueDevTools指的是VueDevTools的Vite插件版👉https://devtools.vuejs.org/guide/vite-plugin 一、有一个用electron-vite创建的项目 略 二、…...

【C++】static修饰的“静态成员函数“--静态成员在哪定义?静态成员函数的作用?
声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用 static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化 一、静态成员变量 1)特性 所有静态成员为所有类对象所共…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...

FFmpeg avformat_open_input函数分析
函数内部的总体流程如下: avformat_open_input 精简后的代码如下: int avformat_open_input(AVFormatContext **ps, const char *filename,ff_const59 AVInputFormat *fmt, AVDictionary **options) {AVFormatContext *s *ps;int i, ret 0;AVDictio…...

2.3 物理层设备
在这个视频中,我们要学习工作在物理层的两种网络设备,分别是中继器和集线器。首先来看中继器。在计算机网络中两个节点之间,需要通过物理传输媒体或者说物理传输介质进行连接。像同轴电缆、双绞线就是典型的传输介质,假设A节点要给…...

在Zenodo下载文件 用到googlecolab googledrive
方法:Figshare/Zenodo上的数据/文件下载不下来?尝试利用Google Colab :https://zhuanlan.zhihu.com/p/1898503078782674027 参考: 通过Colab&谷歌云下载Figshare数据,超级实用!!࿰…...