C语言学习第31篇---内存操作经典问题实例分析
【摘要】
都是从项目经验中提炼出的问题
但凡程序出现了一些问题,多半和内存有关!!!所有资料电子版可以通过置顶文章获取
内存操作经典问题实例分析一
野指针
1.指针变量中的值是非法的内存地址,进而形成野指针
2.野指针不是NULL指针,是指向不可用内存地址的指针
3.NULL指针并无危害,很好判断,也很...
都是从项目经验中提炼出的问题
但凡程序出现了一些问题,多半和内存有关!!!所有资料电子版可以通过置顶文章获取
内存操作经典问题实例分析一
野指针
1.指针变量中的值是非法的内存地址,进而形成野指针
2.野指针不是NULL指针,是指向不可用内存地址的指针
3.NULL指针并无危害,很好判断,也很好调试(只有NULL指针而已)
4.C语言中无法判断一个指针所保存的地址是否合法
野指针的由来(项目开发中出现了崩溃,出现了重启,肯定是有野指针)
1.局不指针变量没有初始化(指针的初始化时非常重要的)
2.指针所指向的变量在指针之前被销毁
3.使用已经释放的指针
4.进行了错误的指针运算
5.进行了错误的强制类型转换
实验1:野指针
#include <stdio.h>
#include <malloc.h>
int main()
{
int* p1 = (int*)malloc(40); //
int* p2 = (int*)1234567; //进行了错误的类型转换
int i = 0;
for(i=0; i<40; i++)
{
*(p1 + i) = 40 - i;//申请了40个字节,不是40个整型变量,肯定会有野指针,这里内存越界了
}
free(p1); //这里只是归还内存,后边的使用都是野指针,最好的方法是 p1 = NULL;
for(i=0; i<40; i++)
{
p1[i] = p2[i]; //野指针的实例,之前已经释放了
}
return 0;
}
杜绝的基本原则:
1.绝不返回局部变量和局部数组的地址
2.任何变量在定义后必须初始化为0
3.字符数组必须确认0结束符之后才能成为字符串
4.任何使用与内存操作相关的函数必须指定长度信息
实验2
#include <stdio.h>
#include <string.h>
#include <malloc.h>
struct Student
{
char* name;
int number;
};
char* func()
{
char p[] = "D.T.Software";
return p;
}
void del(char* p)
{
printf("%s\n", p);
free(p);
}
int main()
{
struct Student s; 0//这里没有初始化,已经有问题了
char* p = func(); //指针指向局部数组,已经产生野指针了
strcpy(s.name, p); //两个都是野指针
s.number = 99;
p = (char*)malloc(5);
strcpy(p, "D.T.Software");// 这里内存越界了,上边只分配了5个字节
del(p);
return 0;
}
小结:
1.内存错误使实际产品开发中最常见的错误,然而绝大多数bug是可以通过遵循最常见的编程原理和规范来避免的
2.因此,在学习的时候要牢记和理解内存操作的基本原则,目的和意义
内存操作经典问题实例分析二
常见内存错误
1.结构体成员未初始化
2.结构体成员指针未分配足够的内存
3.内存分配成功,但是并未初始化
4.内存操作越界
实例1:
#include <stdio.h>
#include <string.h>
#include <malloc.h>
struct Student
{
char* name;
int number;
};
char* func()
{
char p[] = "D.T.Software";
return p;
}
void del(char* p)
{
printf("%s\n", p);
free(p);
}
int main()
{
struct Student s; 0//这里没有初始化,已经有问题了
char* p = func(); //指针指向局部数组,已经产生野指针了
strcpy(s.name, p); //两个都是野指针
s.number = 99;
p = (char*)malloc(5);
strcpy(p, "D.T.Software");// 这里内存越界了,上边只分配了5个字节
del(p);
return 0;
}
总结经验:
1.在哪个函数申请的内存就在哪个函数释放
2.内存越界不是很好调试,因为有可能需要很久时间才会爆发出来
#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));
test(p, 5);
free(p);
func(9);
func(10);
return 0;
}
实例2
#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; //这里的野指针直接造成段错误 Segmentation fault (core dumped)
}
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;
}
总结了几个内存的操作的交通规则
1.动态申请内存之后,应该立刻检查指针值是否为NULL,防止使用NULL指针
int*p = (int*)malloc(56);
if(p != NULL)
{
//dO something here!!!
}
free(p);
2.free指针之后必须立即赋值为NULL
1》避免野指针
2》也可以防止多次释放同一个指针(内存),因为free函数有保护,参数是空什么都不做(指针)
int*p = (int*)malloc(20);
free(p);
p = NULL;
//..
//...
//...
//..
//...............
if(p != NULL)
{
//Do something here
}
3.任何与内存的操作相关的函数都必须带长度信息
1》预防内存越界---数组,字符串,。。。都得带上长度信息
void print(int* p,int size)
{
int i = 0;
char buf[128] = {0};
snprintf(buf,sizeof(buf),"%s","KANGKANG"); //这里是利用函数内部的检查机制
for(i = 0;i<size;i++)
{
printf("%d\n",p[i]);
}
}
4.malloc操作和free操作必须匹配,防止内存泄露和多次释放
1》语法上确实支持跨函数释放内存,但是一旦使用肯定会出错
void func()
{
int* p = (int*)malloc(20);
free(p);
}
int main()
{
int*p = (int*)malloc(40);
func();
free();
return 0;
}
小结:
1.内存错误的本质源于指针保存的地址为非法值
---指针变量未初始化,保存随机值
---指针运算导致内存越界
1.内存泄露源于malloc和free不匹配
---当malloc次数多余free时,会造成内存泄露
---当malloc次数少余free时,程序可能崩溃
文章来源: allen5g.blog.csdn.net,作者:CodeAllen的博客,版权归原作者所有,如需转载,请联系作者。
原文链接:allen5g.blog.csdn.net/article/details/89113979
【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)