动态内存管理【上篇】

这篇具有很好参考价值的文章主要介绍了动态内存管理【上篇】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

动态内存管理【上篇】


动态内存管理【上篇】

⚙️1.为什么存在动态内存分配

🥰我们已经掌握的内存开辟方式有:

int val = 20; //在栈空间上开辟4个字节
char arr[10] = {0}; //在栈空间上开辟10个字节的连续空间

但是上述的开辟空间的方式有两个特点:
🔴 1.空间开辟大小是固定的
🔴 2.数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。
这时候就只能试试动态开辟了👇
动态内存管理【上篇】

⚙️2.动态内存函数的介绍

📬2.1. malloc函数

C语言提供了一个动态内存开辟的函数:👇

void* malloc(size_t size);

这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
🔴如果开辟成功,则返回一个指向开辟好空间的指针
🔴如果开辟失败,则返回一个NULL指针,因此 malloc函数的返回值一定要做检查
🔴返回值的类型是void* ,所以 malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定
🔴如果参数 size为0,malloc的行为是标准是未定义的,取决于编译器

可以参考一下 cplusplus 中的资料👇
动态内存管理【上篇】

📬2.2. free函数

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:👇

void free(void* ptr);

free函数用来释放动态开辟的内存
🔴如果参数ptr指向的空间不是动态开辟的,那么 free函数的行为是未定义的
🔴如果参数ptr是NULL指针,则 free函数什么事都不做

可以参考一下 cplusplus 中的资料👇
动态内存管理【上篇】

malloc函数与free函数相配合着使用:
🥰请看代码与注释👇

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>

int main()
{
	//申请
	int* p = (int*)malloc(20);//字节
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		*(p + i) = i + 1;
	}
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(p + i));
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

🚩malloc和free函数都声明在<stdlib.h>头文件中

📬2.3. calloc函数

C语言还提供了一个函数calloc,calloc函数也用来动态内存分配,函数原型如下:👇

void* calloc(size_t num,size_t size)

🔴函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0
🔴与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0

可以参考一下 cplusplus 中的资料👇动态内存管理【上篇】

🌰举个栗子👇

int main()
{
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		printf("calloc()-->%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}
	//释放
	free(p);
	p = NULL;

	return 0;
}

动态内存管理【上篇】

🚩所以如果我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成任务

🚩calloc和malloc的对比:

🔴1.参数不一样
🔴2.都是在堆区上申请内存空间,但是malloc不初始化,calloc会初始化为0

🔴如果要初始化,就使用calloc
🔴不需要初始化,就可以使用malloc

📬2.4. realloc函数

🔴realloc函数的出现让动态内存管理更加灵活

可以参考一下 cplusplus 中的资料👇
动态内存管理【上篇】

🔴有时我们会发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的调整。那 realloc函数就可以做到对动态开辟内存大小的调整
函数原型如下:👇

void* realloc(void* ptr,size_t size);

🔴ptr是要调整的内存地址
🔴size调整之后新大小
🔴返回值为调整之后的内存起始位置
🔴这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
🔴realloc在调整内存空间的时候存在两种情况:

🚩情况1:原有空间之后有足够大的空间:
要扩展内存就直接在原有内存之后直接追加空间,原来空间的数据不发生变化
🚩情况2:原有空间之后没有足够大的空间:
扩展的方法是:在堆空间上另找一个合适大小的连续空间来使用,这样函数返回的是一个新的内存地址

动态内存管理【上篇】
🥰请看代码与注释👇

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		p[i] = i + 1;
	}

	int* ptr = (int*)realloc(p, 40);
	if (ptr != NULL)
	{
		p = ptr;
	}
	else
	{
		printf("realloc: %s\n", strerror(errno));
	}
	//使用
	for (i = 5; i < 10; i++)
	{
		p[i] = i + 1;
	}
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);
	}

	//释放
	free(p);
	p = NULL;

	return 0;
}

动态内存管理【上篇】

⚙️3.常见的动态内存错误

🔒3.1.对NULL指针的解引用操作

🥰请看代码与注释👇

