舍友打了把王者你就会系列-——内存动态管理(详解)

本文详细介绍了C语言中动态内存管理的必要性,包括malloc、free、calloc和realloc函数的使用示例,以及常见的动态内存错误,如NULL指针处理、越界访问、内存泄漏等,帮助读者更好地理解和实践动态内存管理。
摘要由CSDN通过智能技术生成

前言

给数组分配多大的空间?
你是否和初学C时的我一样,有过这样的疑问。
这一期博客就来聊一聊动态内存的分配
读完这篇文章,你可能对内存的分配有一个更好的理解!

在这里插入图片描述

一、为什么要存在动态内存管理?

在这里插入图片描述

我们知道int val = 20;在栈空间上开辟四个字节。char arr[10] = {0};在栈空间上开辟10个字节的连续空间;但是上述的开辟空间的方式有两个特点:

  1. 空间开辟大小是固定的。
  2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,
那数组的编译时开辟空间的方式就不能满足了。这时候就衍生了动态开辟空间,也就是动态内存管理,我们一起往下看他究竟是如何使用的。

二、 动态内存函数的介绍

2.1动态内存的好处

<1>.可以控制内存的大小
在很多时候,我们申请的空间是未知的,就比如说通讯录的使用就存在一个问题,你定的空间需要多少个字节?当申请空间的太少,就有可能出现满员情况,如果开辟的空间过大,又会造成浪费,很难取到一个合适的值。
在动态内存分配就可以避免这个问题,你可以运用 reallac (下文会介绍)控制大小,当内存达到申请的空间时,就会主动扩容,也就是再次向内存申请空间。
<2> 可以多次利用这部分空间
静态内存分配利用的空间,整个程序结束才会释放给系统,而动态内存分配的空间,只能在函数运行结束后由系统自动释放,需要用户主动去释放,可以在使用结束(就比如说打印元素结束后),用户再通过 free函数释放这块空间,当再次用动态内存申请空间时,就可以再次利用这块空间,这样也能在一定程度上,可以节省一定的空间。
<3>不占用栈区的内存
假设栈区定义了变量,而每个变量分配内存时,之间又有一定的间隙,当定义的变量足够多时,空隙也会很多,这时候向系统申请一个比较大且连续的空间时,虽然有足够的空间,但是缺少了连续的空间。
所以动态内存在堆区申请,就完全不必担心栈区的空间不够的问题

2.2malloc和free以及使用实例

C语言提供了一个动态内存开辟的函数malloc

>void* malloc (size_t size);

功能:

向内存申请一块连续可用的空间(大小为size字节),此存储空间中的初始值不确定

返回值:

若分配成功,则返回一个指向已分配的空间开头的指针;若分配失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。

说明:

1.返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己
来决定。
2.如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,函数原型如下:

void free (void* ptr);

free函数用来释放动态开辟的内存。

如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
如果参数 ptr 是NULL指针,则函数什么事都不做。

注:malloc和free都声明在 stdlib.h 头文件中。
动态开辟空间实例:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include<stdlib.h>

int main()
{
    int num = 0;
    scanf("%d", &num);
    int* ptr = NULL;
    ptr = (int*)malloc(num * sizeof(int));
    if (NULL != ptr)//判断ptr指针是否为空
    {
        int i = 0;
        for (i = 0; i < num; i++)
        {
            *(ptr + i) = 0;
        }
    }
    free(ptr);//释放ptr所指向的动态内存
    ptr = NULL;//防止野指针问题出现
    return 0;
}

2.3calloc函数及使用实例

C语言还提供了一个函数叫 calloccalloc 函数也用来动态内存分配。原型如下:

void* calloc (size_t num, size_t size);

这个函数我们不做详细介绍(与malloc高度相似),我们在此只说明malloc与calloc函数的区别

calloc函数开辟的空间,会将空间的内容全部初始化为零,而malloc函数向系统申请的空间,空间的值都是随机的

根据calloc的这一特性,可以很方便的开辟一些需要初始化的数据
我们看个实例:

#include <stdio.h>
#include <stdlib.h>
int main()
{
 int *p = (int*)calloc(10, sizeof(int));
 if(NULL != p)
 {
 //使用空间
 }
 free(p);
 p = NULL;
 return 0;
}

2.4realloc函数及使用实例

博主认为:有了realloc函数才是真正的动态开辟内存
有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时
候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整,realloc函数的出现让动态内存管理更加灵活。
函数原型如下:

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

功能说明:

先判断当前的指针是否有足够的连续空间,如果有,扩大ptr指向的地址,并且将ptr返回,如果空间不够,先按照size指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来prt所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。

返回值:

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

扩容过大,重新开辟也不够导致的扩容失败,就会导致扩容前申请的空间也发生改变,所以不能直接用ptr来重新赋值,防止先前的数据丢失

我们来看实例:

#include<stdlib.h>
#include<stdio.h>

int main()
{
    int* a; int i = 0;
    a = (int*)calloc(10, sizeof(int));
    if (a == NULL)
        printf("分配失败");
    else
    {
        for (i = 0; i < 10; i++)
        {
            *(a + i) = i;
            printf("*a=%d\n", *(a + i));
        }
        //需要扩容
        int* ret = realloc(a, 80);
        if (ret != NULL)
        {
            a = ret;
        }
        free(a);
        a = NULL;
    }
}

三、3. 常见的动态内存错误

3.1对NULL指针的解引用操作

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

3.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.3对非动态开辟内存使用free释放

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

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

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

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

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

3.6动态开辟内存忘记释放(内存泄漏)

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

忘记释放不再使用的动态开辟的空间会造成内存泄漏
注:动态开辟的空间一定要释放,并且正确释放 。


欢迎点赞收藏加关注,如若有问题可以评论区留言或者私聊博主呦!
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值