【C语言】动态内存

文章介绍了C语言中动态内存管理的关键函数,包括malloc用于分配内存,calloc在分配内存时初始化为0,以及realloc用于调整已分配内存的大小。强调了动态内存分配的安全性,如检查malloc和calloc的返回值,避免内存泄漏,以及正确释放内存以防止野指针。同时,文章列举了常见的错误情况,如越界访问、对非动态内存使用free以及错误地释放内存块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

在C语言中,我们经常会遇到创建变量和数组来解决相应问题编译代码的情况,这时候我们创建的变量大小都是固定的,如果编译要求进行数据扩容,或创建变量过大造成栈空间浪费,且在栈区创建的变量也有大小限制,我如果我们想要一个可以自己进行扩容,针对相应数据创建相应空间的变量,就要用到我们的动态内存管理函数:malloc calloc和realloc

malloc

 cplusplus上的定义

动态内存函数需要调用头文件stdlib,malloc是在内存的动态存储区中分配一个长度为size的连续空间。此函数的返回值是分配区域的起始地址,也就是说,他是一个指针函数,所返回的指针指向该分配域的开头位置,是使用最多最普遍的动态内存创建函数

示例

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

从上面的代码可以看到,在创建了动态变量后,代码进行了一次判断,以用来确定该动态内存变量是否创建成功,这是因为可能会出现对NULL指针的解引用操作,所以malloc函数的返回值要判断,以确保变量的成功创建和安全性。在这里我们将malloc创建的20个字节的变量强制类型转化为int*类型,并用整型指针接收。

接下来是malloc的使用:

示例

	int i = 0;
	for (i = 0; i < 5; i++)
	{
		p[i] = i + 1;
	}
	for (i = 0; i < 5; i++)
	{
		printf("%d ", p[i]);
	}
	//int i = 0;
	//for (i = 0; i < 5; i++)
	//{
	//	*(p + 1) = i + 1;
	//}
	//for (i = 0; i < 5; i++)
	//{
	//	printf("%d ", *(p + 1));
	//}
	//释放
	free(p);
 	p = NULL;
	return 0;
}

在上面的示例中,可以看到我们创建的动态内存变量可以进行正常运用,他可以当成数组进行操作,而在使用完我们创建的变量后,需要手动进行销毁,为什么呢?因为动态内存变量是创建在堆区上的,而堆区中的数据虽然没有规定大小,但是不能自动释放,所以我们需要用到free函数对创建的动态内存进行释放,如果不进行释放,当程序关闭后,系统也会自行进行回收,但不推荐这样做,如果不释放,程序也不结束,会造成内存泄漏,因此,所申请的空间,如果不想使用,需要free函数。在内存进行释放后,我们看到原本用来接收malloc创建的内存的指针p现在变成了一个野指针,众所周知,野指针是非常危险的,所以,我们给p指针赋值一个NULL,让他变成一个空指针,确保安全性。

calloc

cplusplus上的定义

calloc与malloc相似,但是calloc在创建内存时,会将其初始化成0,也就是说:calloc和malloc参数不同,都是在堆区上申请内存空间,但是malloc不初始化,calloc会初始化为0,如果要初始化用calloc

示例

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函数参数也与malloc有些小小的不同,calloc的参数决定了在使用他创建动态内存时,需要指定元素个数,即多少个元素为多少字节,剩下的判断和释放则与malloc相同。

realloc

cplusplus上的定义

realloc与前两个函数不同,他是调整动态内存空间的函数,而不是创建动态内存,当我们的动态内存创建的过大,就可以使用该函数来修改动态内存,值得注意的是,realloc会找更大的空间,而不是在原有空间延续,将原有的数据拷贝到新的空间,释放原有空间,然后返回新空间地址,且如果修改失败返回空指针。

示例

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		printf("malloc:%s", strerror(errno));
		return 1;
	}
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		p[i] = i + 1;
	}
	//p = realloc(p, 40);//最好不要用p接受,如果realloc调整失败返回空指针,会丢失原有数据
	//用临时指针接受
	int* ptr = (int*)realloc(p, 40);
	if (ptr != NULL)
	{
		p = ptr;
		for (i = 5; i < 10; i++)
		{
			p[i] = i + 1;
		}
		for (i = 0; i < 10; i++)
		{
			printf("%d ", p[i]);
		}
	}
	else
	{
		printf("%s\n", strerror(errno));
		return 1;
	}
	free(p);
	p = NULL;
	return 0;
}

从示例中我们可以发现:realloc的返回值不能直接赋值到p中,这是因为如果realloc调整失败返回空指针,赋给p后会丢失原有数据,所以用临时指针接收。

常见错误

越界访问:

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		return 1;
	}
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		p[i] = i;
	}
	//越界访问不可取
	free(p);
	p = NULL;
	return 0;
}

对非动态开辟内存使用free函数

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

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

int main()
{
	int* p = (int*)malloc(40);
	int i = 0;
	if (p == NULL)
	{
		return 1;
	}
	for (i = 0; i < 5; i++)
	{
		*p = i + 1;
		p++;
	}
	//这里释放的是p++过后的地址,不是起始地址
	free(p);
	p = NULL;
	return 0;
}

对一块动态内存多次释放

int main()
{
	int* p = (int*)malloc(20);
	if (p == NULL)
	{
		return 1;
	}
	free(p);
	p = NULL;//将其变为空指针后可以再释放
	free(p);
	p = NULL;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值