动态内存malloc,calloc,realloc如何使用,使用场景及使用free释放内存时崩溃的原因

本文详细解释了内存区域、void与void*的区别,以及malloc、calloc、realloc和free在内存管理中的应用,特别关注了导致free崩溃的几种常见原因,如越界、指针移动和重复释放内存。
摘要由CSDN通过智能技术生成

目录

1.内存区域

2.void与void*

3.应用场景

4.malloc

5.calloc

6.realloc

7.free崩溃的原因

7.1引入

7.2具体原因

7.2.1越界

7.2.2指针移动

7.2.3重复释放同一段内存


1.内存区域

局部变量:定义在函数内部的变量,包括形参,在栈(stack),作用域在函数内部有效,生存周期:进入函数创建,退出函数销毁。

栈:内存空间,局部变量所在的内存区域,系统自行管理内存的分配和回收,容量小(1M,不同的系统存在差异)
:内存空间,动态内存所在的内存区域,由程序(员)管理内存的分配和回收(释放),容量大(1G,不同的系统存在差异)

2.void与void*

void:没有,只能用于返回值或参数列表,表示无返回值或无参数(这个一般省略不写)
void a;错误的
void*:通用指针或泛型指针,没有具体的数据类型的指针,不能[],+i等运算,使用时需要强制类型转换

3.应用场景

1.需要根据变量作为长度定义数组。
2.函数结束后还需要继续使用的内存 ( 例如返回局部数组的地址 , 链表)
3.长度较大的数组 ( 大内存 , 超过栈 1M 的大小)

4.malloc

动态申请内存,需要引用stdlib.h,没有默认值,具体参考帮助手册。

应用场景1.

//1.需要根据变量作为长度定义数组
int main()
{
	int n = 10;
	//int arr[n];//error,变量不能作为数组的长度,C99合法
	int* p = (int*)malloc(n * sizeof(int));//创建成功后,p类似数组名
	assert(p != NULL);
	if (p == NULL)
	{
		perror("出错了");
		return 0;
	}
	for (int i = 0; i < n; i++)
		p[i] = i;
	for (int i = 0; i < n; i++)
		printf("%d ", p[i]);
	return 0;
}

应用场景2.

错误用法:

如下代码,如果返回值为数组,数组是局部变量,函数结束后,系统自行对变量进行回收,这时return str,返回变量的地址,地址仍存在,并没有销毁,所以在主函数中打印函数的返回值时,打印的是函数传的地址,a返回函数传的地址取字符串,此时字符串为随机值,函数内的数据已经随函数的结束而销毁。

char* Getstr()
{
	char str[] = "hello world";//函数结束后,系统自行对变量进行回收
	return str;//返回变量的地址,地址仍存在,并没有销毁
}

int main()
{
	char* a = Getstr();
	printf("%s", a);//乱码
	return 0;
}

辨析:返回值为普通变量时为什么不用动态内存?

如下代码,在函数中返回的是整型a的具体值,而不是它的地址。访问函数时直接将局部变量x赋值为a的值,不会再跳转到a的地址,再取a的值。

int Fun1() {
	int a = 10;
	return a;//合法,返回的是a的值,而不是地址
}

int main() {
	int x=Fun1();
	printf("%d ", x);
	return 0;
}

 正确写法如下,使用动态内存:

动态内存,由程序员自行销毁,可以用来函数传参,使用完成后再释放。

char* GetStr()
{
	int len = strlen("hello world");
	//char* str = (char*)malloc(len * sizeof(char));//常见错误
	char* str = (char*)malloc((len + 1) * sizeof(char));
	assert(str != NULL);
	strcpy(str, "hello world");
	return str;
}

