【C语言:动态内存管理】

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

【C语言:动态内存管理】,C_language,c语言,开发语言

前言

文章的标题是动态内存管理,那什么是动态内存管理?为什么有动态内存管理呢?
回顾一下以前学的知识,我们已经掌握的开辟内存的方式有以下几种:

char ch = 'a';
int val = 10;
float f_val = 12.8f;
int arr[20] = { 0 };
//....

上述开辟内存的方式有几个弊端:

  1. 开辟空间的大小是固定的
  2. 数组在声明的时候,必须指定数组的长度,数组空间⼀旦确定了大小就不能调整了

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组在编译时就开辟空间的方式就不能满⾜了。
C语言引⼊了动态内存开辟,让程序员自己可以申请和释放空间,就比较灵活了。

1.malloc

【C语言:动态内存管理】,C_language,c语言,开发语言
这个函数的功能是:向内存申请一块连续的内存空间,然后返回一个指向该空间的起始地址的指针

  • 如果空间开辟成功,则返回一个指向该空间起始地址的指针。
  • 如果空间开辟失败,则返回一个NULL指针。因此,函数的返回值需要做检查
  • 开辟的空间不会被初始化
  • 返回值的类型是void*,所以该函数并不知道所开辟空间的类型,需要空间的使用者自己决定。
  • 当该函数的参数是0是,返回值是未定义的,取决于编译器。
  • 头文件stdlib.h

使用malloc函数申请40个字节的空间
【C语言:动态内存管理】,C_language,c语言,开发语言
当函数的参数是0时,vs2019中是随机值
【C语言:动态内存管理】,C_language,c语言,开发语言
既然空间是程序员申请的,那么最后由谁释放呢?

  1. 程序员自己释放。
  2. 程序结束,操作系统回收

下面就来介绍如何释放

2.free

C语言提供了另外⼀个函数free,专⻔是⽤来做动态内存的释放和回收的,函数原型如下:
【C语言:动态内存管理】,C_language,c语言,开发语言

  • 该函数用来释放由malloc、calloc、realloc申请的空间的
  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。
  • 该函数不会改变指针ptr的值,因此为了避免野指针的出现,要将其及时置为NULL。

【C语言:动态内存管理】,C_language,c语言,开发语言

3.calloc

【C语言:动态内存管理】,C_language,c语言,开发语言
该函数的功能跟malloc差不多,区别在于:

  • 该函数的参数有两个,一个是有几个元素,一个是每个元素的大小。
  • 该函数会将申请的空间初始化为全0。

【C语言:动态内存管理】,C_language,c语言,开发语言

4.realloc

【C语言:动态内存管理】,C_language,c语言,开发语言

realloc函数的出现让动态内存管理更加灵活。
有时会我们发现过去申请的空间太小了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使用内存,我们⼀定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。

  • ptr 是要调整的内存地址
  • size 调整之后新大小
  • 返回值为调整之后的内存起始位置。
  • 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间
  • realloc在调整内存空间的是存在两种情况:
    • 情况1:原有空间之后有足够大的空间
    • 情况2:原有空间之后没有足够大的空间
  • 情况1
    当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化
  • 情况2
    当是情况2的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

既然该函数有两种特殊情况,那我们在使用时也应该更加注意:
下面的代码有没有问题呢?

int main()
{
	//1. 先申请一部分空间
	int* p = (int*)malloc(20);
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	//2.扩容空间
	p = (int*)realloc(p, 40);
	if (NULL == p)
	{
		perror("realloc");
		return 1;
	}
	//3.使用
	//.......
	
	free(p);
	p = NULL;
	return 0;
}

假如realloc开辟空间失败了,那么它会返回一个空指针,p就是空指针了。
好家伙,没给我扩容成功,我以前的数据也没了,真可以!
所以在接收realloc的返回值时,我们应该使用你一个临时变量接收,判断临时变量是不是空指针,然后再对p进行操作。
所以,应该这样写:

#include<stdlib.h>

int main()
{
	//1. 先申请一部分空间
	int* p = (int*)malloc(20);
	if (NULL == p)
	{
		perror("malloc");
		return 1;
	}
	//2.扩容空间
	int* tmp = NULL;
	tmp = (int*)realloc(p, 40);
	//先判断realloc的返回值
	if (tmp != NULL)
	{
		p = tmp;
		tmp = NULL;
	}
	//3.使用
	//....
	free(p);
	p = NULL;
	return 0;
}

5.动态内存常见错误

  1. 对NULL指针的解引用操作
  2. 对动态开辟空间的越界访问
  3. 对非动态开辟内存使用free释放

【C语言:动态内存管理】,C_language,c语言,开发语言

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

【C语言:动态内存管理】,C_language,c语言,开发语言

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

【C语言:动态内存管理】,C_language,c语言,开发语言

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

6.动态内存经典笔试题分析

  1. 题目一
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

此处是值传递,并不会为str真正开辟空间,str还是NULL。
【C语言:动态内存管理】,C_language,c语言,开发语言

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

p变量生命周期为GetMemory函数内,函数结束,变量销毁。使用一个已经被销毁的变量的地址,结果是不可预测的
【C语言:动态内存管理】,C_language,c语言,开发语言

