野(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语言的根本 ...
随机推荐
- 【Spark-core学习之五】 RDD宽窄依赖 & Stage
环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk1.8 scala-2.10.4(依赖jdk1.8) spark ...
- percona-toolkit工具的使用
percona-toolkit是一组高级命令行工具的集合,可以查看当前服务的摘要信息,磁盘检测,分析慢查询日志,查找重复索引,实现表同步等等 percona-toolkit 源自 Maatkit 和 ...
- Linux基础命令---lpc打印机控制
lpc lpc指令用来控制打印机,它提供了一个命令行,用户可以输出命令来控制打印机.如果命令行上没有指定命令,lpc将从标准输入中显示提示符并接受命令. 此命令的适用范围:RedHat.RHEL.Ub ...
- 专题8:javascript中事件
一.事件流 1.1 事件冒泡 冒泡型事件:事件按照从最特定的事件目标到最不特定的事件目标的顺序逐一触发: 注意:各个浏览器在处理<html>标记级别的事件时顺序有出入,因此无论任何情况,都 ...
- Javascript 面向对象编程2:构造函数的继承
这个系列的第一部分,主要介绍了如何"封装"数据和方法,以及如何从原型对象生成实例.对象之间的"继承"的五种方法.比如,现在有一个"动物"对象 ...
- 使用shiro框架,注销问题的解决
在使用shiro框架的时候,有时候会因为登录问题找不到注销的controller.所以会报404的错误,下面是解决办法: 1.首先写一个类SystemLogoutFilter继承LogoutFilte ...
- /* * 有五个学生,每个学生有3门课的成绩,从键盘输入以上数据 *(包括学生号,姓名,三门课成绩),计算出平均成绩, *将原有的数据和计算出的平均分数存放在磁盘文件"stud"中。 */
1.Student类:类中有五个变量,分别是学号,姓名,三门成绩 package test3; public class Student { private int num; private Stri ...
- ElasticSearch(七) Elasticsearch在Centos下搭建可视化服务
要想可视化ElasticSearch,就需要安装一些插件,安装插件的前提是安装所依赖的环境,比如java,maven等,本篇博文就不再走那些流程了.没安装的童鞋可以看我的ElasticSearch栏目 ...
- hbot固件配置
又入了一台打印机,171到手,本来之前有更好的,无奈别人下手太快,只剩这台了. 175x135x180的样子. 创客的板,还带16g的闪迪内存卡,看到那会儿感觉赚大了! 拿到的时候不少螺丝松的,有的打 ...
- tomcat 修改内存配置
1.linux 下调整tomcat的内存设置修改bin目录下catalina.sh文件在cygwin=false之上添加以下语句JAVA_OPTS="-Xms1024m -Xmx4096m ...