C语言学习第31篇---内存操作经典问题实例分析

举报
CodeAllen 发表于 2021/10/30 01:20:35 2021/10/30
【摘要】 都是从项目经验中提炼出的问题 但凡程序出现了一些问题,多半和内存有关!!!所有资料电子版可以通过置顶文章获取   内存操作经典问题实例分析一   野指针 1.指针变量中的值是非法的内存地址,进而形成野指针 2.野指针不是NULL指针,是指向不可用内存地址的指针 3.NULL指针并无危害,很好判断,也很...

都是从项目经验中提炼出的问题

但凡程序出现了一些问题,多半和内存有关!!!所有资料电子版可以通过置顶文章获取

 

内存操作经典问题实例分析一

 

野指针

1.指针变量中的值是非法的内存地址,进而形成野指针

2.野指针不是NULL指针,是指向不可用内存地址的指针

3.NULL指针并无危害,很好判断,也很好调试(只有NULL指针而已)

4.C语言中无法判断一个指针所保存的地址是否合法

 

野指针的由来(项目开发中出现了崩溃,出现了重启,肯定是有野指针)

1.局不指针变量没有初始化(指针的初始化时非常重要的)

2.指针所指向的变量在指针之前被销毁

3.使用已经释放的指针

4.进行了错误的指针运算

5.进行了错误的强制类型转换

 

实验1:野指针


  
  1. #include <stdio.h>
  2. #include <malloc.h>
  3. int main()
  4. {
  5.     int* p1 = (int*)malloc(40);  //
  6.     int* p2 = (int*)1234567;     //进行了错误的类型转换
  7.     int i = 0;
  8.     for(i=0; i<40; i++)
  9.     {
  10.         *(p1 + i) = 40 - i;//申请了40个字节,不是40个整型变量,肯定会有野指针,这里内存越界了
  11.     }
  12.     free(p1);  //这里只是归还内存,后边的使用都是野指针,最好的方法是   p1 = NULL;
  13.     for(i=0; i<40; i++)
  14.     {
  15.         p1[i] = p2[i];  //野指针的实例,之前已经释放了
  16.     }
  17.     return 0;

 

 

杜绝的基本原则:

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

2.任何变量在定义后必须初始化为0

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

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

 

 

实验2


  
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <malloc.h>
  4. struct Student
  5. {
  6.     char* name;
  7.     int number;
  8. };
  9. char* func()
  10. {
  11.     char p[] = "D.T.Software";
  12.     return p;
  13. }
  14. void del(char* p)
  15. {
  16.     printf("%s\n", p);
  17.     free(p);
  18. }
  19. int main()
  20. {
  21.     struct Student s;  0//这里没有初始化,已经有问题了
  22.     char* p = func();  //指针指向局部数组,已经产生野指针了
  23.     strcpy(s.name, p); //两个都是野指针
  24.     s.number = 99;
  25.     p = (char*)malloc(5);
  26.     strcpy(p, "D.T.Software");// 这里内存越界了,上边只分配了5个字节
  27.     del(p);
  28.     return 0;
  29. }

 

小结:

1.内存错误使实际产品开发中最常见的错误,然而绝大多数bug是可以通过遵循最常见的编程原理和规范来避免的

2.因此,在学习的时候要牢记和理解内存操作的基本原则,目的和意义

 

 

内存操作经典问题实例分析二

 

常见内存错误

1.结构体成员未初始化

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

3.内存分配成功,但是并未初始化

4.内存操作越界

 

实例1:


  
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <malloc.h>
  4. struct Student
  5. {
  6.     char* name;
  7.     int number;
  8. };
  9. char* func()
  10.     char p[] = "D.T.Software";
  11.     return p;
  12. }
  13. void del(char* p)
  14. {
  15.     printf("%s\n", p);
  16.     free(p);
  17. }
  18. int main()
  19. {
  20.     struct Student s;  0//这里没有初始化,已经有问题了
  21.     char* p = func();  //指针指向局部数组,已经产生野指针了
  22.     strcpy(s.name, p); //两个都是野指针
  23.     s.number = 99;
  24.     p = (char*)malloc(5);
  25.     strcpy(p, "D.T.Software");// 这里内存越界了,上边只分配了5个字节
  26.     del(p);
  27.     return 0;
  28. }

 

