本文总结C语言中内存管理的一些注意事项。
2010-10-26 wcdj
本文接:Dissection C Chapter 4
http://blog.csdn.net/delphiwcdj/archive/2010/10/25/5965266.aspx
1,什么是野指针?
2,栈、堆和静态区
3,常见的内存错误及对策
1,什么是野指针?
我们可以把内存比作尺子,尺子上的0毫米处就是内存的0地址处,也就是NULL地址处。这条栓“野指针”的链子就是这个"NULL"。
【结论】定义指针变量的同时最好初始化为NULL,用完指针之后也将指针变量的值设置为NULL。也就是说,除了在使用时,别的时间都把指针“栓”到0地址处,这样它就老实了。
2,栈、堆和静态区
一般,我们可以简单地理解为内存分为三个部分:静态区,栈(stack),堆/堆栈(heap)。
【静态区】保存自动全局变量和static变量(包括static全局和局部变量)。静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。
【栈】保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是:效率高,但空间大小有限。
思考:栈的大小为什么是有限制的?为什么不能开辟的很大?
【堆】由malloc系列函数或new操作符分配的内存。其生命周期由free或delete决定,在没有释放之前一直存在,直到程序结束。其特点是:使用灵活,空间比较大,但容易出错。
堆和栈的区别可以用如下的比喻来看出: 使用栈就像我们去饭馆里吃饭,只管点菜(发出申请)、吃(使用)和付钱,吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,它的好处是快捷,但是自由度小。使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。
另可参考: 对堆和栈的理解整理
3,常见的内存错误及对策
【1】指针没有指向一块合法的内存
情况一:结构体成员指针未初始化。
错误1:
错误2:
情况二:没有为结构体指针分配足够的内存。
情况三:函数的入口校验。
【注意】
(1) 一般,在函数入口处使用assert(NULL!=p)对参数进行校验。在非参数的地方使用if(NULL!=p)来校验。但这都有一个要求:即p在定义的同时被初始化为NULL了,否者未被初始化的指针其内部是一个非NULL的乱码。
(2) assert是一个宏,而不是函数。包含在assert.h头文件中。这个宏只在Debug版本上起作用,而在Release版本被编译器完全优化掉,这样不会影响代码的性能。assert宏只是帮助我们调式代码用的,它的一切作用就是让我们尽可能在调式函数的时候把错误排除掉,而不是等到Release之后。assert宏可以帮助我们定位错误,而不是排除错误。
【2】为指针分配的内存太小
下面代码正确吗?
分析:
p1是字符串常量,其长度为7个字符,但其所占内存大小为8个byte,因为还包含字符串常量的结束标志"/0"。这样的话就导致p1字符串中最后一个空字符"/0"没有被拷贝到p2中。解决方法如下:
使用sizeof(char)的形式是为了提高代码的可移植性。
【3】内存分配成功,但并未初始化
【建议】对定义的变量要初始化,防止意想不到的错误。
比如:对数组进行初始化
int a[10]={0};// [1]
memset(a, 0, sizeof(a));// [2]
memset有三个参数,第一个是要被设置的内存起始地址;第二个是要被设置的值;第三个是要被设置的内存大小,单位是byte。
【4】内存越界
这种错误经常是由于操作数组或指针时出现“多1”或“少1”。
【5】内存泄露
由malloc系列函数或new操作符分配的内存,如果用完之后没有及时free或delete,这块内存就无法释放,直到整个程序终止。
malloc的原型:
void * __cdecl malloc(_In_ size_t _Size);
malloc函数的返回值是一个void类型指针,参数为int类型数据,即申请分配的内存大小,单位是byte。内存分配成功之后,malloc函数返回这块内存的首地址。我们需要一个指针来接收这个地址,但是由于函数的返回值是void*类型的,所以必须强制转换成你所接收的类型,即这块内存将要用来存储什么类型的数据。
比如:char *p=(char*)malloc(100);
【注意】malloc不是每次都能分配成功的。如果所申请的内存块大于目前堆上剩余内存块(整块,malloc函数申请的是连续的一块内存),则内存分配会失败,函数返回NULL。所以,我们必须用if(NULL!=p)语句来验证内存确实分配成功了。
【记住】malloc和free,“一定要一夫一妻制,不然肯定出错”。
【6】内存已经被释放了,但是继续通过指针来使用
情况一:free(p)之后,继续通过p指针来访问内存。解决的办法是:用完就给p置NULL。
情况二:函数返回栈内存。解决方法:要明白栈上变量的生命周期。