3.题目三

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

str指向的空间被释放,那么str就是野指针了,但是str还是指向那块内存空间;向str中拷贝world,那就是非法访问了

7.柔性数组

  1. 什么是柔性数组

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

struct s
{
	int i;
	char ch;
	int a[];  //int a[0]
};

其中a就是一个柔性数组。

  1. 柔性数组的特点
  • 结构中的柔性数组成员前⾯ 必须⾄少⼀个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存
  • 包含柔性数组成员的结构体用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构体的大小,以适应柔性数组的预期大小。
  1. 柔性数组的使用

【C语言:动态内存管理】,C_language,c语言,开发语言

如果我们不使用柔性数组,那么如何实现上述代码呢?
【C语言:动态内存管理】,C_language,c语言,开发语言

虽然不适用柔性数组也能达到目的,但使用柔性数组有两个好处:

  1. 方便内存的释放

如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。⽤⼾调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。

  1. 有利于访问速度

连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。

8.C/C++中的内存区域划分

【C语言:动态内存管理】,C_language,c语言,开发语言

  1. 栈区

栈区(stack):在执⾏函数时,函数内局部变量的存储单元都可以在栈上创建,函数执⾏结束时这些存储单元⾃动被释放。栈内存分配运算内置于处理器的指令集中,效率很⾼,但是分配的内存容量有限。栈区主要存放运⾏函数⽽分配的局部变量、函数参数、返回数据、返回地址等

  1. 堆区

堆区(heap):⼀般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配⽅方式似于链表。

  1. 静态区

数据段(静态区):存放全局变量、static修饰的变量。程序结束后由系统释放。

  1. 代码段

代码段:存放函数体(类成员函数和全局函数)的⼆进制代码

本次的分享就到这里啦!如有错误请纠正。文章来源地址https://www.toymoban.com/news/detail-774355.html

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

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

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

相关文章

  • 【C语言:动态内存管理】

    文章的标题是动态内存管理,那什么是动态内存管理?为什么有动态内存管理呢? 回顾一下以前学的知识,我们已经掌握的开辟内存的方式有以下几种: 上述开辟内存的方式有几个弊端: 开辟空间的大小是固定的 数组在声明的时候,必须指定数组的长度,数组空间⼀旦确定

    2024年02月03日
    浏览(27)
  • <C语言> 动态内存管理

    为什么存在动态内存分配? 上述的开辟空间的方式有两个特点: 空间开辟大小是固定的。 数组在声明的时候,必须指定数组的长度,它所需要的内存在编译时分配。 但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组

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

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

    2024年03月22日
    浏览(28)
  • 【C语言】动态内存管理

    大家好,我是苏貝,本篇博客带大家了解动态内存管理,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️ 我们已经掌握的内存开辟方式有: int val = 20; 在栈空间上开辟四个字节 char arr[10] = {0}; 在栈空间上开辟10个字节的连续空间 但是上述的开辟空间的方式有

    2024年01月16日
    浏览(27)
  • c语言-动态内存管理

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

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

    目录 1.为什么存在动态内存管理 2.动态内存函数的介绍 2.1 malloc 与 free  2.2 calloc 2.3 realloc 3.常见的动态内存错误 4.几个经典笔试题 6.柔性数组 我们已经掌握的内存开辟方式有: 但是上述的开辟空间的方式有两个特点: 空间开辟的 大小是固定的 。 数组在声明的时候, 必须指

    2024年02月11日
    浏览(74)
  • 【C语言】进阶——动态内存管理

    我们已经掌握的内存开辟方式有: 但上述开辟空间的方式有两个 特点: 空间开辟 大小 是固定的 数组在声明的时候,必须指定 数组的长度 ,他需要内存在 编译时分配 对于空间的需求,不仅仅是上述的情况,有时候我们需要的空间大小在程序运行的时候才知道,那数组的编

    2024年02月06日
    浏览(35)
  • C语言之动态内存管理

           在C语言中我们在栈上开辟的空间是固定的,一旦确定好大小就不能随意改变,就想你创建了 int i = 10; int arr[10] = {0}; int i 一旦确定下来就是四个字节,arr一旦确定好大小在重新运行时也是不能改变的。 为此C语言引入了动态内存空间开辟,这是给程序员自主开辟好空间

    2024年04月09日
    浏览(61)
  • C语言【动态内存管理 前篇】

    C语言中的数据结构通常是固定大小的。例如,一旦程序完成编译,数组元素的数量就是固定的。 说到这里,有人就要说: 变长数组呢? 在C99中,变长数组的长度在运行时确定,但在数组的生命周期内仍然是固定的,因为在编写程序时强制选择了大小,所以固定大小的数据结

    2024年02月02日
    浏览(31)
  • 【进阶C语言】动态内存管理

    前言 📕作者简介: 热爱跑步的恒川 ,致力于 C/C++、Java、Python 等多编程语言,热爱跑步,喜爱音乐的一位博主。 📗本文收录于 C语言进阶 系列,本专栏主要内容为数据的存储、指针的进阶、字符串和内存函数的介绍、自定义类型结构、动态内存管理、文件操作等,持续更

    2023年04月18日
    浏览(24)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包