总结经验:

1.在哪个函数申请的内存就在哪个函数释放

2.内存越界不是很好调试,因为有可能需要很久时间才会爆发出来

 


  
  1. #include <stdio.h>
  2. #include <malloc.h>
  3. void test(int* p, int size)
  4. {
  5.     int i = 0
  6.     for(i=0; i<size; i++)
  7.     {
  8.         printf("%d\n", p[i]);
  9.     }
  10.     free(p);  //这里释放了一次内存
  11. }
  12. void func(unsigned int size)
  13. {
  14.     int* p = (int*)malloc(size * sizeof(int));
  15.     int i = 0;
  16.    
  17.     if( size % 2 != 0 )
  18.     {
  19.         return;
  20.     }
  21.     for(i=0; i<size; i++)
  22.     {
  23.         p[i] = i;
  24.         printf("%d\n", p[i]);
  25.     }
  26.     free(p);  //又释放了一次,会出错
  27. }
  28. int main()
  29. {
  30.     int* p = (int*)malloc(5 * sizeof(int));
  31.     test(p, 5);
  32.     free(p);
  33.     func(9);
  34.     func(10);
  35.     return 0;
  36. }

 

实例2


  
  1. #include <stdio.h>
  2. #include <malloc.h>
  3. struct Demo
  4. {
  5.     char* p;
  6. };
  7. int main()
  8. {
  9.     struct Demo d1;
  10.     struct Demo d2;
  11.     char i = 0;
  12.     for(i='a'; i<'z'; i++)
  13.     {
  14.         d1.p[i] = 0//这里的野指针直接造成段错误  Segmentation fault (core dumped)
  15.     }
  16.     d2.p = (char*)calloc(5, sizeof(char));
  17.     printf("%s\n", d2.p);
  18.     for(i='a'; i<'z'; i++)
  19.     {
  20.         d2.p[i] = i;
  21.     }
  22.   
  23.     free(d2.p);
  24.     return 0;
  25. }

 

 

总结了几个内存的操作的交通规则

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


  
  1. int*p = (int*)malloc(56);
  2. if(p != NULL)
  3. {
  4.     //dO something here!!!
  5. }
  6. free(p);

 

2.free指针之后必须立即赋值为NULL

1》避免野指针

2》也可以防止多次释放同一个指针(内存),因为free函数有保护,参数是空什么都不做(指针)


  
  1. int*p = (int*)malloc(20);
  2. free(p);
  3. p = NULL;
  4. //..
  5. //...
  6. //...
  7. //..
  8. //...............
  9. if(p != NULL)
  10. {
  11.     //Do something here
  12. }

 

3.任何与内存的操作相关的函数都必须带长度信息

1》预防内存越界---数组,字符串,。。。都得带上长度信息


  
  1. void print(int* p,int size)
  2. {
  3.     int i = 0;
  4.     char buf[128] = {0};
  5.     
  6.     snprintf(buf,sizeof(buf),"%s","KANGKANG"); //这里是利用函数内部的检查机制
  7.     
  8.     for(i = 0;i<size;i++)
  9.     {
  10.         printf("%d\n",p[i]);
  11.     }
  12. }

 

4.malloc操作和free操作必须匹配,防止内存泄露和多次释放

1》语法上确实支持跨函数释放内存,但是一旦使用肯定会出错


  
  1. void func()
  2. {
  3.     int* p = (int*)malloc(20);
  4.     
  5.     free(p);
  6. }
  7. int main()
  8. {
  9.     int*p = (int*)malloc(40);
  10.     func();
  11.     free();
  12.     
  13.     return 0;
  14. }

 

小结:

1.内存错误的本质源于指针保存的地址为非法值

   ---指针变量未初始化,保存随机值

   ---指针运算导致内存越界

1.内存泄露源于malloc和free不匹配

   ---当malloc次数多余free时,会造成内存泄露

   ---当malloc次数少余free时,程序可能崩溃

 

 

 

文章来源: allen5g.blog.csdn.net,作者:CodeAllen的博客,版权归原作者所有,如需转载,请联系作者。

原文链接:allen5g.blog.csdn.net/article/details/89113979

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。