动态内存管理(2) - 详解

news/2025/9/25 20:55:13/文章来源:https://www.cnblogs.com/yxysuanfa/p/19111997

目录

前言

一、常⻅的动态内存的错误

介绍:

1.对NULL指针的解引⽤操作

什么是NULL指针?(讲一下)

解引用NULL指针的风险

如何避免解引用NULL指针?

2.对动态开辟空间的越界访问

越界访问的原因

越界访问的危害:

如何避免越界访问?

3.对⾮动态开辟内存使⽤free释放

如何避免此类错误?

4.使⽤free释放⼀块动态开辟内存的⼀部分

如何避免此类错误?

5.对同⼀块动态内存多次释放

如何规避重复释放?

代码分析

二、柔性数组知识

总结


前言

动态内存管理的两篇文章讲解为什么要有动态内存分配、malloc和free、calloc和realloc、常⻅的动态内存的错误、动态内存经典笔试题分析、柔性数组知识的相关内容,常⻅的动态内存的错误、动态内存经典笔试题分析、柔性数组知识为本章节知识的内容,动态内存管理知识也将讲解完成。

一、常⻅的动态内存的错误

介绍:

  在C语言动态内存管理中,错误的使用方式可能导致程序崩溃、数据损坏甚至安全漏洞。以下常见动态内存错误的解析及示例说明:

1.对NULL指针的解引⽤操作

  对NULL指针的解引用操作是C/C++中最常见的错误之一,可能导致程序崩溃或不可预期的行为,接下来先讲解对NULL指针的解引⽤操作的知识:

什么是NULL指针?(讲一下)

  • 定义NULL是一个宏常量(通常定义为(void*)0),表示指针不指向任何有效的内存地址。
  • 作用:用于初始化指针或标记指针“未使用”状态。

解引用NULL指针的风险

  1. 程序崩溃:直接访问地址0(或无效地址)会触发操作系统的内存访问错误
  2. 未定义行为:即使程序未立即崩溃,也可能出现数据损坏、逻辑错误或安全漏洞(如缓冲区溢出)。

代码例:

void test()
{
int *p = (int *)malloc(10*sizeof(int));
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}

如何避免解引用NULL指针?

  • 指针使用前检查
    int* p = NULL;
    if (p != NULL) {  // 解引用前必须判断
    *p = 10;  // 安全操作
    }
  • 初始化指针:始终为指针分配有效内存后再使用(如int* p = malloc(sizeof(int));)。

1.即使逻辑上不可能为NULL,也建议检查一下。

2.注意:警告往往预示着潜在崩溃风险。

2.对动态开辟空间的越界访问

  动态内存越界访问是指程序访问了超出动态分配内存范围的空间,是C语言中危险的错误。

越界访问的原因
  1. 数组下标越界
    • malloc/calloc分配的数组(如int *p = malloc(5*sizeof(int))),访问p[5]p[-1]等超出[0, 4]范围的下标。
  2. 指针偏移错误
    • 通过指针运算(如p++)移动指针时,超出分配内存的起始/结束地址。
  3. 字符串操作不当
    • 使用strcpy/strcat等函数时,目标字符串空间不足,导致写入越界。
越界访问的危害:
  • 数据损坏:修改相邻内存区域的数据,导致程序逻辑错误。
  • 程序崩溃:访问未分配的内存区域,触发错误。

代码例:

#include
#include
int main() {
int *p = malloc(3 * sizeof(int)); // 分配3个int(12字节,假设int=4字节)
p[3] = 100; // 越界访问:合法范围是p[0]~p[2]
free(p);
return 0;
}

解释:

  • 问题p[3]超出分配的内存空间,可能覆盖其他变量。
  • 后果:后续free(p)可能崩溃。

如何避免越界访问

  • 明确内存大小:动态分配时记录内存块大小(如int size = 5; int *p = malloc(size * sizeof(int))),访问时严格检查下标是否在[0, size-1]内。
  • 使用安全函数
    • 字符串操作:strncpy(dest, src, n)(限制复制长度)。
    • 内存复制:memcpy(dest, src, n)(确保n不超过目标缓冲区大小)。

3.对⾮动态开辟内存使⽤free释放

  在C语言中,free函数的作用是释放动态分配的内存malloc/calloc/realloc的返回值)。若对非动态内存使用free,会导致严重的未定义行为,接下来,将讲解这方面知识。

注意;

以下内存不可用free释放:

  • 栈内存:函数内局部变量(如int a = 10;)、函数参数。
  • 全局/静态内存:全局变量(如int g_var;)、static变量(如static int s_var;)。
  • 常量内存:字符串常量(如char *str = "hello";中的"hello")。

错误例;

#include
#include
int main() {
int a = 10;
free(&a); // 错误:&a是栈内存地址
return 0;
}
#include
#include
int g; // 全局变量(静态存储区)
int main() {
free(&g); // 错误:全局内存无需手动释放
return 0;
}
#include
#include
int main() {
char *str = "hello"; // "hello"存储在常量区
free(str); // 错误:常量内存不可修改/释放
return 0;
}
如何避免此类错误?
  1. 明确内存来源

    • 仅对malloc/calloc/realloc返回的指针使用free,且只释放一次
    • 局部变量、全局变量、字符串常量等无需手动释放。
  2. 指针初始化与标记

    动态内存指针使用后及时free并置为NULL(如free(p); p = NULL;),避免二次释放或野指针。

