1. 什么是指针?
首先说一下什么是指针,只要明白了指针的含义,你就明白null的含义了。
假设 有语句 :
int a=10;
那么编译器就在内存中开辟1个整型单元存放变量a,我们假设这个整型单元在内存中的地址是 0x1000
;那么内存0x1000
单元中存放了数据10,每次我们访问a的时候,实际上都是访问的0x1000
单元中的10.
现在定义:
int *p;
p=&a;
当编译器遇到语句int *p
时,它也会在内存中给指针变量p分配一个内存单元,假设这个单元在内存的编址为0x1003
;此时,0x1003
中的值是不确定的,(因为我们没有给指针赋值),当编译器遇到了p=&a
时,就会在0x1003
单元中保存0x1000
,请看,这就是说:(指针变量p代表的)内存单元0x1003
存放了变量a的内存地址!用通俗的话说就是p指向了变量a。
2. 空指针NULL
p=NULL,就是说:内存单元0x1003
不存放任何变量的内存地址。
删除一个new了的数组。有必要的话。比如非标准的类( new CMyClass),在Type *p = new Type[N]; delete []p;
的最后最好再加一句: p = NULL
空指针是一个特殊的指针值,也是唯一一个对任何指针类型都合法的指针值。指针变量具有空指针值,表示它当时处于闲置状态,没有指向有意义的东西。空指针用0表示,C语言保证这个值不会是任何对象的地址。给指针值赋零则使它不再指向任何有意义的东西。为了提高程序的可读性,标准库定义了一个与0等价的符号常量NULL. 程序里可以写 p = 0; 或者 p = NULL; 两种写法都把p置为空指针值。相对而言,前一种写法更容易使读程序的人意识到这里是一个指针赋值。
3. 通用指针
我们印象中C语言的指针都有类型,实际上也存在一种例外。这里涉及到通用指针,它可以指向任何类型的变量。通用指针的类型用(void *)
表示,因此也称为void 指针。
int n=3, *p;
void *gp;
gp = &n;
p=(int *)gp1;
4. 野指针
野指针,也就是指向不可用内存区域的指针。通常对这种指针进行操作的话,将会使程序发生不可预知的错误。
“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。野指针的成因主要有两种:
-
一、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
-
二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。例:
char *p = (char *) malloc(100);
strcpy(p, “hello”);
free(p); // p 所指的内存被释放,但是p所指的地址仍然不变
if(p != NULL) // 没有起到防错作用
strcpy(p, “world”); // 出错
另外一个要注意的问题:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
指针是个很强大的工具,可是正因为它太强大,所以要操作它不是件易事。操作不当造成的野指针,甚至会引起系统死机等比较严重的后果。
5. 总结
- 指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的默认值是随机的,它会乱指一气。
- 指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
- 指针操作超越了变量的作用范围。这种情况让人防不胜防。
6. 参考
6.1. 空指针赋值分区
为什么通过空指针读写的时候就会出现异常?
除了NULL表示空指针,是否还有其他的值也是空指针?
如果还有其他的值,你们这些表示空指针的值都是什么?为什么?
首先解答第一个问题,在windows核心编程第四版的windows的内存结构一章中,表13-1有提到NULL指针分配的分区。其范围是从0x00000000
到0x0000FFFF
。这段空间是空闲的,对于空闲的空间而言,没有相应的物理存储器与之相对应,所以对这段空间来说,任何读写操作都是会引起异常的。
有了上面的解答后,第二个问题就很容易解答了。NULL的定义出现以下几个地方:
stdio.h文件中
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
ios.h文件中
#ifndef NULL
#define NULL 0
#endif
windef.h文件中
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
可见,NULL的值,基本上是用0来表示的,是不是只能用0呢?在windows xp sp2的系统平台下,如果执行下面代码也是会发生异常的:
int * pAddr = (int *)0x0000ffff;
*pAddr = 1;
而下面的代码是不会出问题的:
int * pAddr = (int *)0x00010000;
*pAddr = 1;
为什么呢?在windows xp sp2
下发现0x00000000
到0x0000FFFF
是空闲区间,而0x00010000
所处的是进程的私有区间。我想第二个问题应该已经解决了,我想,空指针是程序无论在何时都没有物理存储器与之对应的地址。为了保障“无论何时”这个条件,需要人为划分一个空指针的区域,固有上面NULL指针分区。
在第二个问题的基础上,要解答NULL指针的范围,那就相对来说容易了,对于在32位x86计算机上运行的windows xp sp2来说,就是从0x00000000
到0x0000ffff
。为什么分配如此大的空间?而在定义NULL的时候,只使用了 0x00000000
这么一个值,这不是浪费吗?我想,这是操作系统地址空间的分配粒度相关的,windows xp sp2的分配粒度是64KB,为了达到对齐,空间地址需要从0x00010000
开始分配,故空指针的区间范围有那么大。
声明:以上内容转载自 https://www.cnblogs.com/losesea/archive/2012/11/16/2772590.html,博主进行了整理,使之更清晰易读。