1、内存分配方式
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
2、常见的内存错误及其对策
•
内存分配未成功,却使用了它。
•
编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为
NULL
。如果指针
p
是函数的参数,那么在函数的入口处用
assert(p!=NULL)
进行检查。如果是用
malloc
或
new
来申请内存,应该用
if(p==NULL)
或
if(p!=NULL)
进行防错处理。
•
内存分配虽然成功,但是尚未初始化就引用它。
•
犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。
•
内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。
•
内存分配成功并且已经初始化,但操作越过了内存的边界。
•
例如在使用数组时经常发生下标“多
1
”或者“少
1
”的操作。特别是在
for
循环语句中,循环次数很容易搞错,导致数组操作越界。
•
忘记了释放内存,造成内存泄露。含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
•动态内存的申请与释放必须配对,程序中
malloc
与
free
的使用次数一定要相同,否则肯定有错误(
new/delete
同理)。
•
释放了内存却继续使用它。有三种情况:
•(
1
)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
•
(
2
)函数的
return
语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
•
(
3
)使用
free
或
delete
释放了内存后,没有将指针设置为
NULL
。导致产生“野指针”。
建议:
•
用
malloc
或
new
申请内存之后,应该立即检查指针值是否为
NULL
。防止使用指针值为
NULL
的内存。
•
不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
•
避免数组或指针的下标越界,特别要当心发生“多
1
”或者“少
1
”操作。
•
动态内存的申请与释放必须配对,防止内存泄漏。
•
用
free
或
delete
释放了内存之后,立即将指针设置为
NULL
,防止产生“野指针”。
3、指针与数组的对比
•
C++/C
程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的。
•
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变。
•
指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们常用指针来操作动态内存。指针远比数组灵活,但也更危险。
5、别看
free
和
delete
的名字恶狠狠的(尤其是
delete
),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。
6、动态内存会被自动释放吗?
•
指针消亡了,并不表示它所指的内存会被自动释放。
•
内存被释放了,并不表示指针会消亡或者成了
NULL
指针。
7、杜绝“野指针”
“野指针”不是
NULL
指针,是指向“垃圾”内存的指针。人们一般不会错用
NULL
指针,因为用
if
语句很容易判断。但是“野指针”是很危险的,
if
语句对它不起作用。
“野指针”的成因主要有两种:
(
1
)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为
NULL
指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为
NULL
,要么让它指向合法的内存。例如
char *p = NULL;
char *
str
= (char *)
malloc
(100);
(
2
)指针
p
被
free
或者
delete
之后,没有置为
NULL
,让人误以为
p
是个合法的指针;
(
3
)指针操作超越了变量的作用范围。这种情况让人防不胜防。
8、内存耗尽怎么办?
如果在申请动态内存时找不到足够大的内存块,
malloc
和
new
将返回
NULL
指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。
(
1
)判断指针是否为
NULL
,如果是则马上用
return
语句终止本函数。
(
2
)判断指针是否为
NULL
,如果是则马上用
exit(1)
终止整个程序的运行。
(
3
)为
new
和
malloc
设置异常处理函数。