1.空指针 程序员
通常来讲,程序的起始地址是从“代码区”的0地址开始存放的(注:若是插入一个内存分布图,则更能说明问题,此处省略),但实际上现代操做系统并不是如此,却保留了从0开始的一块内存。至于这块内存到底有有多大,与具体的操做系统有关。若是程序试图访问这块内存,则系统提示异常。 函数
为何操做系统不是保留一个字节呢?因为内存管理是按页来进行的,所以没法作到单独保留一个字节。尽管如此,但仍是有极少数系统设定RAM区从0地址开始,但指向有效变量的指针不会指向0地址。即便“代码区”从0地址开始,但在任何状况下,0地址都不是C语言中任何函数的起始地址,所以指向有效函数地址的指针也不会指向0地址。 spa
☛ 课外知识延伸 操作系统
虽然 80C51微控制器XDATA区(外部RAM)是从0地址开始的,但只要对保存在0地址中的变量不进行取地址操做(&操做),便可有效地保证指针不会指向0地址。 指针
与此同时,虽然32位ARM7微控制器也是从0地址开始的,但这块内存仅用于存放中断向量代码,而不是程序中的有效变量地址,所以即使用空指针来判断指针的有效性,其仍然是可行的。 orm
基于此,因而将空指针定义为指向0地址的指针。毫无疑问,任何一种指针类型都有一个特殊的指针值,即空指针。它既不会指向任何对象或函数,也不是任何对象或函数的地址。而未初始化的指针,则彻底可能指向任何地方。 对象
因而可知,空指针与未初始化的指针是彻底不一样的两个概念。那么,将如何在程序中得到一个空指针呢? 内存
2. 空指针常量与NULL 原型
标准C规定,在初始化、赋值或比较时,若是一边是变量或指针类型的表达式,则编译器能够肯定另外一边的常数0为空指针,并生成正确的空指针值。即在指针上下文中“值为0的整型常量表达式”在编译时转换为空指针。 编译器
为了让程序中的空指针使用更加明确,标准C专门定义了一个标准预处理宏NULL,其值为“空指针常量”,一般为0或(void *)0,即在指针上下文中NULL与0是等价的,而未加修饰的0也是彻底能够接受的。因为void *指针的特殊赋值属性,好比:
#define NULL ((void *)0)
当NULL定义为((void *)0)时,即NULL是能够赋值给任何类型指针的值,它的类型为void*,而不是整数0,所以初始化“FILE *fp = NULL;”是彻底合法的。
而为了区分整数0和空指针0,当须要其它类型的0的时候,即便可能工做,但也不能使用NULL,若是这样处理其格式是错误的,这在非指针上下文中是不能工做的。特别地,不能在须要ASCII空字符(NUL)的地方使用NULL。若是确实须要,则能够自定义为:
#define NUL '\0'
因而可知,常数0是一个空指针常量,而NULL仅仅是它的一个别名。
3. 空指针的用途
通常来讲,未初始化是不能使用的非法指针,由于它彻底有可能指向任何地方,从而致使程序没法判断它为非法指针。所以,无论指针变量是全局的仍是局部的、静态的仍是非静态的,都应该在声明它的同时进行初始化,要么赋予一个有效的地址,要么赋予NULL。
标准C规定,全局指针变量的默认值为NULL,而对于局部指针变量则必须明确地指定其初值。所以,void一般用于指针变量的初始化,用来判断一个指针的有效性。好比:
unsigned char *pucBuf=(void *)0; // 定义pucBuf为unsigned char类型指针并初始化为空指针
若是后续的代码忘记初始化指针而直接使用的话,则可能形成程序失败。虽然空指针也是非法指针,但能够经过程序判断并告诉程序员代码可能有问题。也就是说,若是一开始就将指针初始化为空指针,则可避免程序异常。好比:
if(pucBuf==0){
return error; // 若是pucBuf为空指针,则返回参数错误
}
因为void类型指针的不肯定性,所以它能够指向任意类型的数据,那么只要在使用时作一个简单的强制类型转换就能够了。好比:
unsignned char *pcData = NULL; // 定义pcData为unsigned char类型指针
void *pvData; // 定义pvData为void类型指针
pvData = pcData; // 无需进行强制类型转换
pcData = (unsigned char*) pvData; // 将pvData强制转换为unsigned char类型指针
显然不存在void类型的对象,也就是说,当对象为空类型时,其大小为0字节;当对象未肯定类型时,那么它的大小也是未肯定的,所以不能声明void类型变量。好比:
void a; // 非法声明
既然上述声明是非法的,那么,也就不能将sizeof运算符用于void类型。也就意味着,编译器不知道所指对象的大小,因为指针的算术运算老是基于所指对象的大小的,所以不容许对void指针进行算术运算。
总之,在指针声明中,void *表示通用指针的类型。若是void做为函数的返回类型,则表示不返回任何值。若是void位于参数列表中,则表示没有参数。
4. 用无类型指针做为函数参数
因为C语言中最小长度的变量为char类型(包括unsigned char、signed char等),其sizeof(char)的结果为1,而其它任何变量的长度都是它的整数倍。好比,若是使用SDCC51编译器,其sizeof(int)为2。由于通用swap函数函数不知道须要交换的变量的类型,因此须要一个参数给出相应的指示。因为C语言的变量类型多种多样,所以不可能为每一种变量类型编号,并且swap并不关心变量的真正类型,因此能够用变量的长度代替变量类型。通用swap函数的原型为:
void swap(void *pvData1, void *pvData2, int iDataSize