野(wild)指针与悬空(dangling)指针
1. 什么是野指针(wild pointer)?
A pointer in c which has not been initialized is known as wild pointer.
野指针(wild pointer)就是没有被初始化过的指针。例如,
o foo1.c
int main(int argc, char *argv[])
{
int *p;
return (*p & 0x7f); /* XXX: p is a wild pointer */
}
如果用"gcc -Wall"编译, 会出现如下警告:
$ gcc -Wall -g -m32 -o foo foo.c
foo.c: In function ‘main’:
foo.c::: warning: ‘p’ is used uninitialized in this function [-Wuninitialized]
return (*p & 0x7f); /* XXX: p is a wild pointer */
^
2. 什么是悬空指针(dangling pointer)?
If a pointer still references the original memory after it has been freed, it is called a dangling pointer.
悬空指针是指针最初指向的内存已经被释放了的一种指针。 典型的悬空指针看起来是这样的,(图片来源是这里)

如果两个指针(p1和p2)指向同一块内存区域, 那么free(p1)后,p1和p2都成为悬空指针。如果进一步将p1设置为NULL, 那么p2还是悬空指针。诚然,使用*p1会导致非法内存访问,但是使用*p2却会出现无法预料的结果,可谓防不胜防。例如:
o foo2.c
1 #include <stdlib.h>
2 int main(int argc, char *argv[])
3 {
4 int *p1 = (int *)malloc(sizeof (int));
5 int *p2 = p1; /* p2 and p1 are pointing to the same memory */
6 free(p1); /* p1 is a dangling pointer, so is p2 */
7 p1 = NULL; /* p1 is not a dangling pointer any more */
8 return (*p2 & 0x7f); /* p2 is still a dangling pointer */
9 }
3. 使用野指针和悬空指针的危害
无论是野指针还是悬空指针,都是指向无效内存区域(这里的无效指的是"不安全不可控")的指针。 访问"不安全可控"(invalid)的内存区域将导致"Undefined Behavior"。
关于"Undefined Behavior", 定义(参考来源:A Guide to Undefined Behavior in C and C++, Part 1)如下:
Anything at all can happen; the Standard imposes no requirements. The program
may fail to compile, or it may execute incorrectly (either crashing or silently
generating incorrect results), or it may fortuitously do exactly what the
programmer intended.
也就是说:任何可能都会发生。要么编译失败,要么执行得不正确(崩溃(e.g. segmentation fault)或者悄无声息地产生不正确的执行结果),或者偶尔会正确地产生程序员希望运行的结果。
4. 如何避免使用野指针和悬空指针?
如何避免使用野指针? 好办! 养成在定义指针后且在使用之前完成初始化的习惯就好。
然而,如何避免使用悬空指针,就比较麻烦了。 Solaris引入了ADI(Application Data Integrity)技术避免访问已经释放的内存区域,例如: 最新的SPARC平台已经支持KADI,一旦访问某个已经释放掉的内核内存区域,就会引发操作系统panic。这里简单介绍一下什么是ADI。
ADI (Application Data Integrity) is a software layer built on
MCD (Memory Corruption Detection), a SPARC hardware feature that provides
statistical protection against memory corruption errors such as buffer
overflows, use-after-frees, and use-after-reallocs. KADI allows kernel memory to use ADI.
这有效的避免了foo2.c示例代码中p2变成悬空指针还被使用的情况。 那么问题来了,如果没有ADI/KADI这种高大上的技术,如何避免使用悬空指针?
办法还是有的,直接避免不了就间接避免,那就是所谓的智能指针(smart pointer)。智能指针的本质是使用引用计数(reference counting)来延迟对指针的释放。
o 关于智能指针,请参考维基百科。
Smart pointers eliminate dangling pointers by postponing destruction until
an object is no longer in use.
o 关于引用计数,也请参考维基百科。
Reference counting is a technique of storing the number of references,
pointers, or handles to a resource such as an object, block of memory,
disk space or other resource.
特别说明: 引用计数不但在基于垃圾回收技术的内存管理中被广泛使用,而且在操作系统内核实现中也被广泛使用。 例如: 索引结点(inode)就有使用引用计数,从而保证了硬链接文件的实现。
$ stat foo | egrep Inode
Device: 23000000002h/2405181685762d Inode: Links: $ ln foo foo9 && stat foo | egrep Inode
Device: 23000000002h/2405181685762d Inode: Links: $ rm -f foo && stat foo9 | egrep Inode
Device: 23000000002h/2405181685762d Inode: Links:
关于智能指针,C++11有很好的支持。 为了方便理解智能指针本质上是延迟释放内存,下面给出一个简单的C代码实现(使用proxy(代理) + refcnt(引用计数))。
o foo3.c
#include <stdio.h>
#include <stdlib.h> typedef struct proxy_s {
int *object;
int refcnt;
} proxy_t; static int *create(proxy_t **proxy)
{
if (*proxy == NULL) {
proxy_t *p = (proxy_t *)malloc(sizeof (proxy_t));
p->object = (int *)malloc(sizeof (int));
p->refcnt = ;
*proxy = p;
} else {
((*proxy)->refcnt)++;
} return (*proxy)->object;
} static void destroy(proxy_t *proxy)
{
(proxy->refcnt)--; if (proxy->refcnt == ) {
free(proxy->object);
free(proxy);
}
} static void dump(proxy_t *proxy, int i)
{
printf("%02d\tobject: %p (0x%02x) refcnt: %d\n", i,
proxy->object, *(proxy->object), proxy->refcnt);
} int main(int argc, char *argv[])
{
proxy_t *proxy = NULL;
#define NEW() create(&proxy)
#define DELETE(p) do { destroy(proxy); p = NULL; } while (0)
#define DUMP(i) dump(proxy, i)
int *p1 = NEW();
int *p2 = NEW();
*p1 = 0xab; DUMP();
DELETE(p1); DUMP();
*p2 += 0x21; DUMP();
DELETE(p2); DUMP();
return ();
}
o 编译并执行
$ gcc -Wall -m32 -g -o foo3 foo3.c
$ ./foo3
object: 0x8d0d018 (0xab) refcnt:
object: 0x8d0d018 (0xab) refcnt:
object: 0x8d0d018 (0xcc) refcnt:
object: 0x8d0d010 (0x00) refcnt:
一句话总结: 明白了野指针和悬空指针的形成原理及其危害,那么在编程的时候就能有的放矢,写出安全可控的优质代码。
附: 关于wild pointer(野指针)和dangling pointer(悬空指针), 这里援引一段来自yahoo answer的解释以帮助更好的理解。
A dangling pointer is a pointer that used to point to a valid address but now
no longer does. This is usually due to that memory location being freed up and
no longer available. There is nothing wrong with having a dangling pointer
unless you try to access the memory location pointed at by that pointer.
It is always best practice not to have or leave dangling pointers. A wild pointer is a pointer that has not been correctly initialized and
therefore points to some random piece of memory.
It is a serious error to have wild pointers.
野(wild)指针与悬空(dangling)指针的更多相关文章
- c++野(wild)指针与悬空(dangling)指针
re 1.https://www.cnblogs.com/idorax/p/6475941.html end
- C++中指针常量和常量指针的区别
在C++学习使用过程中,每个人都不可避免地使用指针,而且都或多或少的接触过常量指针或指针常量,但是对这两个的概念还是很容易搞糊涂的. 本文即是简单描述指针常量和常量指针的区别. 常量指针 定义: 又叫 ...
- C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用
类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...
- 你必须知道的指针基础-7.void指针与函数指针
一.不能动的“地址”—void指针 1.1 void指针初探 void *表示一个“不知道类型”的指针,也就不知道从这个指针地址开始多少字节为一个数据.和用int表示指针异曲同工,只是更明确是“指针” ...
- 不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象, const 指针和指向 const 对象的指针, const 对象的引用
[源码下载] 不可或缺 Windows Native (18) - C++: this 指针, 对象数组, 对象和指针, const 对象, const 指针和指向 const 对象的指针, con ...
- 指针数组 vs 数组指针
指针数组,故名思义,就是指针的数组,数组的元素是指针: 数组指针,同样,就是直想数组的指针. 简单举例说明: int *p[2]; 首先声明了一个数组,数组的元素是in ...
- C++中,指针数组和数组指针
这俩兄弟长得实在太像,以至于经常让人混淆.然而细心领会和甄别就会发现它们大有不同. 前者是指针数组,后者是指向数组的指针.更详细地说. 前: 指针数组;是一个元素全为指针的数组. 后: 数组指针;可以 ...
- [Reprint]C++普通函数指针与成员函数指针实例解析
这篇文章主要介绍了C++普通函数指针与成员函数指针,很重要的知识点,需要的朋友可以参考下 C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般 ...
- 深入理解C语言中的指针与数组之指针篇
转载于http://blog.csdn.net/hinyunsin/article/details/6662851 前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本 ...
随机推荐
- STM32的型号的命名规则
每种STM32的产品都由16个字母或数字构成的编号标示,用户向ST订货时必须使用这个编号指定需要的产品.这16个字符分为8个部分,下面通过一个例子说明它们的意义: STM32 F C T xxx ST ...
- Java Hello World源代码notepad++版
Notepad++中写Java Hello World代码并运行,步骤如下: 1. 在E:\CodeDemo\Java文件夹下(你也可以选择自己的文件夹)新建一个txt文本文件. 如果没有NotePa ...
- HTML5入门教程:响应式页面布局
摘自:https://www.sohu.com/a/225633935_647584 随着互联网时代的发展,我们对网页布局有了新的要求,大气,美观,能够在不同的设备上呈现令人焕然一新的效果.此时,一个 ...
- WSDL文档结构图
- 如何在eclipse添加SVN菜单
首先需要在eclipse中安装svn插件,这个网上教程很多 那么我来说下如何在将svn添加到菜单中去吧. 很简单,
- ukylin 使用deepin-wine
之前一直是使用deepin的,但是自打前阵子更新内核之后,我的显卡就总出问题,这里也顺便f**k下NVIDIA. 在装了十几二十遍系统之后我终于还是妥协了,deepin最近又更新了,不知道解决我的显卡 ...
- 剑指offer(57)二叉树的下一个节点
题目描述 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 题目分析 这题一定要画图,因为只有画图我们才能分清楚下 ...
- FSMC_LCD
1. TFT-LCD(Thin Film Transistor Liquid Crystal Display)[薄膜晶体管液晶显示器] 2. 液晶 物质在熔融状态或在溶液状态下虽然获得了液体物质的流动 ...
- freeswitch 修改系统最大呼叫量
freeswitch 中有2个参数限制系统的最大呼叫量,以防止资源耗尽. max_session控制最大并发数.默认值1000:sps控制最大每秒呼叫量,默认值30 命令临时生效:fsctl max_ ...
- Windows下Python安装numpy+mkl,Scipy和statsmodels
最近做时间序列分析需要用到Python中的statsmodels,但是安装过程中遇到很头疼的问题,Google.Stackover各种都没有找到合适的解决办法,而且貌似还有很多同学也在吐槽Window ...