操作系统复习
特点
编译器不能自动发现这类错误,通常是在程序运行时才能捕捉到
时隐时现,无明显症状
分类
1 内存分配未成功,却使用了它
起因 没有意识到内存分配会不成功 编程新手容易犯
解决对策 在使用内存之前,检查指针是否为空指针(NULL)
char *p = (char *) malloc(100); if (p == NULL) { printf("No enough memory! "); exit(0); }
2 内存分配成功,但是尚未初始化就引用它
起因 没有初始化的观念 误以为内存的默认值全为0
实际情况
内存的缺省初值究竟是什么并没有统一的标准。但是对于全局变量和静态变量如果没有手工初始化,编译器会将其初始化为零,而对栈内存和堆内存则不作任何处理。
另外,VC在Debug和Release状态下在初始化变量时所做的操作是不同的。Debug是将每个字节位都赋值成0xcc,以有利于调试。而Release的赋值是直接从内存中分配的,内容近似于随机。所以如果在没有初始化变量的情况下去使用它的值,就会导致问题发生。
解决对策 即使是赋0值也不可省略,不要嫌麻烦
3 内存分配成功,并且已经初始化,但操作越过了内存的边界
内存分配成功,并且已经初始化,但操作越过了内存的边界 例如:使用数组时经常发生下标“多1”或者“少1”的操作
解决对策: 在for语句中,注意循环次数不要搞错
访问越界会出现什么结果?
首先,它并不会造成编译错误! 就是说,C/C++的编译器并不判断和指出代码“访问越界”了。此外,数组访问越界在运行时,它的表现是不定的,有时似乎什么事也没有,程序一直运行(当然,某些错误结果已造成);有时,则是程序一下子崩溃。
4 忘记了释放内存,造成内存泄漏
忘记释放内存,造成内存泄漏(Memory Leak) “内存泄露”一词类似“原料泄露” 泄露出去的原料不能被利用,导致生产过程中原料不足。好比借东西不还 如果申请来的内存不用,别的程序也不能用,就好像这块内存泄露出去一样,造成浪费。
以下这段小程序演示了堆内存发生泄漏的情形:
void MyFunction(int nSize) { char* p= new char[nSize]; if( !GetStringFrom( p, nSize ) ){ MessageBox(“Error”); return; } …//using the string pointed by p; delete p; }
当函数GetStringFrom()返回零的时候,指针p指向的内存就不会被释放。这是一种常见的发生内存泄漏的情形。程序在入口处分配内存,在出口处释放内存,但是C函数可以在任何地方退出,所以一旦对分支处理不完整或者错误处理不当的话,就会发生内存泄漏。虽然函数体内的局部变量在函数结束时自动消亡,但是局部的指针变量所指向的内存并不会被自动释放。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,可能看不到错误,但终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,如果程序在入口处动态申请了内存,那么在程序的每个出口处都必须释放该内存空间。
解决对策
在需要的时候才malloc,并尽量减少malloc的次数 malloc的执行效率就不高,过多的malloc使程序性能下降 能用自动变量解决的问题,就不要用malloc来解决 malloc一般在大块内存分配和动态内存分配时使用
重复使用malloc申请到的内存
尽量让malloc和与之配套的free在一个函数或模块内 尽量把malloc集中在函数的入口处,free集中在函数的出口处
5 释放了内存,却继续使用它
free(ptr); if (ptr != NULL) //不起作用 { … }
起因: 指针所指的内存被释放以后,并不表示指针会消亡 其地址仍然不变(非NULL),只是该地址对应的内存是垃圾 指向垃圾内存的指针是“野指针”
有三种情况:
(a) 程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(b) 函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
(c) 使用free释放了内存后,没有将指针设置为NULL。导致产生“野指针”,即不是NULL指针,而是指向“垃圾”内存的指针。“野指针”是很危险的,因为使用if语句进行判断对它不起作用。
char *p = (char *) malloc(100); strcpy(p, “hello”); free(p); // p 所指的内存被释放,但是p所指的地址仍然不变 … if(p != NULL) // 没有起到防错作用 { strcpy(p, “world”); // 出错 }
解决对策
尽量把malloc集中在函数的入口处,free集中在函数的出口处
如果free不能放在函数出口处,则指针free后立即设置为NULL
不要把局部变量的地址作为返回值返回,因为该内存在函数体结束时被自动销毁
指针要么初始化为NULL,要么是其指向合法的内存
非法内存操作
起因
内存分配未成功,却使用了它
内存分配成功,但是尚未初始化就引用它
内存分配成功,并且已经初始化,但操作越过了内存的边界
释放了内存,却继续使用它
基本特征
代码访问了不该访问的内存地址
后果
几乎全是由指针混乱导致的
少数情况下,如在硬件驱动程序中的内存问题会造成操作系统的死亡