C语言动态内存管理

C语言动态内存管理,第1张

目录

1.为什么存在动态内存管理?

1.1 动态内存管理的函数

1.malloc/free

2.calloc

 3.realloc

1.2 动态内存开辟时常见问题

1.对NULL的解引用 *** 作

2.free释放动态开辟内存的一部分

3.对同一块的内存的重复释放

4.对不是动态开辟内存的空间进行free

5.没有对动态开辟内存的空间free

6.对动态开辟的内存越界访问


 

1.为什么存在动态内存管理?

一般开辟内存空间的时候,像开辟数组、变量时开辟的都是固定的空间

    //常见的开辟内存的方式
    int a = 0;        //向内存申请四个字节的空间,在栈上申请
    int arr[10]={0};  //向内存申请连续的十个int类型的空间,在栈上申请

像上述的方式有两个特点:

        1.空间开辟大小是固定的。


        2.数组在声明时必须指定大小,它所需要的空间在编译时分配。


这些开辟内存的方式是在编译的时候就申请了空间,但有些时候我们在程序运行的时候才知道到底需要多少空间,这时候就只能试试动态内存开辟空间。


1.1 动态内存管理的函数 1.malloc/free

首先认识一下malloc这个函数

这个函数向内存申请一块连续可用的空间,并返回指向这个空间的指针 

特点:

#1.malloc需要一个size来指定创建空间的大小,单位是字节

#2.malloc在申请空间成功时,会返回一个指向申请空间地址的指针

#3.在申请空间失败的时候,会返回一个NULL指针,所以在使用malloc时要进行判空 *** 作

#4.malloc返回的是一个void*的指针,所以在进行开辟空间时要转换成对应的类型

#5.malloc的参数如果是0,那么这种 *** 作是标准未定义的,取决于编译器

在使用malloc的时候,有一件事是必须要做的,那就是内存释放,C语言就很贴心的给我们准备了内存释放的函数free

free的介绍

 这个函数把动态开辟的空间还给 *** 作系统

特点:

#1.如果free的参数指向的不是动态开辟的空间,那么这种 *** 作是未定义的

#2.如果free的参数是NULL,那么free什么也不做

 在使用malloc的时候一定要进行free释放内存,不然会造成内存泄漏,除此之外还要把指针置为NULL,避免野指针的风险。


举个栗子:

   int* ret = (int*)malloc(40);// 申请一个40个字节的动态内存
    if (ret == NULL)            //判断开辟空间有没有成功
    {
        return 1;
    }
    for (int i = 0; i < 10;i++)
    {
        *(ret+i)=i;
    }
    free(ret);                    //使用完毕,进行内存释放
    ret = NULL;                    //将指向的空间置为NULL,避免野指针
2.calloc

 calloc也是一种开辟动态内存空间的函数

特点:

1#.calloc会开辟一个num个size字节的空间

2#.calloc跟malloc唯一的区别就是在返回指针前,将开辟空间的每一个字节初始化为全0

举个栗子:

    int* ret=(int *)calloc(10, sizeof(int));
    if (ret == NULL)                    //进行判空 *** 作
    {
        perror("calloc");               //报错提醒
        return 1;
    }
    free(ret);                          //calloc也是要进行内存释放的
    ret = NULL;                         //避免野指针

可以看到在内存开辟完内存空间内就已经被置为全0了。


 

    使用calloc可以方便的进行初始化,在需要初始化的时候使用calloc是不错的选择,但也不要忘了使用判空 *** 作和内存释放以及指针置为NULL。


 3.realloc

        realloc的出现让动态内存的管理更加的灵活,在我们觉得开辟的空间大了,或者开辟的空间小了,就可以使用realloc来重新定义空间的大小

函数原型:

 特点

#1.ptr是要重新设置的空间的指针

#2.size是要重新设置的字节数

#3.函数会返回一个指针,这个指针指向已经重新分配好了的空间

#4.在改变原有空间的大小的情况下,原有空间的数据会被转移到新开辟的空间

#5.在开辟新空间时,一般会遇到两种情况:

 情况1:原有空间后有足够的空间

 情况2:原有空间后没有足够的空间,需要重新寻找新的空间

 情况1:在原有空间后有足够的空间可以开辟,直接往后开辟。


 情况2:当原有空间后没有空间里,realloc会在堆上寻找一块连续可用的空间来使用,并且把原有的数据转移到新的空间中,并且在转移成功后将原有空间内存释放,然后返回新的空间的地址。


当然realloc也有可能开辟失败,因为空间是有限的,而人可以进行无限的 *** 作,一个人想写bug没有人可以拦着,那怎么办呢,在realloc使用时也判断一下返回的是不是NULL就行了。


所以在使用realloc时,要注意使用的方式

举个栗子:

    int* ret = (int*)malloc(40);//开辟原有内存
    if (ret == NULL)//判空 *** 作
    {
        return 1;
    }
    int* ptr = NULL;
    ptr=(int*)realloc(ret, 80);//在使用realloc时要创建一个临时指针
    if (ptr != NULL)           //因为realloc可能会开辟失败,所以为了数据的安全,在创建失败时就不改变原有空间的地址
    {
        ret = ptr;
    }
    free(ret);
    ret = NULL;
    ptr = NULL;                  //临时变量也不要忘记置为NULL
1.2 动态内存开辟时常见问题 1.对NULL的解引用 *** 作

在使用malloc,calloc,realloc进行开辟时,返回的有可能时NULL,在这个时候对NULL进行解引用的时候就会报错

	int* ret = (int*)malloc(40);//开辟空间
	if (ret == NULL)//判空 *** 作
	{
		return 1;
	}
	free(ret);
	ret = NULL;
2.free释放动态开辟内存的一部分

在使用函数的时候,我们用来存放指向开辟空间的指针如果被改变了,那么这时候再使用free就会报错

	int* ret = (int*)malloc(40);//开辟空间
	if (ret == NULL)//判空 *** 作
	{
		return 1;
	}
	ret++;			//改变了ret,ret不再指向开辟空间的起始地址
	free(ret);
	ret = NULL;

 

 程序直接就会奔溃掉了

3.对同一块的内存的重复释放

再已经free一次指针了之后,再次free一次指针,这样就会报错

	int* ret = (int*)malloc(40);//开辟空间
	if (ret == NULL)//判空 *** 作
	{
		return 1;
	}
	free(ret);
	free(ret);        //重复释放ret,报错
	ret = NULL;
4.对不是动态开辟内存的空间进行free
	int arr[10] = { 0 };
	free(arr);           //对数组进行free,驴头不对马嘴
5.没有对动态开辟内存的空间free
	int* ret = (int*)malloc(40);//开辟空间
	if (ret == NULL)//判空 *** 作
	{
		return 1;
	}

不进行内存释放,大胆!这可是会造成内存泄漏的!!!不要忘记free

6.对动态开辟的内存越界访问

跟数组一样,动态开辟的内存也是有会越界访问的

	int* ptr=(int*)malloc(40);
	if (ptr == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i <= 10; i++)//空间一共是10个int类型的空间
	{
		*(ptr+i) = i;			//在访问第11个元素的时候,就会越界访问
	}
	free(ptr);
	ptr = NULL;

好了到这里就结束了,柔性数组明天出。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/562640.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-02
下一篇 2022-04-02

发表评论

登录后才能评论

评论列表(0条)

保存