动态内存管理

目录

一、为什么要有动态内存分配

二、动态内存开辟的函数

        2.1 malloc函数

         2.2 calloc函数

        2.3 realloc函数

三、内存释放函数 

四、柔性数组

五、 经典例题


一、为什么要有动态内存分配

        我们在写代码时,编译器都会自动为我们分配空间,但会有这么两个特点:

         空间开辟⼤⼩是固定的。

        数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整。

        但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知道,那数组的编译时开辟空间的⽅式就不能满⾜了。

         C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。 

二、动态内存开辟的函数

        2.1 malloc函数

void* malloc (size_t size);

        这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。

         如果开辟成功,则返回⼀个指向开辟好空间的指针。

        如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。

         返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃ ⼰来决定。

        如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。  

         2.2 calloc函数

void* calloc (size_t num, size_t size);

        函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。

        与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

        2.3 realloc函数

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

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

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

        realloc在调整内存空间的是存在两种情况:

        情况1:原有空间之后有⾜够⼤的空间

        情况2:原有空间之后没有⾜够⼤的空间

        情况1

        当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。

        情况2

        当是情况2 的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩ 的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

三、内存释放函数 

        学习完内存开辟函数后,我们随后会来学习内存释放函数。那我们为什么要学习内存释放函数呢?内存开辟函数就是向内存中借一块空间,俗话说:有借有还。所以,我们要来学习内存释放函数来进行归还。所以在学习前记住一句话:只要开辟就要释放。

void free (void* ptr);

        内存释放函数为free函数,如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。

        如果参数 ptr 是NULL指针,则函数什么事都不做。

四、柔性数组

        柔性数组是什么?相信看到这你有点小懵,难道会柔道的数组称之为柔性数组?非也。柔性数组为结构体中定义的处于结构体最后一个成员的数组(最好不要定义其大小,有的编译器会报错)。

typedef struct s
{
	int a;
	int arr[];
};

        柔性数组的特点:

        柔性数组的前面必须包含其他结构体成员。

        sizeof求其结构体大小时不会包含柔性数组的大小。

        使用malloc为柔性数组开辟空间时,要大于结构体的大小以符合柔性数组的期待。

         用法如下:

​
#include<stdio.h>
typedef struct s
{
	int a;
	int arr[];
}type_a;
int main()
{
	int a = 0;
	type_a* p = (type_a*)(malloc(sizeof(type_a) + 100 * sizeof(int)));
	p->a = 100;
	for (int i = 0; i < 100; i++)
	{
		p->arr[i] = i;
	}
	free(p);
	p = NULL;
	return 0;
}

​

五、 经典例题

#include<stdio.h>
#include<stdlib.h>
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

        以上代码运行会有什么后果?

        本题意图为为str开辟出一块空间,对吧?但是,在函数中p为局部变量,运行结束时会销毁,所以,它的地址传不回来,就会开辟失败。但,即使开辟成功,仍有问题,这是为什么呢?咱们在开始时,说过了要有借有还,所以应该要对其进行内存释放,否则,会造成内存泄漏。

#include<stdio.h>
#include<stdlib.h>
void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

         以上代码运行会有什么后果?

        在str开辟完空间后,又对其释放,那str能打印出来吗?答案是:不一定。为什么?内存不是没了吗?那里面存放的理应没了呀?对吧。其实,你可以这样理解:你在酒店开了个房间,第二天为了赶火车,不小心把身份证落下了,如果此时房间还没有被打扫,你还能在原位置找到,对吧?打扫了就不能在原位置找到了对吧?理解了我举得例子,相信你就理解了打印结果的情况。

        完!

相关推荐

  1. C++动态内存管理

    2024-03-30 23:42:06       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-30 23:42:06       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-30 23:42:06       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-30 23:42:06       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-30 23:42:06       18 阅读

热门阅读

  1. Spring Cache 快速入门

    2024-03-30 23:42:06       17 阅读
  2. docker 打包前台程序

    2024-03-30 23:42:06       16 阅读
  3. visual studio快捷键

    2024-03-30 23:42:06       18 阅读
  4. Android TV 4K UI

    2024-03-30 23:42:06       15 阅读
  5. Mysql中的那些锁

    2024-03-30 23:42:06       19 阅读
  6. axios请求类型是文件流怎么显示报错信息

    2024-03-30 23:42:06       14 阅读
  7. UI 神器 - Vue3 中如何使用 element-plus

    2024-03-30 23:42:06       18 阅读