C语言 动态内存管理

1.函数介绍

1.1malloc free

在这里插入图片描述
malloc会在堆中开辟一块连续的空间,空间大小是size,以字节为单位。开辟成功返回一个指向开辟好的空间的指针,开辟失败返回NULL指针,所以在使用malloc函数后一定要进行检查。malloc函数返回的是void*的空指针,所以在使用时需要将其强转成对应类型。

在这里插入图片描述
free函数能释放动态开辟过的内存,函数参数就是指向动态开辟空间首地址的指针。如果指针为NULL,free函数什么都不做,free函数指向的空间不是动态开辟的,程序会出错。

使用模式

void test()
{
	int* p = (int*)malloc(40);
	if (p == NULL)//判断指针是否为空
	{
		printf("%s", strerror(errno));//打印错误信息并且退出程序
		return;
	}
	//使用
	free(p);//使用后将p指向的空间释放
	p = NULL;//这是p是野指针,将p置为NULL
}

malloc和free是一对函数,使用了malloc函数一定要使用free函数释放内存。

1.2calloc

在这里插入图片描述
calloc和malloc一样是开辟动态内存的函数,不一样的是calloc会将开辟空间初始化为0。参数中的num是要开辟的元素个数,size是一个元素的字节数。返回类型也是void*,使用时要注意强转类型。

void test1()
{
	int* p = (int*)calloc(4, 4);
	if(p == NULL)
	{
		printf("%s", strerror(errno));
		return;
	}
	free(p);
	p = NULL;
}

1.3realloc

在这里插入图片描述
使用realloc函数调整动态内存空间的大小,memblock是指向一块动态内存的指针,size是调整后的空间大小,以字节为单位。如果调整空间失败,函数返回NULL。在函数调整空间成功的情况下,有两种情况

  1. 原有空间后有足够空间,函数返回的指针与原来相同
  2. 原有空间后没有足够空间,函数会找到一块足够大的空间,返回的指针与原来不同
    使用
void test1()
{
	int* p = (int*)calloc(4, 4);
	if(p == NULL)
	{
		printf("%s", strerror(errno));
		return;
	}
	p = (int*)realloc(p, 80);
	free(p);
	p = NULL;
}

2.常见错误

2.1对NULL指针解引用

int main()
{
	int* p = (int*)malloc(INT_MAX);
	*p = 1;
	free(p);
	p = NULL;
	rturn 0;
}

没有检查malloc返回的指针是否为空,INT_MAX是一个二十几亿的数字,malloc函数开辟失败返回空指针,p为空指针,对p解引用是非法的。

2.2对动态开辟的空间越界访问

int main()
{
	int* p = (int*)malloc(10 * 4);
	if (p == NULL)
		return 0;
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		*(p + i) = i;
	}
	free(p);
	p = NULL;
	return 0;
}

i为10的时候,p + i指向第11个整形,而malloc只开辟了10个整形大小的空间,*(p + i)属于越界访问。当使用动态开辟的空间时,注意开辟空间的大小,小心越界。

2.3对非动态开辟空间free释放

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

p指向a,不是动态内存开辟的空间,使用free释放p会导致错误

2.4free释放动态开辟空间的一部分内容

void test()
{
	int* p = (int*)malloc(40);
	p++;
	free(p);
}

当p不指向动态开辟空间的起始位置时,使用free释放会导致程序错误。在动态开辟内存时,一定要有一个指针始终指向空间的起始位置。

2.5对同一块空间多次释放

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

2.6动态开辟空间忘记释放

void test()
{
	 int *p = (int *)malloc(100);
	 if(NULL != p)
	 *p = 20;
}

当忘记释放动态开辟空间时,会导致内存的浪费,称作内存泄漏。当使用了malloc时一定要对应使用free。

3.试题分析

3.1第一题

void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}

问:Test函数有什么问题?
两个

  1. 调用GetMemory函数时传递的是值,并没用传递地址,因此str没有指向动态内存开辟的空间,而是一个空指针,将"hello world"的内容拷贝到str中是错误的。
  2. p所指向的动态内存没有释放,导致内存泄漏。

3.2第二题

char* GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();
	printf(str);
}

打印结果乱码。

GetMemory函数返回栈空间变量的地址,这是一个严重的错误,GetMemory函数执行完创建的局部变量销毁,p指针指向的hello world同样销毁,此时p为一个野指针,这时不能再使用p指针。

3.3第三题


void GetMemory(char** p, int num)
{
	*p = (char*)malloc(num);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str, 100);
	strcpy(str, "hello");
	printf(str);
}

与第一题相同,传址调用改变了str的内容,此时str指向一块动态开辟的空间,但是没有free释放,导论了内存泄漏。

3.4第四题

void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}

释放内存之后继续使用原有空间。

总结,在开辟动态内存后一定不能忘了free释放掉内存并置空指针,同时也不能继续使用之前的内存。

4.柔性数组

结构体的最后一个成员为柔性数组,sizeof该结构体得到的是除了柔性数组其他成员的大小,除了柔性数组以外至少还应该有一个成员。目前我还不知道应用场景,先放一个使用
两种写法,一种在结构体中创建arr[]或arr[0]数组,一种创建指针

struct S1
{
	int i;
	int arr[];
};
struct S2
{
	int i;
	int* p;
};

int main()
{
	struct S1* p = (struct S1*)malloc(sizeof(struct S1) + 80);
	p->i = 0;
	int i = 0;
	if (p != NULL)
	for (i = 0; i < 10; i++)
	{
		p->arr[i] = i;
	}
	free(p);
	p = NULL;


	struct S2* ps = (struct S2*)malloc(sizeof(struct S2));
	if (ps != NULL)
	{
		ps->p = (int*)malloc(100);
		if (ps->p != NULL)
		{
			//使用
		}
		free(ps->p);
		ps->p = NULL;
		free(ps);
		ps = NULL;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值