美文网首页DevSupportC语言C++
42_内存操作经典问题分析二

42_内存操作经典问题分析二

作者: 编程半岛 | 来源:发表于2017-12-27 10:44 被阅读8次

    1. 常见内存错误

    (1) 结构体成员指针未初始化——野指针
    (2)结构体成员指针未分配足够的内存——越界
    (3)内存分配成功,但并未初始化——字符串方面的错误,本质还是野指针
    (4)内存操作越界,如字符串操作是没有'\0'结束符
    程序说明:内存泄漏和多次释放指针

    #include <stdio.h>
    #include <malloc.h>
    
    void test(int* p, int size)
    {
        int i = 0;
        
        for(i=0; i<size; i++)
        {
            printf("%d\n", p[i]);
        }
        
        free(p);
    }
    
    void func(unsigned int size)
    {
        int* p = (int*)malloc(size * sizeof(int));
        int i = 0;
        
        if( size % 2 != 0 )     
        {
            return;                 // 没有释放堆空间,导致内存泄漏
        }
        
        for(i=0; i<size; i++)
        {
            p[i] = i;
            printf("%d\n", p[i]);
        }
        
        free(p);
    }
    
    int main()
    {
        int* p = (int*)malloc(5 * sizeof(int));     // p指向动态内存,其所属为main函数,也就是说在main函数中malloc的动态内存,需要在main函数中free
        
        test(p, 5);
        
        free(p);            // 重复释放,error
        
        func(9);
        func(10);
           
        return 0;
    }
    

    总结:需要弄清楚动态内存(堆中的内存)所属的函数:如上程序可知,p指向动态内存,其所属为main函数,也就是说在main函数中malloc的动态内存,需要在main函数中free。

    程序说明:结构体未初始化和内存越界产生的野指针

    #include <stdio.h>
    #include <malloc.h>
    
    struct Demo
    {
        char* p;
    };
    
    int main()
    {
        struct Demo d1;         // 结构体变量未初始化
        struct Demo d2;         // 结构体变量未初始化
        
        char i = 0;
        
        for(i='a'; i<'z'; i++)
        {
            d1.p[i] = 0;        // 由于结构体未初始化,p指针为野指针, error
        }
        
        d2.p = (char*)calloc(5, sizeof(char));
        
        printf("%s\n", d2.p);
        
        for(i='a'; i<'z'; i++)      // 内存越界,本质上是进行了错误的指针运算,产生了野指针。
        {
            d2.p[i] = i; 
        }
        
        free(d2.p);
        
        return 0;
    }
    

    2. 内存操作的交通规则

    (1)动态内存申请之后,应该立即检查指针值是否为NULL,防止使用NULL指针。

     int* p = (int*)malloc(56);
    if( p != NULL )
    {
        // do something here!
    }
    
    free(p);
    

    (2)free指针之后必须立即赋值为NULL,可以杜绝野指针,也可以防止多次释放同一个指针。因为free(NULL)将什么都不回操作,并且空指针是我们指针操作的一个标记,如果指针的值为NULL,将不会有任何操作。

    int* p = (int*)malloc(5*sizeof(int));
    free(p);
    p = NULL;
    // ...
    // ......
    // .........
    // ............
    
    if( p != NULL)
    {
      // do something here!
    }
    

    (3) 任何内存操作相关的函数都必须带长度信息,防止指针越界。

    void print(int* p, int size)
    {
        int i = 0;
        char buf[128] = {0};
    
        snprintf(buf, sizeof(buf), "%s", "D.T.Software");
    
        for(i=0; i<size; i++)
        {
            printf("%d\n", p[i]);
        }
    }
    

    (4)malloc操作和free操作必须比配,并且需要分清楚malloc分配的动态内存空间的所属问题,在所属函数中进行free操作,防止内存泄漏和多次释放。

     void func()
    {
      int* p = (int*)malloc(20);
    
      free(p);
    }
    
    int main()
    {
      int* p = (int*)malloc(40);
      
      func();
      
      free(p);
      
      return 0;
    }
    

    3. 小结

    (1) 内存错误的本质源于指针保存的地址为非法值——野指针

    • 指针变量未初始化,保存随机值
    • 指针运算导致内存越界

    (2)内存泄漏源于malloc和free不匹配

    • 当malloc次数多于free时,产生内存泄漏
    • 当malloc次数少于free时,程序可能崩溃

    相关文章

      网友评论

        本文标题:42_内存操作经典问题分析二

        本文链接:https://www.haomeiwen.com/subject/nhuegxtx.html