int main()
{
	int* p = (int*)malloc(20);
	//可能会出现对NULL指针的解引用操作
	//所以malloc函数的返回值是要判断的
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		p[i] = i;
	}
	free(p);
	p = NULL;

	return 0;
}

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

🥰请看代码与注释👇

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}

	int i = 0;
	//越界访问
	for (i = 0; i < 10; i++)
	{
		p[i] = i;
	}
	free(p);
	p = NULL;

	return 0;
}

🔒3.3.对非动态开辟内存使用free释放

🥰请看代码与注释👇

int main()
{
	int arr[10] = { 1,2,3,4,5 };
	int* p = arr;
	//....
	free(p);
	p = NULL;

	return 0;
}

🔒3.4.使用free释放一块动态开辟内存的一部分

🥰请看代码与注释👇

int main()
{
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 0;
	}

	int i = 0;
	//[1] [2] [3] [4] [5] [ ] [ ] [ ] [ ] [ ]
	for (i = 0; i < 5; i++)
	{
		*p++ = i + 1;
		p++;//这里p不再指向动态内存的起始位置
	}
	//释放(p必须指向起始位置地址)
	free(p);
	p = NULL;

	return 0;
}

🔒3.5.对同一块动态内存多次释放

🥰请看代码与注释👇

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	//使用
	// 
	free(p);
	//释放
	free(p);//(释放过一次就不能再进行释放了)
	p = NULL;

	return 0;
}

🔒3.6.动态开辟内存忘记释放(内存泄漏)

🥰请看代码与注释👇

void test()
{
	int* p = (int*)malloc(100);
	if (NULL != p)
	{
		*p = 20;
	}
}

int main()
{
	test();
	while (1);
}

📍malloc、calloc、realloc所申请的空间,如果不想使用,需要free释放
📍如果不使用free释放:
📍程序结束之后,也会由操作系统回收
📍如果不使用free释放,程序也不结束 ---- 内存泄漏
📍自己申请的,尽量自己释放
📍自己不释放的,告诉别人释放

⚙️4.经典笔试题

💡4.1.题目(1)

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}

void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

int main()      
{
	Test();
	return 0;
}

🔴1.调用GetMemory函数的时候,str的传参为值传递,p是str的临时拷贝,所以在GetMemory函数
内部将动态开辟空间的地址存放p中的时候,不会影响str,所以GetMemory函数返回之后,str中依然是
NULL指针。strcpy函数就会调用失败,原因是对NULL的解引用操作,程序会崩溃

🔴2.GetMemory函数内容malloc申请的空间没有机会释放,造成了内存泄漏。

💡4.2.题目(2)

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

int main()
{
	Test();
	return 0;
}

🔴返回栈空间地址的问题

🔴GetMemory函数内部创建的数组是临时的,虽然返回了数组的起始地址给了str,但是数组的内存出了GetMemory函数就被回收了,而str依然保存了数组的起始地址,这时如果使用str,str就是野指针

💡4.3.题目(3)

void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

int main()
{
	Test();
	return 0;
}

🔴没有free释放

💡4.4.题目(4)

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	//str = NULL;
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

int main()
{
	Test();
	return 0;
}

🔴没有置空,非法访问

总结🥰
以上就是 动态内存管理【上篇】 的内容啦🥳🥳🥳🥳
本文章所在【C语言知识篇】专栏,感兴趣的烙铁可以订阅本专栏哦🥳🥳🥳
欲知后事如何,请听下篇分解喽😘😘😘
前途很远,也很暗,但是不要怕,不怕的人面前才有路。💕💕💕
小的会继续学习,继续努力带来更好的作品😊😊😊
创作写文不易,还多请各位大佬uu们多多支持哦🥰🥰🥰

动态内存管理【上篇】文章来源地址https://www.toymoban.com/news/detail-414534.html

