关于C/C++内存的见解

    对于初次接触C语言人来说,内存管理往往是令人十分头疼的事,我记得以前在敲代码时往往也常常会发生很多内存泄露问题,现在把可能发生的问题整理如下:
  一.未分配空间就使用:
   这种问题往往应该时新手经常犯的问题,比如
 
#include <stdio.h>
struct num{    
    int n;
};
int main()
{    
    struct num *temp;
    temp->n=1;
    printf("%d\n",temp->n);
}
 


 这个例子中,temp只是一个指针,他并没有指向一个已知的空间,就会导致2中情况发生:
    1.有些编译器(笔者的就会)会给temp初始化为NULL,temp->n=1,就会使程序尝试对NULL的地址上存放值,这在c语言上时不被允许的,运行到这部就会报段错误。
    2.既然没给temp初始化,那么temp可能就是亿个野指针,胡乱指,这个问题就比较严重了,如果指向代码段,而代码段又是可读的,给他赋值段错误,如果指向堆空间,没有经过malloc,new事先申请,依然会报错。最严重的时没报错,因为有可能指向个你能访问的地址,同时你有权力该值,比如你定义的某个条件变量,这会使你整个程序某个地方错误,而你还不知道错在哪。
     个人习惯定义个指针时,就赋初值位NULL,不管编译器会不会给指针赋NULL,阻绝野指针。
二.超过内存界限
     这种错误往往发生在数组中,比如
int arry[10]={0};
for(int i=0;i<=10;++i)
{
    arry[i]=i;
    do something;
}
    这个程序随着i的不断自增,会导致出现arry【10】的情况,运行时报     stack  smashing  dected(检测到堆栈废碎)的错误,当然这种情况在c++中很少遇到,因为c++的容器有at这个小标自动检测是否溢出。当然这么简单的循环不会搞错,但如果是复杂的循环了?还有有时候你定义一个char buf【100】数组,然后执行sprintf(),write(),fwrite()等函数时特别是sprintf()函数,这个函数没有size_t count这个参数,就会导致你后面的字符串一旦超过100,就会出现堆栈废碎这种情况。解决方法也有,简单的就是定义个buf【1024】的数组,或者封装这个函数,进行错误检测。
三.malloc()和new()
     malloc()和new()这2个函数其实是会申请内存失败的,一旦申请失败就会返回NULL,如果不判断返回值,直接用的话,就可能对NULL进行操作,导致段错误。还有种情况最让程序员头疼的就是,我们知道malloc()申请的内存要用free()还,new()的要用delete还(至于2个能不能换,涉及了c++的类,不讨论),一般简单的程序没啥问题,都能记得。一旦程序复杂起来了,往往就会不知道还了没,哪边还,这就需要做项目前好好的构思构思,列个框架。当然这种情况在C++中其实已经得到基本解决,就是引用智能指针,智能指针的基本思路就是在调用构造函数是把这个指针的计数减一,为0时delet掉。有关智能指针注意点本篇就不概述了(笔者掌握的也不够熟练)。再提一点一个指针只会free(),delete一次,这也是为什么智能指针需要计数的原因了。而如果释放的指针指针没有置为NULL,就是是这个指针成为野指针,因为free(),delete这2个函数只是把指针指向的内存释放了,指针指向的地址依然存在,复杂的程序你就不知道这个指针是否释放掉了,比如:
int mian()
{
  dosomething;
  free(p);
  dosomething;
  if(p!=NULL)
  {
      *p=...;
      ....;
  }
  return 0;
}

     你就无法用if(p!=NULL)来进行判断了。所以释放好指针后乖乖的赋位NULL吧。
   既然释放内存这么麻烦,那我不释放不就好了!千万别抱这种思想,如果申请的内存不进行释放就会导致内存丢失,刚开始可能没啥问题,而且这种情况在程序退出时会自动释放掉内存。所以不是一直运行的程序也没啥大问题,如果你写的程序客户一直运行不关呢?就会导致不断申请空间,导致空间不够,程序卡死。所以就别抱什么侥幸心理了,乖乖的释放把。
四.函数返回指针
   函数的返回值千万不能是个栈空间的指针,即不是全局变量或者动态申请的,因为函数在返回时会自动销毁栈空间,导致返回的指针是个野指针。如果是在c++中因为因为类的缘故,又出现新的情况,(既为浅拷贝和深拷贝)大致如下:大家都知道类的对象在构造是会自动调用构造函数,销毁是会自动调用析构函数。那么如果自己写一个new申请堆空间的构造函数,那么很明显也得在析构函数中加入delete释放堆空间功能(不然会造成内存泄漏)。那么看下面的代码:
    
class A
{    
        public:  
                char *c;
                A(char *m)        
                {              
                    c=new char(10);          
                    strcpy(c,m);           
                    cout<<c<<endl;       
                }          
                ~A()     
               {             
                      delete c;    
               }  
};
int main()
{   
        char m[10]="123";       
        A a=m;   
        A b=a;
}

    这个函数功能很简单,只是把对象a的值赋值给b而已,但是因为a中有个成员c储存在堆空间中的,当程序运行到A b=a时,b中的成员c只是简单的把a中的成员c的地址拷一份而已,并不会再次申请新的堆空间,这就会导致执行析构函数时会对同一个地址delete2次(即对象a和b的析构函数)而对同一个地址只能释放一次,否则报错,这就是浅拷贝带来的弊端。解决方法很简单,在class中在加一个深拷贝函数即

 
 
A(const A &a)
{
   c=new char(10);
    strcpy(c,a.c);
}
    这样在运行到A b=a时,会执行上面的函数而不是系统默认的函数。其实就是在拷贝时在申请一个新的空间后在拷,就不会对同一块空间释放2次了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值