【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)特性 所有静态成员为所有类对象所共…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...

高危文件识别的常用算法:原理、应用与企业场景
高危文件识别的常用算法:原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件,如包含恶意代码、敏感数据或欺诈内容的文档,在企业协同办公环境中(如Teams、Google Workspace)尤为重要。结合大模型技术&…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...