动态内存分配的相关函数

本文详细介绍了C语言中动态内存管理的关键函数,包括malloc()、free()、calloc()和realloc()的使用方法及注意事项。通过示例代码展示了如何申请、释放和调整内存,同时列举并解析了常见的动态内存错误,帮助读者理解和避免内存泄漏等问题。
摘要由CSDN通过智能技术生成

目录

一、前言

二、malloc和free

1、malloc()函数

2、free()函数

三、calloc

四、realloc函数

五、常见的动态内存错误


一、前言

为什么需要动态内存分配?

当我们创建数组时,如:

int val=20;
int arr[20]={0};

数组开辟的内存是固定的,而实际过程中,有时候我们是无法预知程序所需要的空间大小,内存要是开辟大了,浪费空间,开辟小了,程序出错。这就需要一种方法来灵活控制开辟空间的大小,在这种时候,我们就需要用到动态内存。

二、malloc和free

1、malloc()函数

malloc是c语言用来开辟动态内存的函数,这个函数向内存申请了一块连续可用的空间,并返回指向这块空间的起始地址。

void* malloc(size_t size);

1、如果开辟成功,则返回一个指向开辟好空间的指针。
2、如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
3、返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定;参数size的类型是无符号整型,表示需要申请多少个字节的空间。

 使用示例:

//申请40个字节的空间
int* p = (int*)malloc(40);

2、free()函数

free()函数是搭配malloc()函数使用的,用来释放malloc申请的内存空间。如果不对malloc函数申请的空间进行释放,那么这块空间只有到整个程序结束时才释放,如果这个程序永远不会停止,那么这块空间就无法被其他程序使用,造成内存泄漏问题(即空间浪费问题)。

void free(void* ptr)

如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的。

如果参数 ptr是NULL指针,则函数什么事都不做。

参数ptr是指向的动态内存的指针

 下面给出一个malloc与free的完整例子:

int main()
{
	//int arr[10] = {0};
	//申请空间
	int* p = (int*)malloc(40);
	if (p == NULL)
	{
		return -1;
	}
	//开辟成功了
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//释放空间
	free(p);
	p = NULL;
	
	return 0;
}

三、calloc

calloc函数也是用来申请动态内存的,原型如下:

void* calloc(size_t num,size_t size);

1、函数的功能是开辟num个元素,大小为size个字节的空间,并且把空间的每个字节初始化为0。

2、这个函数与malloc大体一致,唯一的区别就是会将每个字节初始化为0.

 下面给出一个例子:

int main()
{
	//申请10个int的空间
	int*p = (int*)calloc(10, sizeof(int));
	
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));//这句代码的作用是打印错误信息。
		return -1;
	}
	
	//申请成功
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	//空间不够了,增加空间至20 个int
	int*ptr = (int*)realloc(p, 20*sizeof(int));
	if (ptr != NULL)
	{
		p = ptr;
	}
	else
	{
        //申请失败
		return -1;
	}
	for (i = 10; i < 20; i++)
	{
		*(p + i) = i;
	}
	//打印
	for (i = 0; i < 20; i++)
	{
		printf("%d ", *(p + i));
	}

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

	return 0;
}

四、realloc函数

当我们申请了一块动态内存,使用的过程中,发现申请的这块内存太大或者太小了,这个时候,我们就需要运用realloc函数来调整这块空间的大小,这个函数的原型如下:

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

ptr是要调整的内存地址.

size调整之后新大小(单位为字节)

返回值为调整之后的内存起始位置。

这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。realloc在调整内存空间的是存在两种情况∶

情况1︰原有空间的后面有足够大的空间,能够满足size个字节,就直接在旧空间的后面开辟。例如,我原本开辟了20个字节的空间,现在我需要40个字节的空间,如果旧空间的后面有20个字节的空间,那这个函数就直接在后面开辟20个空间,这样在加上原本的20个字节空间的地址,刚好满足40个空间,然后返回这块空间的起始地址,如果开辟失败,返回NULL。

情况2:原有空间的后面没有足够大的空间,不能够满足size个字节,那么就得新找一块size个空间来开辟。例如,我原本开辟了20个字节的空间,现在我需要40个字节的空间,如果旧空间的后面只有10个字节的空间,这样我就得重新找一块40个字节空间来开辟,其中原本的旧空间的20个地址会被释放,且这20个空间的内容会被复制到新空间的前20个字节上,最后返回这块新空间的起始地址,如果开辟失败,返回NULL。

代码示例:

int main()
{
 int *ptr = malloc(100);
 if(ptr != NULL)
 {
     //开辟成功,业务处理
 }
 else
 {
    //开辟失败,退出程序
     exit(EXIT_FAILURE);    
 }
 //扩展容量

 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)
 {
 ptr = p;
 }
 //业务处理
 free(ptr);
 return 0;
}

注意:我们不能直接用ptr=realloc(ptr,1000);而是创建一个新的指针变量p来接收新开辟的空间的起始地址,如果开辟成功才让ptr来接收p的地址,因为如果ptr=realloc(ptr,1000),当开辟失败,realloc会返回NULL,ptr就会等于NULL,这样的话,我们不但没有开辟到新的空间,还把原来的旧空间给”弄丢了“

五、常见的动态内存错误

1、对NULL指针的解引用操作

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

当开辟失败时,p=NULL,就不能进行*p=20这个操作。

正确做法是:

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

 if(p==NULL)
{
    return -1;
}
 else
{
     *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;//当i是10的时候越界访问
 }
 free(p);
}

我们只开辟了10个int型字节的空间,现在却要访问11个int型字节的空间,这样就越界访问了,这显然是不行的。

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

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

a不是动态开辟的空间,不能使用free!!free只能用来释放动态开辟的空间。

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

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

p++之后,指向了下一个int型数据的地址,即跳过了4个字节,如果此时释放,就还有4个字节的空间没有被释放,造成内存泄漏。

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

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

6、忘记free,造成内存泄漏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值