动态内存管理详细讲解

1.为什么存在动态内存分配

int main()
{
    
    
	int a = 0;
	char arr[] = "abcde";
	return 0;
}
  • 以上空间开辟的特点:
  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配
  • 而有时候我们我们需要的空间大小在程序运行起来才能知道,这时候就需要动态开辟的内存了

2.动态内存开辟的函数

1.malloc

在这里插入图片描述

  • 由于malloc不清楚将来申请的空间的类型是什么,所以不能确定指针类型,那么就返回一个空指针
  • 参数为申请的字节数
  • 我们可以将malloc的返回值强制类型转换成我们想要的指针类型
int main()
{
    
    
	//开辟
	char*p = (cahr*)malloc(10*sizeof(char));
		if (p == NULL)
	{
    
    
		perror("malloc");
		return 1;
	}
	//使用
	
	//释放
	free(p);
	p = NULL;

}

2.calloc

在这里插入图片描述

  • 参数分别为元素个数,每个元素大小
  • 可以将动态开辟的空间初始化为0
int main()
{
    
    
		//开辟
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
    
    
		perror("calloc");
		return 1;
	}
	//使用


	//释放
	free(p);
	p = NULL;

}

在这里插入图片描述

3.realloc

在这里插入图片描述

int main()
{
    
    
	int*p = (int*)malloc(sizeof(int) * 10);
	if (p == NULL)
	{
    
    
		perror("calloc");
		return 1;
	}
	int*str = (int*)realloc(p,sizeof(int)*20);
	if (str == NULL)
	{
    
    
		perror("realloc");
		return 1;
	}
	p = str;

	//使用

	//释放
	free(p);
	p = NULL;
	return 0;
}
  • 当所开辟的空间小了,用realloc来调整动态内存开辟的空间,使其变大
  1. 因为返回值可能为空指针,所以realloc的返回值不能直接用p来接收,需要先进行判断
  • realloc在调整空间大小时有两种情况
  • 情况一
  • malloc申请的空间后面有足够大的空间用来调整空间,realloc会在空间后追加一块新的空间
  • 情况二
    • malloc申请的空间后面没有足够大的空间用来调整空间,realloc寻找一块新的空间供其开辟,并且把原来的数据拷贝到新的内容,返回新的空间的地址
  • 由此得出realloc开辟的空间可能返回旧地址,也可能返回新地址,所以要将realloc返回的指针传给p

4.free

在这里插入图片描述

  • 释放开辟动态内存
  • 当free的参数传的是NULL时,free不会做任何事

3.常见的动态内存错误

1.对NULL解引用

void test()
{
    
    
int *p = (int *)malloc(sizeof(int)*4);
*p = 20;//如果p的值是NULL,就会有问题
free(p);
}

2.对动态内存开辟空间的越界访问

void test()
{
    
    
int i = 0;
int *p = (int *)malloc(10*sizeof(int));
if(NULL == p)
{
    
    
	return;
}
for(i=0; i<=10; i++)
{
    
    
*(p+i) = i;//当i是10的时候越界访问
}
free(p);
}

3.对非动态内存开辟的空间进行free

void test()
{
    
    
int a = 10;
int *p = &a;
free(p);
}

4.使用free释放动态内存的一部分

void test()
{
    
    
int *p = (int *)malloc(10);
p++;
free(p);//p不再指向动态内存的起始位置
}

5.对同一块空间进行多次释放

void test()
{
    
    
int *p = (int *)malloc(100);
free(p);
free(p);//重复释放
}

6.动态内存开辟忘记释放(内存泄漏)

void test()
{
    
    
int *p = (int *)malloc(100);
if(NULL != p)
{
    
    
*p = 10;
}
}
int main()
{
    
    
test();
return 0;
}
  • 此时p从函数出来时被回收,也就没人记得该内容的地址了,导致内存泄漏

4. 柔性数组

struct s
{
    
    
	int a;
	int arr[];//int arr[0]
	//以上两者arr均为柔性数组
};
  • 柔性数组的特点
  1. 结构体中柔性数组前面必须至少有一个成员,否则不知道结构体大小多大,不能为它开辟空间
  2. sizeof计算结构体大小不包括柔性数组大小
  3. 包含柔性数组的结构体要用malloc动态开辟内存空间,并且要大于结构体大小来适应柔性数组的预期大小

1. 使用柔性数组动态内存开辟与不使用柔性数组动态内存开辟

  • 使用柔性数组
struct s
{
    
    
	int a;
	int arr[];
};
int main()
{
    
    
	struct s*p = (struct s*)malloc(sizeof(struct s)+sizeof(int)*10);
	if (p == NULL)
	{
    
    
		perror("malloc");
		return 1;
	}
	p->a = 100;
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		*(p->arr+i) = i;
	}
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(p->arr+i));
	}
	printf("\n");
	struct s* str = (struct s*)realloc(p, sizeof(struct s) + sizeof(int) * 20);
	if (str == NULL)
	{
    
    
		perror("str");
		return 1;
	}
	p = str;
	for (i = 10; i < 20; i++)
	{
    
    
		*(p->arr + i) = i;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		printf("%d ", *(p->arr + i));
	}
	free(p);
	p = NULL;
	return 0;

}
  • 不使用柔性数组
struct s
{
    
    
	int a;
	int *arr;
};
int main()
{
    
    
	struct s* p = (struct s*)malloc(sizeof(struct s));
	int*pc = (int*)malloc(sizeof(int) * 10);
	if (p == NULL)
	{
    
    
		perror("p:malloc");
		return 1;
	}
	if (pc == NULL)
	{
    
    
		perror("pc:malloc");
		return 1;
	}
	
	p ->arr = pc;
	
	int i = 0;
	for (i = 0; i < 10; i++)
	{
    
    
		*(p->arr + i) = i;
	}
	for (i = 0; i < 10; i++)
	{
    
    
		printf("%d ", *(p->arr + i));
	}
	int*str = (int*)realloc(p->arr, 20 * sizeof(int));
	if (str == NULL)
	{
    
    
		return 1;
	}
	p->arr = str;
	
	printf("\n");
	for (i = 10; i < 20; i++)
	{
    
    
		*(p->arr + i) = i;
	}
	for (i = 0; i < 20; i++)
	{
    
    
		printf("%d ", *(p->arr + i));
	}

	//释放
	free(p->arr);
	p->arr = NULL;
	free(p);
	p = NULL;
	return 0;
}

2.使用柔性数组的优点

  • 使用柔性数组只是用了一次malloc,free,不使用柔性数组使用了两次malloc,free
  • 在堆区上进行动态内存开辟时,开辟的空间并不是一块紧接着一块,它们之间的空隙成为内存碎片,由于内存碎片所占空间较小,被使用的机会也会很少,这就导致了空间的浪费,内存使用率下降,所以要尽量少的使用malloc
  • 使用更多次的malloc,free,空间维护难度加大,更容易出错

猜你喜欢

转载自blog.csdn.net/wan__xia/article/details/129896613
今日推荐