【C语言】动态内存分配

目录

动态内存分配存在的原因

首先我们知道内存开辟的方式有:

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

上述的两种方式有两个特点:
1)空间开辟的大小是固定的
2)数组在声明时需要明确其大小,即数组长度,其内存在编译时分配。
但是在实际应用中我们所需要的不止是一个固定大小的数组,而常常需要堆数组的长度进行修改,而数组在编译时开辟空间的方式就不能满足我们的需求了,这时我们就需要用到动态内存分配了。

动态内存函数

我们常用的动态内存函数有malloc,free,calloc,realloc等等

1. malloc 和 free

C语言给出了这么一个动态内存开辟的函数:

void* malloc (size_t size);

这个函数会向内存申请一块连续可用的空间,同时返回指向这块空间的指针。
在这里我们来了解一下内存的大致分布:内存分为栈,堆,静态区等等部分。其中栈是临时变量所存储的空间,同时函数开辟空间时也是在栈中进行的,这被称为函数栈帧,有机会的话后面会介绍的;堆就是动态内存开辟时所申请的空间;而静态区是静态变量开辟时所申请的空间,比如全局变量,static修饰的变量等。【C语言】动态内存分配

而malloc函数向内存申请空间就是在堆申请的。
malloc函数在申请空间时会有两种情况:
1)若空间开辟成功,则返回指向该空间的指针。
2)若空间不足,开辟失败,则返回NULL。
而函数的返回值为void* ,是因为malloc在开辟空间时并不知道所要开辟空间的类型,在具体使用时使用者需要自己决定;其次,若size为0,malloc的行为是标准未定义的,具体取决于编译器。
与malloc相对的,C语言提供了另外一个函数free,是专门用来做动态内存的释放和回收的,其函数原型如下:

void free (void* ptr);

对于free函数有两点需要注意:
1)若所free的指针不是指向动态开辟的空间,那么free的行为是标准未定义的。
2)若所free的指针是NULL,则什么也不会发生。
malloc和free都声明在<stdlib.h>头文件中。
需要注意的是malloc和free通常是成对使用的,否则会造成内存泄露。
举个例子:

int main()
{
	int* a = (int*)malloc(sizeof(int) * 10);//向内存申请10个int字节的连续空间
	if (a == NULL)//判断内存申请是否成功
	{
		printf("malloc fail");
		exit(-1);
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		a[i] = i;
	}
	free(a);//释放a所指向的内存空间
	a = NULL;//free函数并不会将a置空,那么此时a为野指针,需要我们手动置空
	return 0;
}

2. calloc

C语言同时还提供了calloc函数来实现动态内存分配,其函数原型为:

void* calloc (size_t num, size_t size);

calloc函数的功能是向内存申请num个size大小的连续空间,并在申请的同时将这些空间初始化为0;与malloc不同的是calloc会在返回地址之前将申请的空间中的每个字节初始化为0,也就是说若我们在申请空间时需要初始化所申请的空间,calloc可以方便我们的操作。比如:

`int main()
{
	int* a = (int*)calloc(10, sizeof(int));
	if (a == NULL)
	{
		printf("calloc fail");
		exit(-1);
	}
	free(a);
	a = NULL;
	return 0;
}`

在这个函数中,calloc向内存申请空间后的连续40个字节空间内容均被初始化为0。
【C语言】动态内存分配

3.realloc

从功能上将上述的malloc和calloc函数都并未实现可以是内存动态改变的功能,它们都只是向内存申请了一片固定的空间,而实际上想要真正对内存进行动态的调整就需要realloc函数,其函数原型为:

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

参数中的ptr为要调整的内存空间的起始地址;size为调整后空间的新大小;
函数的返回值为调整后的空间的起始地址。
函数在调整内存空间时,有可能会将原内存空间的内容转移到新的内存空间上,这就与realloc调整空间可能出现的两种情况有关:
1)原内存空间后的空间足够大,足以调整。
2)原内存空间后没有足够的空间,不足以开辟所需要的空间。【C语言】动态内存分配
由于上述两种情况的存在,realloc在具体使用过程中需要注意一些:

int main()
{
	int* a = (int*)malloc(sizeof(int) * 10);//向内存申请40个字节的连续空间
	if (a == NULL)
	{
		printf("malloc fail");
		exit(-1);
	}
	int* tmp = (int*)realloc(a, sizeof(int) * 20);//调整原空间使其扩大为80个字节
	if (tmp == NULL)
	{
		printf("realloc fail");
		exit(-1);
	}
	a = tmp;
	free(a);
	a = NULL;
	return 0;
}

上述程序中的tmp是必要的,这是因为如果原内存后没有足够大的空间,那么若代码改为a = (int*)realloc(a,sizeof(int)*20);a在realloc函数执行过程中会改变指向,致使realloc找不到原内存,从而无法将原内存空间的数据拷贝到新开辟的内存空间中,导致数据丢失,因此需要用一个临时变量来记录新开辟的内存空间,在修改原指针。
另外一点,若realloc中的ptr参数为NULL,那么realloc函数就和malloc函数的功能一样了。

上一篇:LibreOJ #6008. 「网络流 24 题」餐巾计划 最小费用最大流 建图


下一篇:「APIO2018新家」