• 动态内存管理


    动态内存管理

    内存空间如果简单划分的话,分为3个区:栈区,堆区,静态区

    栈区主要是存放:局部变量,函数的形参,函数的返回值。

    堆区主要是存放:malloc calloc realloc free动态开辟的内存空间。

    静态区主要是存放:由static修饰的静态的变量,包括局部变量和全局变量。

    内存开辟的方式

    1. 在栈区开辟空间:
    int i = 0;//在栈上开辟一块大小为4byte的空间
    float f = 0.0f;//在栈上开辟一块大小为4byte的空间
    double d = 0.0;//在栈上开辟一块大小为8byte的空间
    int arr[10] = { 0 };//在栈上连续开辟一块大小为40byte空间
    
    • 1
    • 2
    • 3
    • 4

    上述开辟空间的大小都是固定的,开辟好之后就不能被修改。

    1. 在堆区开辟空间:

    这时候,就涉及到动态内存函数malloc calloc realloc free

    malloc

    void* malloc(size_t size);

    malloc函数是用来向内存申请一块空间的,要是申请成功,则返回该空间的起始地址;申请失败,返回空指针NULL.

    几点注意事项:

    1. 要是申请成功,该块空间并未初始化。
    2. 如果size是0,标准未定义该行为,结果取决于不同的编译器。
    3. void*类型的指针不能直接进行解引用操作,得先将其转化为具体类型的指针。

    free

    void free(void* ptr);

    free函数是用来释放动态内存函数开辟的内存空间的,因为只开辟,不释放,会使得内存泄漏。

    几点注意事项:

    1. ptr指针只能是指向的空间是malloc calloc realloc函数开辟的。
    2. 如果ptrNULL,则free函数什么也不干。
    3. 不能对一块由动态内存函数开辟的空间多次用free释放。

    calloc

    void* calloc(size_t num, size_t size);

    malloc函数类似,calloc函数也是用来开辟内存空间的,不过于malloc不同的是,calloc函数是用来开辟num个大小为size的连续空间的。

    需要注意的是:

    1. calloc同样是返回泛型指针void*,不能直接对其进行解引用操作。
    2. calloc函数开辟空间成功,返回的是空间的起始地址;开辟失败返回的是空指针NULL
    3. calloc在开辟空间的时候,顺便将空间的每个字节全部初始化为0。

    realloc

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

    realloc是用来调整由动态内存函数开辟空间的大小的函数。

    需要注意的点是:

    1. ptr指向的是由动态内存函数开辟空间的起始地址;调整之后,新内存空间的大小是size
    2. 如果调整空间成功,返回调整空间的地址;要是失败,返回空指针NULL
    3. 如果给定的size大小是0,则该函数跟malloc一样,啥事也不干。
    4. realloc返回的地址可能是原来的地址,也有可能是经过调整之后新的地址。

    realloc的两种开辟方式:
    在这里插入图片描述

    动态内存函数的使用

    1. calloc和free
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(40);//动态开辟40个字节的空间,并将起始位置的地址赋值给整型指针ptr
        //因为开辟可能成功,也有可能失败,故在使用ptr指针时,需要先判断指针是否为空
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));//打印错误信息
            return 1;
        }
        
        //...
        //动态内存空间的使用
        
        //动态内存空间的释放
        free(ptr);//释放只是释放了指针指向的空间,但是ptr还是指向原来的那块空间,为了确保安全,需要把ptr置为NULL
        ptr = NULL;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. malloc和free
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = malloc(sizeof(int), 10);//动态开辟10个整型的空间
        //判断返回值
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        //...
        //相关操作
        
        //内存释放
        free(ptr);
        ptr = NULL;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. realloc和free
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(40);
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        int* p = (int*)free(ptr, 80);
        //判断是否空间是否开辟成功
        if (p != NULL)
        {
            ptr = p;
            
        }
        //...
        //内存释放
        free(ptr);
        ptr = NULL;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    动态内存开辟中有关的错误

    1. NULL进行解引用。
    #include 
    #include 
    
    int main()
    {
        int* p = malloc(10 * sizeof(int));
        *p = 20;
        free(p);
        p = NULL:
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    此代码中,使用malloc动态开辟10个整型的空间,可能开辟失败,返回空指针,如果一个指针没有明确的指向是不能直接进行解引用操作的!

    1. 操作越过动态开辟的空间。
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(10 * sizeof(int));
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        for (int i = 0; i <= 10; i++)
        {
            ptr[i] = i;
            //p[i] <--> *(p + i)
        }
        //内存的释放
        free(ptr);
        ptr = NULL;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. 释放非动态开辟的内存
    #include 
    #inclue <stdlib.h>
    
    int main()
    {
        int a = 0;
        int* ptr = &a;
        //...
        free(ptr);
        ptr = NULL;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    1. 释放动态开辟内存的一部分
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(10 * sizeof(int));
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        for (int i = 0; i < 5; i++)
        {
            ptr++;
        }
        //ptr经过循环之后已经不再指向动态开辟内存的起始位置了。
        
        free(ptr);
        ptr = NULL;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    1. 对同一块动态开辟的内存多次释放
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(10 * sizeof(int));
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        //...
        free(ptr);
        ptr = NULL;
        //...
        free(ptr);
        ptr = NULL;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    1. 忘记释放动态开辟的内存(导致内存泄漏)
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(10 * sizeof(int));
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        //...相关操作
        
        //...没有进行内存释放
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 动态内存释放后继续使用
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        int* ptr = (int*)malloc(10 * sizeof(int));
        if (ptr == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        //...相关操作
        
        //内存释放
        free(ptr);
        //ptr = NULL;
        
        *ptr = 10;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    柔性数组

    有几点必须明确:

    1. 柔性数组只可能出现在结构体中。
    2. 柔性数组成员必须是结构体中的最后一个成员,也就是说,结构体中至少有两种成员。
    3. 使用sizeof计算结构体的大小时,柔性数组成员不算在内。
    4. 含有柔性数组成员的数组应使用malloc函数进行内存分配,大小应大于结构体自身的大小。

    举例

    #include 
    #include 
    #include 
    #include 
    
    typedef struct S
    {
      char c;
      int i;
      int arr[0];
    }type_s;
    
    
    int main()
    {
        type_s* p = (type_s*)malloc(sizeof(type_s) + 100 * sizeof(int));
        if (p == NULL)
        {
            printf("%s\n", strerror(errno));
            return 1;
        }
        p->c = 'w';
        p->i = 0;
        for (int i = 0; i < 100; i++)
        {
            p->arr[i] = i;
        }
        //动态内存释放
     	free(p);
        p = NULL;
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    1052 Tian Ji -- The Horse Racing 田忌赛马 贪心算法
    基于Java公益志愿捐赠管理系统设计与实现(源码+LW+调试+开题报告)
    记录一次Linux挂载NAS共享的SMB文件系统经历
    web前端期末大作业——网页制作基础大二dw作业——动画漫展学习资料电影模板(6页)
    多线程一定能优化程序性能吗?
    C++中的std::move函数到底是做什么的?
    JCMsuite应用:光子晶体谐振腔光子晶体谐振腔
    JSON概念、基础语法及数据和对象转换
    LLVM IR 构建 分析 转换 优化 IRBuilder Pass AI编译器后端代码生成
    监控——前端性能监控方案
  • 原文地址:https://blog.csdn.net/qq_62395724/article/details/125879816