【内存管理大猫腻:从“越界”到“内存泄漏”应有尽有】

news2024/12/23 23:27:21

797de056b3c241e7ae2b83df9cd61759.jpeg

 

 

 

 

本章重点

  • 什么是动态内存
  • 为什么要有动态内存
  • 什么是野指针
  • 对应到C空间布局, malloc 在哪里申请空间
  • 常见的内存错误和对策
  • C中动态内存“管理”体现在哪

11151d2dc87b47e7ba85044a081988c9.png

什么是动态内存

  • 动态内存是指在程序运行时,根据需要动态分配的内存空间。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define N 10
int main()
{
	int* p = (int*)malloc(sizeof(int) * N); //动态开辟空间40个字节的空间
	if (NULL == p) { //判断是否成功申请空间
		perror("malloc\n");
		//perror函数是标准库函数,其作用是输出上一个系统调用(例如malloc)的错误信息
		exit(0);
		//exit函数结束程序运行
	}
	for (int i = 0; i < N; i++) {
		p[i] = i;//赋值0,1,2,3,4,5,6,7,8,9
	}
	for (int i = 0; i < N; i++) {
		printf("%d ", i);//打印
	}
	printf("\n");
	free(p); //开辟完之后,要程序员自主释放
	p = NULL;
	return 0;
}

6e748ed4a0e449aa9ffa90b966aad400.png

 

为什么要有动态内存

  • 通常,在编写程序时,我们可以使用静态内存(静态分配内存),也就是在程序编译阶段就确定了内存空间的大小和位置,但是静态内存存在一定的限制和局限性,比如无法在运行时改变分配的内存大小等。
  • 而动态内存则具有更大的灵活性和可变性,可以在程序运行时动态地分配、释放内存,以适应程序的实际需求。

38fd44bc350745e4b2793a5b3f1a3132.png

栈、堆和静态区

C程序动态地址空间分布

ff864c714be04bb095832edbcc38fce5.png

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

int g_val2;//未初始化变量
int g_val1 = 1;//已初始化变量

int main()
{
	printf("code addr: %p\n", main);//代码区

	const char* str = "hello world";//字符常量区
	printf("static readonly: %p\n", str);//这里输出的是字符串的首地址

	printf("init(初始化) global val: %p\n", &g_val1);//已初始化变量区

	printf("uninit(未初始化) global val: %p\n", &g_val2);//未初始化变量区

	int* p = (int*)malloc(sizeof(int) * 10);
	printf("heap(堆) : %p\n", p);//这里输出的是开辟40个字节空间的首地址
	
	//输出两个局部指针变量的地址
	printf("stack(栈) addr: %p\n", &str);
	printf("stack(栈) addr: %p\n", &p);
}

由于win中有地址随机化保护,我们这里的结果是再Linux中验证的

48de635d6c294556bd459703c8d963f8.png

同时我们也可以发现栈是向下增长的,后定义的变量后入栈,其相应的地址也较小。

再来验证一下堆区的特点。

char* p1 = (char*)malloc(sizeof(char) * 10); 
printf("heap(堆) : %p\n", p1);

char* p2 = (char*)malloc(sizeof(char) * 10); 
printf("heap(堆) : %p\n", p2);

char* p3 = (char*)malloc(sizeof(char) * 10); 
printf("heap(堆) : %p\n", p3);

printf("stack(栈) addr: %p\n", &p1);
printf("stack(栈) addr: %p\n", &p2);
printf("stack(栈) addr: %p\n", &p3);

15a81b2515fd45cf9531bcc1a65ff145.png

堆是符合向上增长的,先开辟的变量,其相应的地址较小。

在C语言中,为何一个临时变量,使用static修饰之后,它的生命周期变成全局的了?

bca1a935851b48cf84cc7a4e952f2013.png

        当在一个函数中将一个局部变量添加了static关键字时,编译器会将其转化为对应的静态变量,这使得该变量的存储位置从栈(stack)转变为全局数据区(data segment)中的静态变量存储区,使得该变量在函数调用结束后不会被自动销毁。