到了这里,关于动态内存管理【上篇】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 探索C语言的内存魔法:动态内存管理解析

    ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C语言学习 贝蒂的主页:Betty‘s blog 通过前面的学习,我们已经掌握了两种开辟内存的方法,分别是: 但是静态开辟的空间明显有两个缺陷: 空间开辟⼤⼩是 固定 的。 数组在申明的时候,

    2024年02月19日
    浏览(45)
  • 【C进阶】-- 动态内存管理

    目录 1. 为什么存在动态内存分配❓ 2. 动态内存函数的介绍 2.1 malloc和free✅ ①申请:1️⃣ ②使用:2️⃣  ③释放:3️⃣ 2.2 calloc 🧨与malloc的区别: 2.3 realloc 3.常见的动态内存错误 3.1 对NULL指针的解引用操作 🎇3.2 对动态开辟空间的越界访问 3.3对非动态开辟内存使用free释放🍕

    2024年02月04日
    浏览(32)
  • C++动态内存管理+模板

    💓博主个人主页:不是笨小孩👀 ⏩专栏分类:数据结构与算法👀 C++👀 刷题专栏👀 C语言👀 🚚代码仓库:笨小孩的代码库👀 ⏩社区:不是笨小孩👀 🌹欢迎大家三连关注,一起学习,一起进步!!💓 C语言内存管理方式在C++中可以继续使用,但有些地方就无能为力,而且使

    2024年02月09日
    浏览(45)
  • 动态内存管理-c语言

    目录 1.为什么要有动态内存分配 2.malloc函数和free函数 malloc 函数原型 栗子 free 函数原型 栗子 3.calloc和***realloc*** 3.1calloc函数 原型如下: 栗子 3.2***recalloc*** 第一种情况 第二种情况 第三种情况 recalloc模拟实现calloc函数 4.六大常⻅的动态内存的错误 4.1对NULL指针的解引⽤操作

    2024年03月22日
    浏览(38)
  • c语言-动态内存管理

    1.我们一般的开辟空间方式: 2.这样开辟空间的特点 (1)申请的空间大小是固定的 (2)像数组那样一开始就要确定大小,一旦确定大小就不能改变了 3.动态内存 对于程序来说上述的内存申请是不能满足 因此为了能够对内存进行调整,C语言引入了动态内存开辟,让程序员自

    2024年02月04日
    浏览(36)
  • C语言->动态内存管理

    文章目录  ✅作者简介:大家好,我是橘橙黄又青,一个想要与大家共同进步的男人😉😉 🍎个人主页:橘橙黄又青_C语言,函数,指针-CSDN博客 目的:学习malloc,free,calloc,realloc函数的使用。 内存函数在#includestdio.h头文件里面。 我们已经掌握的内存开辟⽅式有: 但是上述的

    2024年02月04日
    浏览(50)
  • C语言:动态内存管理

    先点赞再观看哦! 学习数据结构之前,一定要对指针、结构体、动态内存管理进行深入学习! 小伙伴们可以看看博主之前的文章! 今天重点介绍动态内存开辟!十分重要哈! 我们已知的内存开辟方式有什么呢?? 但是上述开辟的空间有三个特点: 1、空间开辟的大小是固定

    2024年01月22日
    浏览(38)
  • 【C++初阶】动态内存管理

      说明: 1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的; 2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口     创建共享共享内存,做进程间通信; 3. 堆用于程序运行时动态内存分配,堆是可以上增长的;

    2024年02月06日
    浏览(41)
  • 【C++】——动态内存管理

      目录 🌞导读 🌔C/C++内存分布  🌔C++内存管理方式  🌗new/delete操作内置类型  🌗new和delete的使用方法 🌔operator new与operator delete函数 🌔new和delete的实现原理 🌗内置类型 🌗自定义类型 🌔定位new表达式  🌗使用格式 🌔malloc/free和new/delete的区别 🌟作者简介: 日出等日

    2024年02月07日
    浏览(35)
  • 动态内存管理、柔性数组

    我们已经掌握的内存开辟的方式: 上面开辟的空间大小是固定不可变的 数组申请的大小也是确定了之后就不能改变 这里就引入了动态内存的开辟,可以根据内存的需要进行更改  malloc和free malloc的功能是申请size个连续可用 size_t类型的字节 空间,并返回指向这块空间的void

    2024年02月22日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包