int main()
{
	char* p = GetStr();
	printf("%s\n", p);
	free(p);
	return 0;
}
应用场景 3( 需要大容量的内存 ):
int main()
{
	//定义1000000长度的int数组
	//int arr[1000000];//不能定义这么大的数组
	//int* arr = (int*)malloc(1000000 * sizeof(int));//ok
	//char* arr = (char*)malloc(1024 * 1024 * 1024);//1G,ok
	char* arr = (char*)malloc(1024 * 1024 * 1020 * 2);//20亿字节,2G失败
	if (arr == NULL)
		perror("出错了");
	assert(arr != NULL);
	printf("好了\n");
	getchar();
	free(arr);
	return 0;
}

5.calloc

申请内存函数 , 把每个元素初始化为 0, 具体参考帮助手册
int main()
{
	int n = 10;
	int* arr = (int*)calloc(n, sizeof(int));
	for (int i = 0; i < n; i++)
		printf("%d ", arr[i]);//输出n个 0
	free(arr);
	return 0;
}

6.realloc

扩大内存函数,具体参考帮助手册 (通常情况,扩容后地址会改变而不是在原来的地址上进行扩容,与操作系统的决策有关)

int main()
{
   char *str;
 
   /* 最初的内存分配 */
   str = (char *) malloc(15);
   strcpy(str, "runoob");
   printf("String = %s,  Address = %p\n", str, str);
 
   /* 重新分配内存 */
   str = (char *) realloc(str, 25);
   strcat(str, ".com");
   printf("String = %s,  Address = %p\n", str, str);
 
   free(str);
   
   return(0);
}

7.free崩溃的原因

7.1引入

根据以往的编程经验,在使用函数传递数组时,形参包括数组的首地址arr数组的长度len,因为传递的是首地址,是一个指针,无法在函数内部根据sizeof(arr)/sizeof(数组类型)求得数组的长度,所以形参必须包括数组的长度。

但是在使用free()释放内存空间时,只传递了数组的首地址,并没有传递数组的长度,因为在申请动态内存时,这段内存的头尾会分别生成标记,标记也占一定的内存,所以不需要传长度信息,但是在操作时如果不小心破坏了这个标记,在释放内存时就会发生错误。

int main() {
	int n = 10;
	int* arr = (int*)malloc(n * sizeof(int));

	arr = (int*)realloc(arr, 2 * n * sizeof(int));//arr接收新的地址
	free(arr);//没有传长度,申请的内存头尾会有标记占一定的内存,不需要传长度信息

	return 0;
}

7.2具体原因

  • 越界
  • 指针移动
  • 重复释放同一段内存
  • 释放不是动态创建的内存

7.2.1越界

越界会损坏所申请空间的结尾标志。

//1.越界
int main()
{
	int n = 10;
	int* arr = (int*)malloc(n);
	assert(arr != NULL);
	for (int i = 0; i < n; i++)
		arr[i] = i;
	for (int i = 0; i < n; i++)
		printf("%d ", arr[i]);
	printf("\n");
	free(arr);
	return 0;
}
int main() {
	int n = 10;
	int* arr = (int*)malloc(n * sizeof(int));
	assert(arr != NULL);
	for (int i = 0; i <= n; i++)//越界,申请空间的损坏结尾标志
		arr[i] = 0;
	free(arr);//程序崩溃
	return 0;
}

7.2.2指针移动

指针为数组首地址,指针移动了,在释放时就找不到所申请空间的头部信息。

//指针移动
int main() {
	int n = 10;
	int* arr = (int*)malloc(10 * sizeof(int));
	for (int i = 0; i < n; i++) {
		*arr = 0;
		arr++;//指针移动了,释放时找不到头部信息
	}
	free(arr);//崩溃
	return 0;
}

7.2.3重复释放同一段内存

//重复释放同一段内存
int main()
{
	int n = 10;
	int* arr = (int*)malloc(n * sizeof(int));
	assert(arr != NULL);
	for (int i = 0; i < n; i++)
	{
		arr[i] = i;
	}
	printf("%p\n", arr);
	free(arr);
	printf("%p\n", arr);//arr是野指针
	free(NULL);//可以
	//free(arr);//崩溃,重复释放
	return 0;
}

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值