c++野指针小结
Posted bitcarmanlee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++野指针小结相关的知识,希望对你有一定的参考价值。
1.什么是野指针
所谓野指针(wild pointer),简单讲是指指向不可用的内存区域的指针。需要注意的一点是,野指针与NULL空指针是不同的。NULL指针一般比较好判断,直接用if (p==NULL)语句判断即可。但是野指针指向的是垃圾内存区域的指针,一旦使用往往会造成不可预测的结果,这种随机不可预测的结果才是最可怕的。
2.未初始化
造成野指针最常见的情况之一就是指针未被正确初始化。任何指针在被创建的时候,不会自动变成NULL指针,他的default值是随机的。所以一个比较好的习惯是,指针刚创建的时候,要么设置为NULL空指针,要么指向合理的内存区域。
看一下一个简单例子
void func()
int* p;
cout<<*p<<endl;
我们在main方法中调用该函数,会得到输出
1901647869
不难看出得到的就是一个随机值。
3.悬垂指针
悬垂指针也是野指针常见的一种。当我们显式地从内存中删除一个对象或者返回时通过销毁栈帧,并不会改变相关的指针的值。这个指针实际仍然指向内存中相同位置,甚至该位置仍然可以被读写,只不过这时候该内存区域完全不可控,因为你不能保证这块内存是不是被别的程序或者代码使用过。
void dangling_point()
char *p = (char *)malloc(10);
strcpy(p, "abc");
cout<<p<<endl;
free(p);
if (p != NULL) cout<<p<<endl;
strcpy(p, "def");
我们执行free§语句时,p所指向的内存被释放,此时该内存区域变成不可用内存。但是,p此时指向的地址并没有发生改变,而且也不会为空,所以if(p != NULL)判断为真,会继续执行后面的语句。
但是strcpy(p, “def”); 这一句,虽然代码不会报错崩溃,但此时正在篡改动态内存区,会产生不可预料的结果,此操作相当危险,尤其是debug时候非常不好排查,往往会让人崩溃…
为了避免悬垂指针的问题,一般做法是在free/delete指针以后,再将p=NULL,这样就可以避免上述问题。
4.返回栈内存指针或引用
看一个例子
int* return_ret()
int result = 1;
return &result;
int main(int argc, char const *argv[])
int *p = return_ret();
cout<<*p<<endl;
cout<<*p<<endl;
return 0;
首先,这段代码能编译通过。不过IDE会提醒warning:
test_code.cc:80:13: warning: address of stack memory associated with local variable 'result' returned [-Wreturn-stack-address]
return &result;
^~~~~~
1 warning generated.
什么意思?就是我们返回了一个局部变量的栈内内存地址。
代码也能正常运行:
1
32767
我们注意看,第一个打印语句,甚至还能输出"正确"的结果1。
但是到了第二个打印语句,此时输出的结果就不可控了,因为此时该内存区域的内容,已经发生了变化。此时该内存地址已经变得不"可靠",操作该内存区域将会相当危险。
5.特殊的空指针
空指针为一特殊指针,是唯一一个对任何指针类型都合法的指针值。为了提高程序可读性,标准库定义了一个与0等价的符号常量NULL。p = 0;p = NULL;都是把p置为空指针值,上面两种写法等价。
6.free操作的真相
以下部分内容来自网络:
内存管理有以下几个层次(从高到低):C程序 - C库(malloc)- 操作系统 - 物理内存
首先,操作系统保证每个进程都有独立的虚拟内存空间(32bit上应该是4G吧,一般进程也用不了这么多)。当然实际上物理内存是所有进程共享的,所以当你需要动态内存时,需要向操作系统申请,这时候虽然从你程序的角度,内存是连续的,其实是被操作系统映射到某一块物理内存而已。程序用完内存归还后,实际归还的部分可能被操作系统分配给其他进程。
要注意,上面说的“归还”是malloc库的行为。malloc库会使用一些策略来提高内存使用的效率,比如程序需要使用10K内存时,malloc实际可能上会申请1M,因为一次系统调用开销很大;再比如即使你调用了free“归还“了程序使用的内存,malloc库也可能并未真正把这些内存归还给操作系统,因为将来程序可能还会再申请动态内存。
malloc库有多种实现,我知道的一种是使用标记(tag)来存储内存的元信息。比如你申请了8个byte,得到的头指针地址是0x1001(实际内存为0x1001-0x1008),malloc会在0x1000(也就是头指针-1的位置)保存8,即这段内存的长度。等释放时,程序将头指针地址传给free,malloc库从头指针-1的位置发现需要释放的内存长度,释放内存(实际的操作可能只是将tag清空)。这就解释了:1. 为什么和malloc不同,free的参数只有一个头指针而不需要长度;2. free后内存实际上可能并未归还给操作系统。
所以,访问被(程序)释放的内存是一种undefined行为,就是说结果是不确定的。在malloc库未将此内存归还给操作系统也未进行下一次动态分配时,这块内存事实上仍属于程序。而当malloc库不清理归还的内存时(多数实现都是如此),你能访问到的值仍是原来的值。这和函数调用完毕而未清理栈帧、后续调用函数可以访问到之前已经设置的局部变量值是一个道理。
但是,当malloc库已经将内存归还给系统时,再去访问原来的地址(更别说写),由于这段地址已经不属于程序了,就会出现经典的segmentation fault。
说到底,这些现象还是C语言库为了更有效率的实现而妥协的结果。
创作打卡挑战赛 赢取流量/现金/CSDN周边激励大奖以上是关于c++野指针小结的主要内容,如果未能解决你的问题,请参考以下文章