内存动态分配函数

主要涉及malloc/calloc/realloc和free四个函数,头文件:#include <stdlib.h>

(1)malloc

函数原型:

void *malloc(size_t size);

说明

向系统申请分配一个长度为size大小字节的内存块,并返回内存块的起始地址。

返回值

如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

演示示例:

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

int main()
{
	/* 1. malloc返回的是void *类型,在需要时候的时候要做一个适合目标指针类型的转换,比如这里转换成int *;
		如果是 char *p_a = (char *)malloc(10);
	*/
	int *p_a = (int *)malloc(10 * sizeof(int));
	//int *p_a = (int *)malloc((size_t)(1024*1024*1024*2));  //执行此句就会执行失败,
        //所以malloc之后一定要做一个是否申请内存成功的判断
	if (!p_a)
	{
		printf("malloc 失败, p_a = %p\n", p_a);
		exit(1);
	}

	// 2.malloc分配的内存它的值是随机
	printf("malloc刚申请的一段内存其值是:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}
	printf("\n");


	// 赋初值
	for (int i = 0; i < 10; i++)
	{
		*(p_a + i) = i;
	}
	printf("初始化后的数据:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}
	printf("\n");

	printf("free前的p_a = %p\n", p_a);

	free(p_a);
	//p_a = NULL;


	// 3. 测试free后指向的内存区域数值
	printf("free后的p_a = %p\n", p_a);
	printf("测试free后指向的内存区域数值:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}
	printf("\n");
/*
	printf("测试free还往其指向的内存空间写数据:\n");
	for (int i = 0; i < 10; i++)
	{
		*(p_a + i) = i; //往free后的空间写数据,程序出错
	}
*/
	printf("程序结束\n");
	return 0;
}

C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

使用结论:

(1)malloc返回的是void *类型,在需要时候的时候要做一个适合目标指针类型的转换,

  比如char *p_a = (char *)malloc(10);

(2)malloc分配的内存它的值是随机;比如分配的字符串空间char *p_a = (char *)malloc(10),如果我们直接操作strlen(p_a)则结果不确定。可以用memset去赋予初值。

(3)在free后就不要再去操作该段内存区(不管是读还是写)。

(4)malloc后要判断内存是否申请成功。

(2)calloc

函数原型:

void *calloc(size_t n, size_t size);

返回值

如果分配成功则返回指向被分配内存的指针(此存储区中的初始值已被设置为0),否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

与malloc的区别

(1)传入参数不一样;

比如申请同样10个int的内存空间

int *p_a = (int *)malloc(10*sizeof(int));

int *p_b = (int *)calloc(10, sizeof(int));

(2)calloc申请的内存是有将其值初始化为0,而malloc则是随机的;

和malloc相同之处

(1)都是要用free去释放申请的内存;

(2)该段内存被free后都不要再去操作该段内存(不管读还是写)

什么使用calloc或malloc呢?

(1)如果你想把申请的内存区域初始化为0,可以使用calloc;

初始化为0是要花时间的。

(2)如果你的应用在内存申请后会手动赋予值,可以使用malloc。

即使你用了malloc,但是想把数据初始化为0,则用memset去初始化就可以了。

代码演示:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	//int *p_a = (int *)malloc(10*sizeof(int));
	// 1. calloc的参数和malloc有区别
	int *p_a = (int *)calloc(10, sizeof(int));

	if (!p_a)
	{
		printf("malloc 失败, p_a = %p\n", p_a);
		exit(1);
	}

	// 2. calloc申请的一段内存空间其值都被初始化为0
	printf("calloc刚申请的一段内存其值是:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}
	printf("\n");


	
	for (int i = 0; i < 10; i++)
	{
		*(p_a + i) = i;
	}
	printf("初始化后的数据:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}
	printf("\n");

	printf("free前的p_a = %p\n", p_a);

	free(p_a);
	//p_a = NULL;

	// 3. 测试free后指向的内存区域数值
	printf("free后的p_a = %p\n", p_a);
	printf("测试free后指向的内存区域数值:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}
	return 0;
}

结果:

C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

(3)realloc

函数原型

void* realloc(void* ptr, unsigned newsize);

给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度。

返回值

如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。

说明

realloc可以对给定的指针所指的空间进行扩大或者缩小,无论是扩大或是缩小,原有内存的中内容将保持不变。当然,对于缩小,则被缩小的那一部分的内容会丢失。

realloc并不保证调整后的内存空间和原来的内存空间保持同一内存地址。

比如C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

相反,realloc返回的指针很可能指向一个新的地址。

图示理解:比如要重新分配已经malloc的内存

C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

代码演示:

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

int main()
{
	int *p_a = (int *)malloc(10 * sizeof(int));     //申请10*sizeof(int)字节的空间

	// 1. 给p_a指向的内存做初始化
	for (int i = 0; i < 10; i++)
	{
		*(p_a + i) = i;           // 赋值
	}

	printf("\n初始化p_a指向内存的数值:\n");
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p_a + i));
	}

	// 1. 扩充数据,从10个int变为15个int
	printf("\n扩充之前p_a = %p\n", p_a);
	p_a = (int *)realloc(p_a, 15 * sizeof(int));
	printf("扩充之后p_a = %p\n", p_a);
	printf("\n扩充之后的的数值:\n");	// 扩充后地址空间是很有可能发生变化的
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", *(p_a + i));	// 原有的内容的数值没有变化, 但扩充之后的数值是不确定的
	}

	// 2. 缩减数据
	p_a = (int *)realloc(p_a, 5 * sizeof(int));
	printf("\n缩减之后p_a = %p\n", p_a);
	printf("\n缩减之后的的数值:\n");	// 
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", *(p_a + i));	//  缩减内存后被缩减部分的数据有变化
	}

	// 测试越界使用
	for (int i = 0; i < 10; i++)
	{
		*(p_a + i) = i;           // 赋值
	}

	free(p_a);
	p_a = NULL;     // 内存释放后将p_a置为NULL,这是良好的编程习惯

	printf("程序结束\n");
	return 0;
}

结果:

C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

注意:

* (1)realloc扩充内存时,原有的数据不变,但扩充的内存的数值是不确定的;

* (2)扩展内存后其指针和原来可能不一样;

* (3)缩减内存后只能使用其指定长度的数据,不要越界使用。

思考:

使用malloc,memcpy和free去实现一个类似realloc的函数?

//待补充

(4)free

函数原型:

void free(void *ptr)

释放ptr指向的存储空间(ptr指向的内存需要由malloc/calloc或realloc动态申请)。

说明:

在释放内存后,建议将指针置为NULL,这是良好的编程习惯,C语言内存动态分配函数——malloc、calloc、realloc的用法与区别以及free

相关文章: