C语言动态内存分配详解(malloc/calloc/free/realloc)以及常见错误

目录

一:内存分配的概念

1.什么是内存分配?

2.两种内存分配方式

二:动态内存函数详解

       C语言中,内存的动态分配是通过系统提供的库函数来实现的,主要有malloc、calloc、realloc和 free 函数,都需要包含头文件stdlib.h。

1.malloc函数

2.free函数

3.calloc函数

4.realloc函数

三:动态内存分配中常见的错误

1.对NULL指针的解引用操作:

2.对动态分配的内存进行越界访问:

3.对非动态分配内存进行free释放:

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

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

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


一:内存分配的概念

1.什么是内存分配?

       当我们在计算机上打开一个程序,或者创建一个文件时,计算机需要为其分配一定的内存空间来存储程序代码和数据。内存分配是指计算机系统将可用的内存空间分配给不同的程序或文件,以让它们能够在计算机上运行或存储。
       内存分配是计算机操作系统中的一项重要工作,它涉及到内存的申请、分配、使用和释放等一系列操作。不同的程序或文件需要的内存空间大小不同,因此内存分配需要按需分配,以充分利用计算机的内存资源。

2.两种内存分配方式

       静态内存分配:静态内存分配一般用于程序运行中内存需求不会变化的情况,静态的内存使用的是栈空间内存,不用程序员自己来分配。静态变量占用的存储空间对于编译器而言是可预计的,静态内存只需要编程的时候直接声明,数组就是一种静态内存分配方式。

       动态内存分配:在c语言中,编写程序有时不能确定数组应该定义为多大,因此这时在程序运行时要根据需要从系统中动态地获得内存空间。所谓动态内存分配,就是指在程序执行的过程中动态地分配或者回收存储空间的分配内存的方法。动态内存分配不像数组静态内存分配方法那样需要预先分配存储空间,而是由系统根据程序的需要即时分配,且分配的大小就是程序要求的大小。

二:动态内存函数详解

       C语言中,内存的动态分配是通过系统提供的库函数来实现的,主要有malloc、calloc、realloc和 free 函数,都需要包含头文件stdlib.h。

注:动态内存分配是在栈上进行的。

1.malloc函数

malloc函数原型为void *malloc(unsigned int size);

其作用是在内存的动态存储区中分配一个长度为size的连续空间。此函数的返回值是分配区域的起始地址,在这里写void*是因为系统并不知道开辟的空间的类型,需要程序员在使用的时候自己定义开辟空间的类型,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置。

如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。

代码实例:

#include<stdio.h>
#include<stdlib.h>
/*malloc函数练习
int main()
{
	int* p = (int*)malloc(40);//开辟一块40字节连续的空间
	if (p == NULL)//判断是否开辟成功
	{
		perror("malloc");//没有则报错并中止运行
		return 1;
	}

2.free函数

在C语言中,我们可以通过malloc、calloc等函数动态地分配内存空间,但是这些函数只是将空闲的内存块分配给我们,并没有真正地将这块内存块从系统中移除,当我们不再需要这块内存时,我们需要使用free函数来释放这块内存,以便系统可以重新利用这块内存。

因此C语⾔提供了另外⼀个函数free,专⻔是⽤来做动态内存的释放和回收的,函数原型如下:

void free (void* ptr);

其中ptr是指向要释放的内存空间的指针。如果free函数接收的指针是NULL,则函数不会执行任何操作。当使用malloc、calloc或realloc分配内存后,应该使用free来释放这些内存,以避免内存泄漏。

       内存泄漏是指程序在动态分配内存后未能释放这些内存,那么这些内存便会一直占用着空间。需要注意的是,对非动态分配的内存或已经释放的内存使用free函数会导致未定义的行为。此外,free函数并不会将指针设置为NULL,它仅仅释放了指针指向的内存空间。这意味着,释放内存后,指针仍然存在,但指向的内存已经被操作系统收回,此时的指针是野指针,需要重置为空指针。

代码示例:

*malloc函数练习
int main()
{
	int* p = (int*)malloc(40);//开辟一块40字节连续的空间
	if (p == NULL)//判断是否开辟成功
	{
		perror("malloc");//没有则报错并中止运行
		return 1;
	}
	//成功开辟则对空间进行赋值
		for (int i = 0; i < 10; i++)
		{
			*ptr++ = i;
			prinf("%d ", *(ptr-1));
		}
		free(p);//进行动态内存的释放
		p = NULL;//释放空间后p变为野指针,对野指针赋NULL
	return 0;

3.calloc函数

函数原型:void* calloc(unsigned int num,unsigned int size);

功能:在内存的动态存储区中分配num个长度为size的连续空间;

num:对象个数;size:对象占据的内存字节数;

注:相较于malloc函数,calloc函数会自动将内存初始化为0;

int main()
{
	char* p = (char*)calloc(5, sizeof(char));//开辟一块5个char字节大小的空间,并且全部赋初始值0
	if (p== NULL)//判断是否开辟成功
	{
		perror("calloc");
		return 1;//没有则报错并且程序终止运行
	}
	for (int i = 0; i < 5; i++)//在内存中输入数据并且打印出来
	{
		*p++ = i;
		printf("%d ", *(p-1));
	}
	free(p);//释放掉动态开辟的内存空间
	p = NULL;//将p置为空指针

	return 0;
}

4.realloc函数

realloc函数的出现让动态内存管理更加灵活。有时会我们发现过去申请的空间太小了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理地使用内存,我们⼀定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。

realloc函数原型: void *realloc(void *mem_address, unsigned int newsize);

• ptr 是要调整的内存地址

• size 调整之后新大小 

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

• 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

注意: realloc在调整内存空间的是存在两种情况:

情况1:原有空间之后有足够大的空间情况     情况2:原有空间之后没有足够大的空间

当是情况1的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。

当是情况2的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找⼀个合适大小的连续空间来使用。这样函数返回的是⼀个新的内存地址。

因此,在realloc函数的使用过程中要更注意一些。

代码示例:

#include<stdio.h>
#include<stdlib.h>
int main()
{
	int* p = (int*)malloc(20);
	int* a = p;//提前将空间的初始地址赋给a,为后面释放旧空间做准备
	if (a== NULL)//判断
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 5; i++)//给内存空间存入数据
	{
		*(a + i) = i++;
	}
    int*ptr=(int*)realloc(a, 40);//将a所指空间调整为40个字节大小并把新的内存地址返回给ptr
	if (ptr == NULL)//判断
	{
		perror("realloc");
		return 1;
	}
	a= ptr;//开辟成功将新开辟的空间的初始地址赋给旧的空间的初始地址
	for (int i = 0; i < 10; i++)//赋值
	{
		*(ptr+i) = i;
		printf("%d ",*(ptr+i));
	}
		free(p);//释放掉旧的初始空间
		p= NULL;
	return 0;
}

三:动态内存分配中常见的错误

1.对NULL指针的解引用操作:

void test()
 {
 int *p = (int *)malloc(INT_MAX/4);
 *p = 20;//如果p的值是NULL,就会有问题 
 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);

 因为动态开辟的空间后面的空间不一定被开辟,因此,不一定读到数据。

3.对非动态分配内存进行free释放:

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

free函数只可以释放动态开辟的内存。

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

void test()
 {
 int *p = (int *)malloc(100);
 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 = 20;
 }
 }
 
 int main()
 {
 test();
 while(1);
 }

动态内存使用后不进行free会导致内存泄漏,这些内存会一直占用着空间,影响程序的效率。

完结撒花。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值