动态内存管理

为什么要有动态内存分配

在之前我们已经掌握了内存开辟的方式

int a = 0;//在栈空间开辟四字节的空间
char s[20] = { 0 };//在栈空间开辟连续的20字节空间

但是我们通过上述方法开辟的空间是固定的,数组的空间定义后也是不能更改的

C语言中就引入了动态内存开辟,让我们自己申请空间,就相对灵活了

四个函数

malloc

头文件:stdlib.h

void* malloc (size_t size);

这个函数用于在堆区内存申请一块连续的空间(相当于数组),如果申请成功返回空间的起始地址,申请失败返回空指针(使用时要判断),返回类型是void *使用时自行定义返回值。如果size的值为0,结果未定义,由编译器决定

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p1 = malloc(20);
	if (p1 == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 5; i++)
	{
		*(p1+i)= i;
		printf("%d ", *(p1 + i));
	}
	return 0;
}

输出结果

free

void free (void* ptr);

专门用于释放和回收动态内存,本质是将空间的权限还给操作系统,失去对这块内存的使用权,并不是自动变成空指针。操作后 ptr 变成野指针,需要置空指针。

free(p1)
p1 = NULL

参数指向的空间必须是起始地址并且是动态开辟的,如果是空指针则什么都不做。

calloc

void* calloc (size_t num, size_t size);

用于开辟num个size字节的空间,并初始化为0,除此之外与malloc没有区别

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p1 = calloc(5,sizeof(int));
	if (p1 == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", *(p1 + i));
	}
	return 0;
}

输出结果

realloc

 void* realloc (void* ptr, size_t size);

用于对内存大小灵活调整,调整ptr指向的内存为size字节,并返回起始地址。

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p1 = calloc(1,sizeof(int));
	if (p1 == NULL)
	{
		perror("calloc");
		return 1;
	}
 int* p3= (int*)realloc(p1, 40);
	for (int i = 0; i < 5; i++)
	{
		*(p3 + i) = i;
		printf("%d ", *(p3 + i));
	}
	return 0;
}

如果p1后面的空间大于等于40字节则正常开辟,并返回p1的地址,如果空间小于40字节则在内存中选取另一块40字节的空间,作为开辟的空间,并将p1原来的数据复制过来然后返回新开辟空间的地址

realloc也可以完成malloc的功能

realloc(NULL,20)=malloc(20);

常见动态内存错误

1 开辟空间后没有判断,容易对空指针解引用

void test()
{
	int* p = (int*)malloc(INT_MAX / 4);
	*p = 20;
	free(p);
}

2 动态内存越界访问

	void test()
	{
		int i = 0;
		int* p = (int*)malloc(10 * sizeof(int));
		if (NULL == p)
		{
			exit(EXIT_FAILURE);
		}
		for (i = 0; i <= 10; i++)
		{
		 * (p + i) = i;
		}
		free(p);
	}

3 对非动态内存的free释放

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

4 free释放空间的时候没有指向空间的起始位置

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

5 对同一块内存的多次free释放(释放一次内存后必须置空指针)

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

6 在局部变量动态开辟的内存没有使用free释放,这个空间将无法使用(malloc calloc realloc开辟的内存空间只有在程序结束时才会回收),发送内存泄漏

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

尽量做到

1 谁申请的空间谁释放,如果不能释放,应该告诉使用的人,记得释放。

2 释放空间之后,紧接着置空指针

动态内存经典笔试题解析

第一题

下面程序运行会产生什么现象

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char* p)
{
	p = (char*)malloc(100);
}
void Test(void)
{
	char* str = NULL;
	GetMemory(str);
	strcpy(str, "hello world");
	printf(str);
}
int main()
{
	Test();
	return 0;
}

结果

程序崩溃

原因

void GetMemory(char* p)
{
	p = (char*)malloc(100);  //形参p获得malloc开辟的100字节空间的起始地址
}
char* str = NULL;
GetMemory(str); //退出函数后,形参p销毁,无法将地址传给实参str,str还是空指针

修改

如果在函数中想修改实参,那么应该传递实参的地址,通过对地址的解引用来修改实参

void GetMemory(char** p)
{
	*p = (char*)malloc(100); 
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str); 
	strcpy(str, "hello world");
	printf(str);
    free(str);
    str = NULL;
}
int main()
{
	Test();
	return 0;
}

或者函数返回p的地址,这里不做过多解释

第二题

下面程序运行会产生什么现象

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

结果

程序不可控,是否输出不确定

原因

退出函数后,函数空间被销毁,也就是说,p所指向的空间被操作系统回收,然而函数返回了p的地址,这时候p就是空指针。如果内存没被覆盖就可以成功打印,如果被覆盖那么打印随机值或者程序崩溃

柔性数组

C99,结构体的最后一个成员可以是一个未知大小的数组,这个数组就是柔性数组

柔性数组的特点

柔性数组前面必须有一个或多个成员,结构体大小不包括柔性数组,sizeof访问的时候也不返回柔性数组的大小。用malloc函数进行分配内存,大小应该大于结构体的大小。也可以用realloc调整大小

举个例子

#include<stdio.h>
#include<stdlib.h>
struct S
{
	int n;
	int a[];
};
int main()
{
	struct S* s1 = { 0 };
	 s1 = malloc(sizeof(s1) + 5 * sizeof(int));
	if (s1 == NULL)
	{
		perror("malloc");
		return 1;
    }
	else
	{
		for (int i = 0; i < 5; i++)
		{
			s1->a[i] = i;
			printf("%d ", s1->a[i]);
		}
	}
	//调整空间
	struct S* str = realloc(s1, 1000);
	if (str != NULL)
	{
		s1 = str;
	}
	free(str);
	str = NULL;
	return 0;
}

模拟实现柔性数组

struct S
{
	int a;
	int* arr;
};
int main()
{
	struct S* s1 = (struct S*)malloc(sizeof(struct S)); 
	//struct S* s1 = (struct S*)malloc(sizeof(s1));
	if (s1 == NULL)
		return 1;
	int* p = (int*)malloc(20);
	if (p == NULL)
		return 2;
	s1->arr = p;
	for (int i = 0; i < 4; i++)
	{
		s1->arr[i] = i + 1;
		printf("%d ", s1->arr[i]);
	}
	//调整空间
	int* p1 = realloc(s1->arr, 10 * sizeof(int));
	if (p1 == NULL)
		return 3;
	s1->arr = p1;
	free(s1->arr);
	s1->arr = NULL;

	free(s1);
	s1 = NULL;
	return 0;
}

总结C/C++中内存划分

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值