zoukankan      html  css  js  c++  java
  • 31 内存操作问题分析

    1 野指针

    1.1 野指针危害

    • 指针变量中的值是非法的内存地址,进而形成野指针
    • 野指针不是 NULL 指针,是指向不可用内存地址的指针
    • NULL 指针并无危害,很好判断,也很好调试
    • C 语言中无法判断一个指针所保存的地址是否合法

    1.2 野指针的由来

    • 局部指针变量没有被初始化

    • 指针所指向的变量在指针之前被销毁:返回局部数组

    • 使用已经释放过的指针:指针 free

    • 进行了错误的指针运算

    • 进行了错误的强制类型转换:整型值当作指针值

    • 示例:野指针

      • Demo

        #include <stdio.h>
        #include <malloc.h>
        
        int main()
        {
            int* p1 = (int*)malloc(40);  //p1指向malloc申请的动态内存
            int* p2 = (int*)1234567;  //p2指向一个整型值强制类型转换而来的地址,极大可能是野指针。但是程序不会在此处崩溃,取决于如何使用p2
            int i = 0;
            
            printf("%p
        ",p1);
            
            for(i = 0; i < 40; i++)
            {
                *(p1 + i) = 40 - i;  //野指针:进行了错误的指针运算(40个字节内存当作了40个int字节内存的),改写了非法的内存地址。
            }
        
            free(p1);  //p1所指向的内存释放后,p1保存的值还是原先的地址值,需要重新赋值为NULL
            
            printf("%p
        ",p1);
            
            for(i = 0; i < 40; i++)
            {
                p1[i] = p2[i];  //野指针使用:使用已经释放了的内存空间,非常难以调试,因为不会立即产生影响
            }
            
            return 0;
        }
        
      • 编译运行

        0x9baa008
        0x9baa008
        段错误
        
      • 修改

        #include <stdio.h>
        #include <malloc.h>
        
        int arr[40] = {1,2,3,4,5,6,7};
        
        int main()
        {
            int* p1 = (int*)malloc(40*sizeof(int));  
            int* p2 = arr;
            int i = 0;
            
            printf("%p
        ",p1);
            
            for(i = 0; i < 40; i++)
            {
                *(p1 + i) = 40 - i;
            }
        
            free(p1);
            p1 = NULL;
            
            printf("%p
        ",p1);
            
            for(i = 0; i < 40; i++)
            {
                p1[i] = p2[i];  //这里编译运行就会报错
            }
            
            return 0;
        }
        
      • 编译运行

        0x8ea8008
        (nil)
        段错误
        

    1.3 避免野指针的基本原则

    • 绝不返回局部变量和局部数组的地址

    • 任何变量在定义后必须 0 初始化

    • 字符数组必须确认 0 结束符后才能成为字符串

    • 任何使用与内存操作相关的函数必须指定长度信息

    • 示例:无处不在的野指针

      • Demo

        #include <stdio.h>
        #include <string.h>
        #include <malloc.h>
        
        struct Student
        {
            char* name;
            int number;
        };
        
        char* func()
        {
            char p[] = "ABCD";
            
            return p;  //返回局部字符数组的地址
        }
        
        void del(char* p)
        {
            printf("%s
        ", p);
            
            free(p);
        }
        
        int main()
        {
            struct Student s;  //没有进行初始化,s.name为野指针
            char* p = func();  //产生野指针,p为野指针
            
            strcpy(s.name, p);  //使用野指针p,s.name
            
            s.number = 99;
            
            p = (char*)malloc(5);
            
            strcpy(p, "ABCDEFGHI");  //没有指定长度信息,产生内存越界,操作了野指针指向的内存空间
            
            del(p);
            
            return 0;
        }
        
      • 编译

        test.c: In function 'func':
        test.c:15: warning: function returns address of local variable
        
      • 运行

        段错误
        

    2 常见内存错误

    • 结构体成员指针未初始化

    • 结构体成员指针未分配足够的内存

    • 内存分配成功,但并未初始化

    • 内存操作越界

    • 示例:常见的内存错误

      • Demo1

        #include <stdio.h>
        #include <malloc.h>
        
        void test(int* p, int size)
        {
            int i = 0;
            
            for(i = 0; i < size; i++)
            {
                printf("%d
        ", p[i]);
            }
            
            free(p);  //多次释放p指针所指向的内存空间 
        }
        
        void func(unsigned int size)
        {
            int* p = (int*)malloc(size * sizeof(int));
            int i = 0;
            
            //如果size为奇数,p指针所指向的堆空间不会释放
            if( size % 2 != 0 )
            {
                return; 
            }
            
            for(i = 0; i < size; i++)
            {
                p[i] = i;
                printf("%d
        ", p[i]);
            }
            
            free(p);
        }
        
        int main()
        {
            //在哪个函数中所申请的内存,就在哪个函数中进行释放
            int* p = (int*)malloc(5 * sizeof(int));
            
            test(p, 5);
            
            free(p);
            
            func(9);
            func(10);
               
            return 0;
        }
        
      • 编译运行:程序崩溃

      • Demo2

        #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;  //d1.p指向一个随机的地址,是一个野指针,这里操作了野指针
            }
            
            d2.p = (char*)calloc(5, sizeof(char));  //申请的内存空间全部置零,产生段错误
            
            printf("%s
        ", d2.p);  //d2.p指向一个空串
            
            for(i = 'a'; i < 'z'; i++)
            {
                d2.p[i] = i;  //d2.p所指向的内存空间只有5个字节大小,不能容纳下26个字节,内存越界(本质是指针运算产生野指针),不会立即产生影响
            }
            
            free(d2.p);
            
            return 0;
        }
        
      • 编译运行

        段错误
        

    3 内存操作的规则

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

      int* p = (int*)malloc(56);
      if(p != NULL)
      {
          //Do something here!
      }
      free(p);
      
    • free 指针之后必须立即赋值为 NULL

      int* p = (int*)malloc(20);
      
      free(p);
      p = NULL;
      
      //...
      
      if(p != NULL)
      {
          //Do something here!
      }
      
    • 任何与内存操作相关的函数都必须带长度信息

      void print(int* p,int size)
      {
          int i = 0;
          char buf[128] = {0};
          
          snprintf(buf,sizeof(buf),"%s","ABCDEFGH");
          
          for(i = 0;i < size; i++)
          {
              printf("%d
      ",p[i]);
          }
      }
      
    • malloc 操作和 free 操作必须匹配,防止内存泄漏和多次释放

      • 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;
      }
      
  • 相关阅读:
    [leetcode] 135. Candy (hard)
    [leetcode] 134. Gas Station (medium)
    [OpenGL] 绘制并且判断凹凸多边形、自相交多边形。
    [leetcode] 45. Jump Game II(hard)
    [leetcode] 55. Jump Game (Medium)
    [leetcode] 392. Is Subsequence (Medium)
    [leetcode] 147. Insertion Sort List (Medium)
    [leetcode]914. X of a Kind in a Deck of Cards (easy)
    [leetcode] 234. Palindrome Linked List (easy)
    [leetcode] 290. Word Pattern (easy)
  • 原文地址:https://www.cnblogs.com/bky-hbq/p/13774038.html
Copyright © 2011-2022 走看看