82c24f9ce6b044498159bbdd9dc9c447.png

6537846313224309b5170f6c3efd5599.png

常见的内存错误及对策

ONE:指针没有指向一块合法的内存

1、结构体成员指针未初始化

9b7a2b338cf9420e98d39368acc948f0.png


2、没有为结构体指针分配足够的内存

87bfa12551864a0cb8e1d1291c36c53d.png


3、函数的入口检测

441dc99ed85f4555b6ea14e9cd2be9ba.png


TWO:为指针分配的内存太小

b3634ec44df1432ab4df6807704d272f.png


THREE:内存分配成功,但并未初始化d113ff07962249c8803cdf7e79cc06e8.png

FOUR:内存越界

5045a560a766474f8fedd1ce657751c4.png

FIVE:内存泄漏

  • 申请内存是在哪里申请?- 堆
  • 申请内存是向谁要空间?- 操作系统
  • 如何申请内存? - malloc函数
  • 申请内存是否需要释放?如何释放? - 需要,free函数
  • 申请内存不释放会有什么问题? - 内存泄露

程序退出的时候,曾经的内存泄漏问题还存在吗?

b195e60535894512848e1ce80b00186c.png

内存释放的本质是什么?

4e49cd27ba4e438a8b16dd9791463a07.png

观察free函数的参数,free函数只知道释放空间的起始地址,貌似并不知道要释放多大空间,那如何正确释放呢?

e3d836705a0e43a2ba4425c3f186cb1a.png

我们这里写一个单链表代码来演示动态开辟内存

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <windows.h>
#define N 10
typedef struct _Node {
	int data;
	struct _Node* next;
}node_t;

static node_t* AllocNode(int x)
{
	node_t* n = (node_t*)malloc(sizeof(node_t));
	if (NULL == n) {
		exit(EXIT_FAILURE);
	}
	n->data = x;
	n->next = NULL;
	return n;
}

void InsertList(node_t* head, int x)
{
	node_t* end = head;
	while (end->next) {
		end = end->next;
}
node_t* n = AllocNode(x);
end->next = n;
}

