关闭

正确使用内存

标签: deletenull编译器招聘数据结构存储
593人阅读 评论(0) 收藏 举报

对于初学者来说,内存是个神秘的空间。程序的绝大部分错误,也是在于内存的使用不当造成的,而且这些错误有些都是隐藏很深的。所以,如何掌握内存的使用,通晓系统对内存的管理手段,将是软件成功的一个非常关键的因素。

       首先我们要了解内存的分配方式。一般来说,内存的分配方式有三种:

1.从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

2.在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

3.从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

       以上三种分配方式,我们要注意内存生命期的问题:

1.静态分配的区域的生命期是整个软件运行期,就是说从软件运行开始到软件终止退出。只有软件终止运行后,这块内存才会被系统回收

2.在栈中分配的空间的生命期与这个变量所在的函数和类相关。如果是函数中定义的局部变量,那么它的生命期就是函数被调用时,如果函数运行结束,那么这块内存就会被回收。如果是类中的成员变量,则它的生命期与类实例的生命期相同

3.在堆上分配的内存,生命期是从调用new或者malloc开始,到调用delete或者free结束。如果不掉用delete或者free。则这块空间必须到软件运行结束后才能被系统回收。

下面我们再看看,在使用内存的过程中,我们经常发生一些什么样的错误。以及我们应该采取哪些对策。

发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。

常见的内存错误及其对策如下:

1  内存分配未成功,却使用了它。

编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。

2  内存分配虽然成功,但是尚未初始化就引用它。

犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。

内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

3  内存分配成功并且已经初始化,但操作越过了内存的边界。

例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for循环语句中,循环次数很容易搞错,导致数组操作越界。

4  忘记了释放内存,造成内存泄露。

含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。

动态内存的申请与释放必须配对,程序中malloc与free的使用次数一定要相同,否则肯定有错误(new/delete同理)。

5  释放了内存却继续使用它。

有三种情况:

1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。

2)函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。

3)使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

综上所述,我们应该注意:

1.用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。

2.不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

3.避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。

4.动态内存的申请与释放必须配对,防止内存泄漏。

5.用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”

下面举几个经典的错误例子,大家不要犯同样的错误:

1.  返回栈内存指针

char *GetString(void)

{

char *p = "hello world";

return p;

}

char* pGet = GetString();

这段程序编译时没有错误,运行也没有错误,但是你却无法使得返回的pGet指针指向的数据是你想要的“hello world”,因为指针p的生命期是函数GetString内,运行完函数GetString后,p分配的栈空间马上被系统回收了。虽然pGet指向了p当初分配的内存地址,但是那块地址已经没有内容了。

2.这是一个出现频率非常高的错误

char* pChar = new char;

……

int a ;

pChar = &a;

……

delete pChar;

当然这是一个例子,具体的程序各有不同。

这段程序有两个问题。一是pChar = &a;将导致pChar原先分配的空间无法再被获取,就象我们的丢失了朋友的电话号码一样,无法再联系这个朋友了。这就造成了内存泄漏。如果内存 泄漏多了,可能导致系统的崩溃,因为可用的资源将越来越少,直到枯竭为止。第二个问题是delete pChar将导致异常发生,因为这时的pChar已经不是指向动态分配的内存了,而是指向了a分配的栈空间,而栈空间是不能使用delete来回收的,因 此将导致内存异常。

       内存是财富,正确使用财富是关键,为人如此,编程也如此。

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=21584


[收藏到我的网摘]   [发送Trackback]  happyparrot发表于 2004年04月27日 23:59:00




特别推荐: 关闭

评论

#   狂想2046 发表于2004-07-07 13:12:00  IP: 61.163.245.*
呵呵,好文,顶之…………

#   zhoujiamurong 发表于2004-07-07 14:11:00  IP: 218.19.117.*
清楚

#   hongyang1980 发表于2004-09-23 09:52:00  IP: 218.3.94.*
good

#   追风的叶 发表于2005-05-17 11:34:00  IP: 221.6.29.*
上面写的是摘录林锐的高质量程序设计指南,希望在引用别人的文章内容时,请注明摘录的出处,表示对作者的尊敬.

#   suifengpiao 发表于2006-09-25 13:40:00  IP: 58.240.178.*
1. 返回栈内存指针

char *GetString(void)

{

char *p = "hello world";

return p;

}

char* pGet = GetString();

这段程序编译时没有错误,运行也没有错误,但是你却无法使得返回的pGet指针指向的数据是你想要的“hello world”,因为指针p的生命期是函数GetString内,运行完函数GetString后,p分配的栈空间马上被系统回收了。虽然pGet指向了p 当初分配的内存地址,但是那块地址已经没有内容了

//=======
上面错的很离谱啊

char *p = "hello world";
这里是局部常量,
char* pGet = GetString();这样是可以输出hello world的。


作者所指的应该是下面这种情况:
1. 返回栈内存指针

char *GetString(void)

{

char p[] = "hello world";

return p;

}

char* pGet = GetString();
 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:58210次
    • 积分:874
    • 等级:
    • 排名:千里之外
    • 原创:25篇
    • 转载:0篇
    • 译文:0篇
    • 评论:39条
    最新评论