c语⾔⼊门-C语⾔空指针NULL以及void指针
空指针 NULL
⼀个指针变量可以指向计算机中的任何⼀块内存,不管该内存有没有被分配,也不管该内存有没有使⽤权限,只要把地址给它,它就可以指向,C语⾔没有⼀种机制来保证指向的内存的正确性,程序员必须⾃⼰提⾼警惕。
很多初学者会在⽆意间对没有初始化的指针进⾏操作,这是⾮常危险的,请看下⾯的例⼦:
1 #include <stdio.h>
2int main(){
3char *str;
4 gets(str);
5 printf("%s\n", str);
6return0;
7 }
这段程序没有语法错误,能够通过编译和链接,但当⽤户输⼊完字符串并按下回车键时就会发⽣错误,在 Linux 下表现为段错误(Segment Fault),在 Windows 下程序直接崩溃。如果你⾜够幸运,或者输⼊的字符串少,也可能不报错,这都是未知的。
前⾯我们讲过,未初始化的局部变量的值是不确定的,C语⾔并没有对此作出规定,不同的编译器有不同的实现,我曾警告⼤家不要直接使⽤未初始化的局部变量。上⾯的代码中,str 就是⼀个未初始化的局部变量,它的值是不确定的,究竟指向哪块内存也是未知的,⼤多数情况下这块内存没有被分配或者没有读写权限,使⽤ gets() 函数向它⾥⾯写⼊数据显然是错误的。
我强烈建议对没有初始化的指针赋值为 NULL,例如:
char *str = NULL;
NULL 是“零值、等于零”的意思,在C语⾔中表⽰空指针。从表⾯上理解,空指针是不指向任何数据的指针,是⽆效指针,程序使⽤它不会产⽣效果。
注意区分⼤⼩写,null 没有任何特殊含义,只是⼀个普通的标识符。
很多库函数都对传⼊的指针做了判断,如果是空指针就不做任何操作,或者给出提⽰信息。更改上⾯的代码,给 str 赋值 NULL,看看会有什么效果:
1 #include <stdio.h>
2int main(){
3char *str = NULL;
4 gets(str);
5 printf("%s\n", str);
6return0;
7 }
运⾏程序后发现,还未等⽤户输⼊任何字符,printf() 就直接输出了(null)。我们有理由据此推断,gets() 和 printf() 都对空指针做了特殊处理:gets() 不会让⽤户输⼊字符串,也不会向指针指向的内存中写⼊数据;
printf() 不会读取指针指向的内容,只是简单地给出提⽰,让程序员意识到使⽤了⼀个空指针。
我们在⾃⼰定义的函数中也可以进⾏类似的判断,例如:
null官方更新地址1void func(char *p){
2if(p == NULL){
3 printf("(null)\n");
4 }else{
5 printf("%s\n", p);
6 }
7 }
这样能够从很⼤程度上增加程序的健壮性,防⽌对空指针进⾏⽆意义的操作。
其实,NULL 是在stdio.h中定义的⼀个宏,它的具体内容为:
#define NULL ((void *)0)
(void *)0表⽰把数值 0 强制转换为void *类型,最外层的( )把宏定义的内容括起来,防⽌发⽣歧义。从整体上来看,NULL 指向了地址为 0 的内存,⽽不是前⾯说的不指向任何数据。
在进程的虚拟地址空间中,最低地址处有⼀段内存区域被称为保留区,这个区域不存储有效数据,也不能被⽤户程序访问,将 NULL 指向这块区域很容易检测到违规指针。
关于虚拟地址空间的概念以及程序的内存分布,我们将在《》专题中深⼊讲解,现在读者只需要记住,在⼤多数操作系统中,极⼩的地址通常不保存数据,也不允许程序访问,NULL 可以指向这段地址区间中的任何⼀个地址。
注意,C语⾔没有规定 NULL 的指向,只是⼤部分标准库约定成俗地将 NULL 指向 0,所以不要将 NULL 和 0 等同起来,例如下⾯的写法是不专业的:
int *p = 0;
⽽应该坚持写为:
int *p = NULL;
注意 NULL 和 NUL 的区别:NULL 表⽰空指针,是⼀个宏定义,可以在代码中直接使⽤。⽽ NUL 表⽰字符串的结束标志 '\0',它是ASCII码表中的第 0 个字符。NUL 没有在C语⾔中定义,仅仅是对 '\0' 的称呼,不能在代码中直接使⽤。
void 指针
对于空指针 NULL 的宏定义内容,上⾯只是对((void *)0)作了粗略的介绍,这⾥重点说⼀下void *的含义。void ⽤在函数定义中可以表⽰函数没有返回值或者没有形式参数,⽤在这⾥表⽰指针指向的数据的类型是未知的。
也就是说,void *表⽰⼀个有效指针,它确实指向实实在在的数据,只是数据的类型尚未确定,在后续使⽤过程中⼀般要进⾏强制类型转换。C语⾔动态内存分配函数 malloc() 的返回值就是void *类型,在使⽤时要进⾏强制类型转换,请看下⾯的例⼦:
1 #include <stdio.h>
2int main(){
3//分配可以保存30个字符的内存,并把返回的指针转换为 char *
4char *str = (char *)malloc(sizeof(char) * 30);
5 gets(str);
6 printf("%s\n", str);
7return0;
8 }
运⾏结果:
c.biancheng↙
c.biancheng
关于动态内存分配的概念以及 malloc() 的具体⽤法,我们将在《》专题中详细说明,这⾥重点是让⼤家理解void *,它不是空指针的意思,⽽是实实在在的指针,只是指针指向的内存中不知道保存的是什么类型的数据。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论