void ShowList(node_t* head)
{
	node_t* p = head->next;
	while (p) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

void DeleteList(node_t* head)
{
	node_t* n = head->next;
	if (n != NULL) {
		head->next = n->next;
		free(n);
	}
}

int main()
{
	node_t* head = AllocNode(0); //方便操作,使用带头结点的单链表
	printf("插入演示...\n");
	Sleep(10000);
	for (int i = 1; i <= N; i++) {
		InsertList(head, i); //插入一个节点,尾插方案
		ShowList(head); //显示整张链表
		Sleep(1000);
	}
	printf("删除演示...\n");
	for (int i = 1; i <= N; i++) {
		DeleteList(head); //删除一个节点,头删方案
		ShowList(head); //显示整张链表
		Sleep(1000);
	}
	free(head); //释放头结点
	head = NULL;
	return 0;
}

efdd4d630dfa475b9b223cb8ad1fc218.gif

SIX:内存已经被释放了,但是继续通过指针来试用

2600e6a166944b70b8b47cb3d2a9350e.png

 

C中动态内存“管理”体现在哪

c641d7734e3741f6a2a2ce05371c9a33.png

cfce00bdbeef49ac91199777cb98a447.png

 

 

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

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

相关文章

1.链表的实现:不带哨兵

一、链表linked list 1.定义 链表是数据元素的线性集合&#xff0c;其每个元素都指向下一个元素&#xff0c;元素存储上并不连续,链表逻辑连续。 2.分类 ①单向链表&#xff1a;每个元素只知道其下一个元素是谁 ②双向链表: 每个元素知道其上一个元素和下一个元素 ③循环链…

Java - Stream流详解

文章目录 前言 大家好,好久不见了,最近由于实训的影响导致拖更了,在更新这一次估计javaSE基本上就算是完结了,还有一些落下的后面也会补上的,下次见面就是数据结构了 尽情期待吧!那么就让我们步入Stream流的学习吧! 一、Stream流是什么&#xff1f; Stream流是Java 8中的一个…

【openEuler 20.03 TLS编译openGauss2.0.0源码】

openEuler 20.03 TLS编译openGauss2.0.0源码 一、安装环境二、安装前准备二、安装步骤 一、安装环境 项目Value操作系统openEuler 20.03 64bit with ARMopenGauss2.0.0openGauss-third_party2.0.0 二、安装前准备 项目Value购买华为ECS鲲鹏 8vCPU32G 100M/s带宽 openEuler 2…

使用CubeMX配置STM32驱动HC-SR04超声波模块

文章目录 前言1 使用STM32CubeMX初始化代码1.1 时钟配置1.2 设置定时器1.3 触发引脚1.4 串口配置 2 代码编写2.1 添加驱动文件2.2 修改main.c 3 实现效果参考 前言 硬件选择 stm32f103c8t6&#xff08;最小板&#xff09;hc-sr04超声波模块 软件环境 stm32cubeIDE 1.12.1 …

【Linux】TCP网络套接字编程+协议定制+序列化和反序列化

悟已往之不谏&#xff0c;知来者之可追。抓不住的就放手&#xff0c;属于你的都在路上…… 文章目录 一、TCP网络套接字编程1.日志等级分类的日志输出API2.单进程版本的服务器客户端通信3.多进程版本和多线程版本4.线程池版本5.守护进程化的线程池服务器6.三次握手和四次挥手的…

python编程——pycharm的安装与使用

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 本文专栏&#xff1a;python专栏 专栏介绍&#xff1a;本专栏为免费专栏&#xff0c;并且会持续更新python基础知识&#xff0c;欢迎各位订阅关注。 目录 一、python IDLE的使用 二、pycharm的安装与使用 1、…

十分钟带你看懂——Python测试框架之pytest最全讲

pytest特短 pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有以下几个特点&#xff1a; 简单灵活&#xff0c;容易上手 支持参数化 能够支持简单的单元测试和复杂的功能测试&#xff0c;还可以用来做selenium/appnium等自动化测试、接口自动化测试&#xff08…

重磅版本发布|三大关键特性带你认识 Milvus 2.2.9 :JSON、PartitionKey、Dynamic Schema

亮点颇多、精彩程度堪比大版本的 Milvus 2.2.9 来啦&#xff01; 随着 LLM 的持续火爆&#xff0c;众多应用开发者将目光投向了向量数据库领域&#xff0c;而作为开源向量数据库的领先者&#xff0c;Milvus 也充分吸收了大量来自社区、用户、AI 从业者的建议&#xff0c;把重心…

非常简单就能理解的 链表带环问题 你也能轻松学会!

文章目录 判断链表是否带环若链表带环找出环的入口其他高频的面试问题 判断链表是否带环 题目描述&#xff1a; 给定一个链表&#xff0c;判断链表中是否有环。 思路&#xff1a; 可以明确的是&#xff1a;若一个链表带环&#xff0c;那么用指针一直顺着链表遍历&#xff0c…

《嵌入式系统》知识总结10:使用位带操作操纵GPIO

位操作 汇编层面 外设控制常要针对字中某个位&#xff08;Bit&#xff09;操作 以字节编址的存储器地址空间中&#xff0c;需要3步骤&#xff08;读出-修改-写回&#xff09; 1.&#xff08;从外设&#xff09;读取包含该位的字节数据 2. 设置该位为0或1、同时屏蔽其他位&am…

微信小程序 <view></view>容器嵌套,wxss样式修改内部内部样式不产生效果

网上关于”微信小程序讲的知识很少“&#xff0c;微信开发文档对于新手不是很友好&#xff0c;但是建议一定要学会看文档。 问题如下&#xff1a; 我写了好几个<view></view> 容器嵌套&#xff0c;我在对内部容器包括的内容做修改时&#xff0c;不产生效果&#…

apache RocketMQ远程代码执行(CVE-2023-33246)

RocketMQ是阿里巴巴在2012年开发的分布式消息中间件&#xff0c;专为万亿级超大规模的消息处理而设计&#xff0c;具有高吞吐量、低延迟、海量堆积、顺序收发等特点。它是阿里巴巴双十一购物狂欢节和众多大规模互联网业务场景的必备基础设施。 RocketMQ的NameServer、Broker、…

【计算机网络之HTTP篇】Cookie与Session的区别

目录 Cookie 原理 缺点 Session 原理 区别 Cookie cookie是浏览器在本地存储数据的一种机制。 原理 当浏览器向服务器第一次发送请求时&#xff0c;服务器会向浏览器返回一个Cookie&#xff0c;此时 cookie记录着浏览器访问服务器的用户登录状态。 后续浏览器再次访问服…

[深度学习入门案例1]基于Keras的手写数字图像识别

文章目录 一、工具与环境 二、深度学习环境的搭建 1.安装Anaconda 2.创建虚拟环境 第1步&#xff1a;打开Anaconda的命令窗口&#xff0c;即Anaconda Prompt 第2步&#xff1a;使用命令创建指定版本的python环境&#xff08;这里以py36命令环境名称举例&#xff09; 3.切换…

深度解析MethodHandle方法句柄之findspecial方法的原理

网上看过太多关于MethodHandle方法句柄的文章&#xff0c;但是基本上没有人能把其中的findspecial方法讲清楚&#xff0c;特别是findspecial的第四个参数specialCaller, 相信大家都不明白是干嘛用的&#xff0c;网上给出的水文是很多都是说&#xff1a; 执行到specialCaller的父…

kafka二

练一练 需求&#xff1a;写一个生产者&#xff0c;不断的去生产用户行为数据&#xff0c;写入到kafka的一个topic中 生产的数据格式&#xff1a; 造数据 {"guid":1,"eventId":"pageview","timestamp":1637868346789} isNew 1 {&quo…

面了一个32岁的程序员,只因这一点,一眼看穿是培训班出来的,简历都是假的.....

首先&#xff0c;我说一句&#xff1a;培训出来的&#xff0c;优秀学员大有人在&#xff0c;我不希望因为带着培训的标签而无法达到用人单位和候选人的双向匹配&#xff0c;是非常遗憾的事情。 最近&#xff0c;在网上看到这样一个留言&#xff0c;引发了程序员这个圈子不少的…

Kafka安装及架构

kafka的特点 高吞吐量、低延迟&#xff1a;kafka每秒可以处理几十万条消息&#xff0c;它的延迟最低只有几毫秒&#xff0c;每个topic可以分多个partition, 由多个consumer group 对partition进行consume操作。可扩展性&#xff1a;kafka集群支持热扩展持久性、可靠性&#xf…

机智云的离线语音识别模组,让家电变得更加智能和便捷

随着人们对智能化生活的需求不断增加&#xff0c;离线语音模组越来越受到欢迎。它可以为家庭、工作和娱乐提供更加智能和便捷的服务&#xff0c;例如通过语音指令控制家居设备、查询天气信息、播放音乐等。 “小智同学&#xff0c;打开灯光” “调到最亮” “正转一档” 人工智…

Golden Gate (GGX) ZK 预编译: 彻底改变游戏玩法,成本降低千倍

Golden Gate (GGX) 作为一种新型跨链基础设施协议&#xff0c;解决了困扰 Web3.0 Layer1 和 Layer2 的跨链通信和流动性转换难题。 其解决方案主要涉及两个核心: 1) 与协议无关的通信&#xff0c;可以实现主流标准消息的传递&#xff0c;包括 IBC、XCMP 和 LayerZero 等标准。 …