void
Test(
void
)
{
char
*
str
=
(
char
*
) malloc(
100
);
strcpy(str, “hello”);
free(str);
if
(str
!=
NULL)
{
strcpy(str, “world”);
printf(str);
}
请问运行Test函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str != NULL)语句不起作用。
1.何为野指针?
野指针指指向一个已删除的对象或未申请访问受限内存区域的指针。与空指针不同,野指针无法通过简单地判断是否为NULL避免,而只能通过养成良好的编程习惯来尽力减少。对野指针进行操作很容易造成程序错误。
解释一下:
malloc 和 free 是在系统的堆上分配空间。
malloc是申请,意思就是告诉系统,我要用一块RAM,给我用了别人就不要用了。
free是释放,意思是告诉系统,给我的这块RAM我用完了,不再用了,系统可以把它干别的了。free()释放的是指针指向的内存。并不是说,free()解除了原来指向这块内存的指针与这块内存地址之间的联系,联系依然存在,这就可能导致误用出错,所以需要在free之后置NULL,这样就解除关联了。举个例子就是,A跟妻子离婚了,但A和她曾经是夫妻,曾经关系很亲密,这种关系不会随着离婚而改变,这是客观的,离婚了即free,A不能再去骚扰前妻,否则就是违法,A要是再想干那啥,A会坐牢的!!!
free之后,系统还没有拿这块RAM干别的事之前,这块RAM的内容可能是不会变的,依然可以读出原来的内容,因为你的指针a还是指向这块RAM。但要注意,这块RAM已经不属于你了,读一下内容无所谓,如果往里面写就很危险了。
2.成因
通俗说法:
如果程序定义了一个指针,就必须要立即让它指向一个我们设定的空间或者把它设为NULL,如果没有这么做,那么这个指针里的内容是不可预知的,即不知道它 指向内存中的哪个空间(即野指针),它有可能指向的是一个空白的内存区域,可能指向的是已经受保护的区域,甚至可能指向系统的关键内存,如果是那样就糟 了,也许我们后面不小心对指针进行操作就有可能让系统出现紊乱,死机了。所以我们必须设定一个空间让指针指向它,或者把指针设为NULL,这是怎么样的一 个原理呢,如果是建立一个与指针相同类型的空间,实际上是在内存中的空白区域中开辟了这么一个受保护的内存空间,然后用指针来指向它,那么指针里的地址就 是这个受保护空间的地址了,而不是不可预知的啦,然后我们就可以通过指针对这个空间进行相应的操作了;如果我们把指针设为NULL,我们在头文件定义中的 #define NULL 0 可以知道,其实NULL就是表示0,那么我们让指针=NULL,实际上就是让指针=0,如此,指针里的地址(机器数)就被初始化为0了,而内存中地址为0 的内存空间……不用多说也能想象吧,这个地址是特定的,那么也就不是不可预知的在内存中乱指一气的野指针了。
还应该注意的是,free和delete只是把指针所指的 内存给释放掉,但并没有把指针本身干掉。指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不 把p设置为NULL,会让人误以为p是个合法的指针。用free或delete释放了内存之后,就应立即将指针设置为NULL,防止产生“野指针”。内存 被释放了,并不表示指针会消亡或者成了NULL指针。(而且,指针消亡了,也并不表示它所指的内存会被自动释放。)
指针变量未初始化
任何
指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
指针释放后之后未置空
有时
指针在free或delete后未赋值 NULL,便会使人以为是合法的。别看free和delete的名字(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”。
指针操作超越变量作用域
不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。示例程序如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
A {
public
:
void
Func(
void
){ cout << “Func of
class
A” << endl; }
};
class
B {
public
:
A *p;
void
Test(
void
) {
A a;
p = &a;
// 注意 a 的生命期 ,只在这个函数Test中,而不是整个class B
}
void
Test1() {
p->Func();
// p 是“野指针”
}
};
|
函数 Test1 在执行语句 p->Func()时,p 的值还是 a 的地址,对象 a 的内容已经被清除,所以 p 就成了“野指针” 。
3.规避
在养成这些习惯的情况下,野指针的危害是可以降低的:
如:int* pInteger=NULL;//指针定义处
...
if(pInteger != NULL)
{
free(pInteger);
pInteger=NULL;//指针释放之后并不为空,要设置其为空
}
pInteger=(int*)malloc(10*sizeof(int));
if(pInteger != NULL)
{
printf("内存申请成功/n!");
exit(0);
}
...
初始化时置 NULL
指针变量一定要
初始化为NULL,因为任何指针变量刚被创建时不会自动成为NULL指针,它的
缺省值是随机的。
释放时置 NULL
当指针p指向的内存空间释放时,没有设置指针p的值为NULL。delete和free只是把内存空间释放了,但是并没有将指针p的值赋为NULL。通常判断一个指针是否合法,都是使用if语句测试该指针是否为NULL。例如:
1
2
3
4
5
6
7
8
9
|
int
*p=newint(6);
delete
p;
// 应加入 p=NULL; 以防出错
// ...
if
(p != NULL)
{
*p=7;
cout << p << endl;
}
|
对于使用 free 的情况,常常定义一个宏或者函数 xfree 来代替 free 置空指针:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#define xfree(x) free(x); x = NULL;
// 在 C++ 中应使用 nullptr 指代空指针
// 一些平台上的 C/C++ 已经预先添加了 xfree 拓展,如 GNU 的
libiberty
xfree(p);
// 用函数实现,例如 GitHub 上的 AOSC-Dev/Anthon-Starter #9:
static
inline
void
*Xfree(
void
*ptr) {
free
(ptr);
#ifdef __cplusplus
return
nullptr
;
#else
return
NULL;
#endif
}
q=Xfree(q);
|
所以动态分配内存后,如果使用完这个动态分配的内存空间后,必须习惯性地使用delete操作符取释放它。
关于函数使用需要注意的一些地方:
A、申请了内存空间后,必须检查是否分配成功。
B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。
C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。
D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一
些编译器的检查。
4.相关链接
http://www.cnblogs.com/viviwind/archive/2012/08/14/2638810.html
http://blog.chinaunix.net/uid-24227137-id-3270110.html