4.使⽤free释放⼀块动态开辟内存的⼀部分

  先说结论:C语言的内存释放是整体释放,是从头开始释放,不可以在中间位置释放空间,即free函数只能释放整块动态内存(即malloc/calloc/realloc返回的完整内存块),禁止释放内存块的一部分(如指针偏移后的地址)。这种操作会直接导致未定义行为,是严重的内存错误。

这是常发错误:
例:

#include
#include
#include
int main() {
char *p = malloc(100); // 动态分配100字节
if (p == NULL) return 1;
char *q = p + 50; // 指针偏移,指向内存块的中间位置
free(q); // 错误:释放内存块的一部分(从第50字节开始)
return 0;
}

本质原因

动态内存分配时,系统会在内存块头部维护数据(如块大小、是否空闲、前后块指针等),用于内存管理。free函数需要通过指针找到元数据才能正确释放整块内存。若指针指向内存块中间(非起始地址),free将无法识别数据。

如何避免此类错误?
  1. 跟踪动态内存指针:始终保存malloc返回的原始指针,避免对其进行偏移或修改。
  2. 禁止对指针进行算术运算后释放:如free(p + n)free(&p[i])均为错误操作,如果要操作,可以通过中间变量来实现。

5.对同⼀块动态内存多次释放

  重复释放是错误行为,即对同一块动态内存调用多次free 是严重的内存错误,会直接导致未定义行为,释放空间free一次即可,如果多次释放同一内存,会导致程序崩溃。

#include
#include
int main() {
int *p = malloc(4); // 分配4字节内存
free(p); // 第一次释放(正确)
free(p); // 第二次释放(错误:重复释放)
return 0;
}
如何规避重复释放?

释放后立即置空指针,将指针设为NULL,后续再次free(NULL)会被系统忽略(安全操作)。

即:

#include
#include
int main()
{
int *p = malloc(4); // 分配4字节内存
free(p); // 第一次释放(正确)
p=NULL;
free(p);
return 0;
}

解:

第二次释放前已经将指针 p 显式置为 NULL,而 C 语言标准规定:free(NULL) 是一个空操作,不会对内存造成任何影响。

代码分析
  1. 第一次释放free(p) 释放了 p 指向的动态内存块,此时 p 成为野指针(指向已释放内存)。
  2. 指针置空p = NULL 将指针标记为“空指针”,避免其成为“野指针”(指向无效内存的指针)。
  3. 第二次释放free(p) 等价于 free(NULL),这是安全的,系统会忽略该操作,不会引发任何错误。

二、柔性数组知识

  也许你从来没有听说过柔性数组这个概念,但是它确实是存在的,结构中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做柔性数组成员。

例:

struct A
{
int i;
int a[];
};

这样实现:

特点讲解:

1.结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。

2.sizeof返回的这种结构⼤⼩不包括柔性数组的内存。

3.包含柔性数组成员的结构⽤malloc()函数进⾏内存的动态分配,并且分配的内存应该⼤于结构的⼤ ⼩,以适应柔性数组的预期⼤⼩。

#include
struct A
{
int i;
int a[];
};
int main()
{
printf("%d",sizeof(struct A));
}

为柔性数组创建空间:

#include
#include
struct A
{
int i;
int a[];
};
int main()
{
struct A* h=(struct A*)malloc(sizeof(struct A)+sizeof(int)*10);
//注意:一次性分配结构体 + 柔性数组内存(共 sizeof(struct A) + 10个int)
h->i=1;
free(h);
h=NULL;
}
  • 柔性数组的内存与结构体连续,无需单独为 a[0] 分配内存
  • 分配时需计算总大小:sizeof(struct A)(结构体本身) + sizeof(int)*10(10个int元素)。
  • 只需释放结构体指针 h,柔性数组内存会随结构体一起释放,无需单独释放 a[0]

总结

  以上就是今天要讲的内容,动态内存管理的两篇文章讲解了为什么要有动态内存分配、malloc和free、calloc和realloc、常⻅的动态内存的错误、动态内存经典笔试题分析、柔性数组知识的相关内容,希望大家能喜欢我的文章,谢谢各位。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917511.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机视觉(opencv)实战二十七——目标跟踪 - 教程

计算机视觉(opencv)实战二十七——目标跟踪 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas&quo…

P8367 [LNOI2022] 盒

传送门。 神仙题,做了半年。 整体是不好做的,考虑每个\(w_i\)对整体的贡献。记\(s_i=\sum_{i=1}^{i}a_i\),\(d_i=\sum_{i=1}^{i}b_i\),当且仅当\(s_i\neq d_i\)时,才会有货物流通\(i\)号点。所以总体的答案为: \…

