【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)特性 所有静态成员为所有类对象所共…...
生产环境最佳实践
生产环境最佳实践 前言 本文将介绍Spring Cloud Alibaba在生产环境中的最佳实践,包括配置优化、监控告警、高可用设计等方面。 一、高可用设计 1.1 服务端高可用 # Nacos集群配置 # 至少3个节点 # 推荐使用外部数据库spring:cloud:nacos:server-addr: nacos-1:8848,…...
RMAN 增量备份(Incremental Backup)
1、概念RMAN 增量备份是指 RMAN 只备份自上次备份以来发生过更改的数据块,而不是备份整个数据库的所有数据块。它是 Oracle 为解决大型数据库全量备份时间长、占用空间大的问题而设计的核心特性,也是现代企业级备份策略的基础。简单类比:全库…...
DeepSeek总结的从 DuckDB 迁移到 chDB基准测试
来源: https://github.com/chdb-io/cookbook/tree/main/migration-from-duckdbBENCHMARK.md 迁移基准测试 —— 深度探讨 本文是从 DuckDB 迁移到 chDB指南的配套文档。指南的第 5 节将环境/场景/结果/摘要内联呈现;本文件则包含不适合指南风格流程的部分…...
ThingsVis v1.1.15 版本更新:补齐嵌入与运维体验短板,多场景集成更可靠
ThingsVis v1.1.15:嵌入与运维体验的全面升级ThingsVis v1.1.15 版本以 ThingsVis 嵌入能力和设备详情页体验为核心进行更新。在 ThingsVis 嵌入方面,支持全屏、自动播放、剪贴板写入权限,修复 iframe 无法全屏问题;在设备详情页&…...
Unity低耦合可复用交互系统设计与实现
1. 为什么“交互系统”在Unity项目里总变成一锅粥?你有没有遇到过这样的场景:美术同事改了个按钮位置,UI脚本里硬编码的transform.Find("Button")就报空引用;策划临时加个新交互逻辑,程序员得翻遍PlayerCont…...
从ISA到PCIe:为什么老电脑升级显卡要插对槽?聊聊PCI总线的那些事儿
从ISA到PCIe:老电脑升级显卡必须知道的插槽进化史 当你从储物间翻出一台2003年的戴尔Dimension 4600准备升级显卡时,会发现主板上那些长短不一的插槽仿佛在讲述一段被遗忘的技术史诗。黑色PCI插槽旁紧挨着棕色的AGP 8X,而最边缘那个几乎被灰尘…...
AI智能体Skills设计:从API工具到核心能力的工程实践
1. 从“工具”到“能力”:重新理解AI智能体的Skills最近和几个做AI应用开发的朋友聊天,发现一个挺有意思的现象:大家一提到给智能体加“Skills”,第一反应往往是去翻看官方文档,找那个叫“Tools”或者“Functions”的列…...
如何快速解决Windows 11区域模拟问题:完整API钩子技术指南
如何快速解决Windows 11区域模拟问题:完整API钩子技术指南 【免费下载链接】Locale_Remulator System Region and Language Simulator. 项目地址: https://gitcode.com/gh_mirrors/lo/Locale_Remulator Locale Remulator是一款强大的系统区域和语言模拟工具&…...
使用Taotoken的OpenAI兼容协议与PythonSDK三分钟完成大模型接入
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用Taotoken的OpenAI兼容协议与Python SDK三分钟完成大模型接入 本文面向刚开始接触大模型API的开发者,旨在提供一个清…...
【限时公开】Anthropic内部文档评审Checklist泄露版:92%开发者忽略的8个Claude API语义一致性陷阱
更多请点击: https://codechina.net 第一章:Claude API文档编写的核心原则与语义一致性定义 编写高质量的Claude API文档,首要任务是坚守三大核心原则:可预测性、可验证性与可演进性。可预测性要求所有接口行为严格遵循OpenAPI 3…...
