动态内存管理

一、为什么要有动态内存分配

目前我们掌握的内存开辟方式,主要是在定义一个变量(如int a=0)的时候由操作系统为我们开辟的空间,但是这样开辟的内存空间是固定的,不可更改的,比如我们在定义一个20个元素的数组时向内存申请了80个字节的空间,那么但我们发现数组的大小太大了或者太小了,想要更改是不可能的,但是动态内存分配而来的空间是可以更改的。要学会动态内存开辟我们就需要学会用来开辟内存的四个函数,malloc,free,calloc,realloc。

二、malloc和free

1.  malloc

<1>malloc是C语言中的一个库函数,它用于向内存申请一块连续可用的空间 (相当于一个数组)

<2>它的返回值为void*,函数参数为需要开辟的字节数

<3>如果malloc成功向内存申请空间,则返回这段空间的起始地址,如果开辟失败,则返回空指针NULL(因此我们在开辟空间后一定要注意检查函数返回的地址是否为NULL);

<4>动态开辟的内存在使用过后要使用库函数free释放

2. free

free的作用是释放动态开辟的内存,它的返回值为void,函数参数为void*,在使用这个函数是我们需要把要释放空间的起始地址传递给free ,需要注意的是,free不能释放由操作系统直接开辟的空间,如:

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int a = 0;
	free(&a);
	//这种方式是错误的
	return 0;
}

下面我们通过一段代码来练习一下,这两个函数:

#include <stdio.h>
#include<stdlib.h>
int main()
{
	int *p=(int*)malloc(sizeof(int)*5);
	if (p == NULL)
		//注意判断malloc的返回值是否为NULL
	{
		perror("malloc");
	}
	else
	{
		for (int i = 0; i < 5; i++)
		{
			*(p + i) = i;
		}
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(p + j));
		}
		//使用malloc后需要把malloc开辟的空间释放
		free(p);
		//释放过后原来的空间不存在了,因此p不需要在指向内存的某个地方,就需要
		//改为空指针,否则p会变更为野指针
		p = NULL;
	}
	return 0;
}

 三、calloc和realloc

1.calloc

calloc与malloc是非常相似的,它们的主要区别有:

<1>函数参数不同,malloc的参数为一共要申请的字节数,而calloc的参数为需要申请x个大小为x个字节的字节数,下面我们通过不同的两个函数来看看它们在参数上的区别:

int* p1 = (int *)malloc(sizeof(int)*5);
int* p2 = (int*)calloc(5, sizeof(int));

<2>calloc申请的空间会自动初始化为0,而malloc申请的空间不会自动初始化

calloc的其它注意事项和malloc相同。

2. realloc

前面我们说到,动态开辟的内存可以调整内存的大小,而函数realloc就是用来调整大小的,可以看到,realloc有两个参数,第一个参数用于接收需要改变大小的内存的首地址,第二个参数用来表示想要改变的字节数(如增大n个字节或者缩小n个字节),realloc也有可能会开辟失败,当内存申请失败是,realloc返回空指针,因此在使用realloc时我们也需要对它的返回值进行判断

我们通过一段代码来看看它是如何使用的:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)calloc(5, sizeof(int));
	if (p == NULL)
	{
		perror("calloc");
	}
	else
	{
		for (int i = 0; i < 5; i++)
		{
			*(p + i) = i + 1;
		}
		printf("改变空间前");
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", *(p + j));
		}
		printf("\n");
		int* ptr = (int *)realloc(p, sizeof(int) * 5);
		//对realloc的返回值进行判断
		if (ptr == NULL)
		{
			perror("realloc");
			//说明开辟失败,则不再使用calloc开辟的空间
			free(p);
			p = NULL;
		}
		else
		{
			p = ptr;
			ptr = NULL;//ptr不再使用,赋值为NULL
			for (int i = 5; i < 10; i++)
			{
				//对后面增加的空间赋值
				*(p + i) = i + 1;
			}
			printf("改变空间后\n");
			for (int j = 0; j < 10; j++)
			{
				printf("%d ", *(p + j));
			}
		}
		//使用后对开辟的空间进行释放
		free(p);
		p = NULL;
	}
	return 0;
}

四、动态内存开辟时的常见错误(以代码形式说明)

1.对NULL指针解引用操作

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	*p = 5;//当malloc返回空指针时程序出错
	return 0;
}

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

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p != NULL)
	{
		for (int i = 0; i <= 5; i++)
		{
			*(p + i) = i;
			//只开辟了5个整形的空间却访问6个整形
		}
	}
	return 0;
}

3.对非动态开辟内存使用free释放

int main()
{
	int arr[10] = { 0 };
	free(arr);
	//arr是由操作系统直接开辟的内存,并非动态开辟,不能使用free进行释放
	return 0;
}

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

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	p++;
	p = 5;
	free(p);
	//由于p++,因此p指向的不再是动态开辟内存的首字节地址
	//因此程序出错
	return 0;
}

5.对同一块动态内存多次释放

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	free(p);
	free(p);
	//多次释放
	return 0;
}

6.动态开辟内存忘记释放

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	//没有释放
	return 0;
}

未释放动态内存会导致内存泄露。

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值