蓝桥杯 2025 省 B 题:画展布置 - 题解笔记

蓝桥杯 2025 省 B 题:画展布置 - 题解笔记.md 一、题目核心信息 1. 问题描述 给定 N 幅画作的艺术价值数组 A,需从其中挑选 M 幅并排列成序列 B(长度为 M),目标是最小化评价指标 L,L 的定义为: \[L = \sum_{i=1…

二维坐标下的运算

在二维图中,常常遇到一些需要大量坐标运算的题目,这时可以封装一个Point类,实现坐标高效运算。 // #define LOCAL #include<iostream> #include<queue> #include<map> using namespace std; #define…

凡科建站怎么导出网页网站优化方案和实施

有一个数据库应用程序存在过多的解析问题&#xff0c;因此需要找到产生大量硬解析的主要语句。 什么是硬解析 Oracle数据库中的硬解析&#xff08;Hard Parse&#xff09;是指在执行SQL语句时&#xff0c;数据库需要重新解析该SQL语句&#xff0c;并创建新的执行计划的过程。这…

Polar2025秋季个人挑战赛web-writeup

感觉难度还行polar快递 在登录页面下载备忘录发现用户等级分四个,抓包发现有id=user,改为最高等级的root登录即可获取flag white 常规输入执行命令发现很多符号都被ban了/[;&$"<>?*[]{}()#@!%]/`,发…

题解:P12751 [POI 2017 R2] 集装箱 Shipping containers

cnblogs 题面 第二道根号分治,对初学者来说很友好的一道题。 题意在题面中写的很清楚,这里不多赘述。 思路 先从暴力开始想。 每次暴力的时间复杂度最坏明显是 \(O(n^2)\) 的,因为是类似区间加和最后统计的问题,可…

弱网配置

sudo tc qdisc replace dev eno1 root netem delay 120ms 30ms 25% loss 5% 解除sudo tc qdisc del dev eno1 ingresshttps://blog.csdn.net/2303_78922833/article/details/151372115

网站建设网页设计小江wordpress重写插件

上篇文章《C自动注册的工厂与--whole-archive》提到了--whole-archive选项在自动工厂示例的必要&#xff0c;“貌似也没其他方法了”。 这篇文章介绍另一种可以替代的方式&#xff0c;并分析其优缺点&#xff0c;采用的代码示例同上篇文章。文章最后附代码。 方法介绍 ld链接器…

net网站开发教学视频牌子网排行榜

报告来源&#xff1a;国泰君安&#xff08;訾猛&#xff09;亚马逊以技术为核心驱动力&#xff0c;实现从电商向科技公司的跨越&#xff0c;形成电商、物流、AWS、新零售协同发展的完整生态圈。亚马逊从1995年开始为用户提供线上商品&#xff0c;从一家网上书店发展成全品类电商…

选择网站做友情链接的标准一般是wordpress点击折叠展开内容

Java概况 JavaSE是java分类中的标准版&#xff0c;是刚接触java要学习的基础知识。 JavaEE是java分类中的企业版&#xff0c;是java中的高级&#xff0c;涉及到的知识广泛。 JavaME中M是Micro的缩写&#xff0c;用在嵌入式等电子设备中。 Java软件工程师&#xff1a;通过Ja…

通过【开题答辩过程】以《基于JavaEE的创意产品众筹平台的设计与实现》为例,不会开题答辩的能够进来看看

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Nano-Banana免费使用指南:一键生成专属3D手办,附超详细提示词 - 指南

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

绘制金融集团监控大屏的地图demo

<!-- 引入ECharts和地图数据 --> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> <script src="https://geo.datav.aliyun.com/areas_v3/…

如何在CentOS 7上安装bzip2-1.0.6-13.el7.x86_64.rpm RPM包(详细步骤)

如何在CentOS 7上安装bzip2-1.0.6-13.el7.x86_64.rpm RPM包(详细步骤)​bzip2​ 是一个在 Linux 系统中常用的文件压缩工具,这个 RPM 包是专门为 ​CentOS 7 / RHEL 7(64位系统)​​ 准备的安装版本 一、先确认你…

实用指南:《原神助手》开源神器:游戏体验大升级

实用指南:《原神助手》开源神器:游戏体验大升级pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

百度收录网站需要多久做海外推广的公司

一、Git概述&#xff08;1&#xff09;定义Git是目前世界上最先进的分布式版本控制系统。&#xff08;2&#xff09;能干什么&#xff1f;解决冲突、管理权限、代码备份、协同开发、版本还原、历史追查、版本记录、分支管理、代码审查&#xff08;3&#xff09;集中管理型版本管…

AM1.5G 太阳光谱 - 教程

AM1.5G 太阳光谱 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", &qu…

2025年Java常见面试题

2025年Java常见面试题2025年Java常见面试题 原文链接:https://zhuanlan.zhihu.com/p/1913568498535360114 数据库事务特性。原子性、一致性、隔离性、持久性如何防止SQL注入:使用#不要使用$符号;对所有的入参做校验…

实用指南:k8s 跟 nacos 关于服务注册以及服务发现

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …