这里解释一下:值为0的整型常量表达式,或强制(转换)为 void * 类型的此类表达式,称为 空指针常量 。如0、0L、3-3、" "、017、(void)0等都是空指针常量。
至于系统将使用哪种形式作为空指针常量,取决于具体的实现。大多数通用C系统使用(void *)0或0,有些使用0L。对于C++系统,由于严格的类型转换要求,void *不能像C中那样自由转换为其他指针类型,因此通常选择0。作为空指针常量。
将空指针常量赋值给指针类型变量p,p就变成了空指针。
2.NULL值
宏NULL 在(和其他标头)中定义为空指针常量。
也就是说,NULL是一个标准的宏定义,用来表示空指针常量。
我们在stddef.h中找到宏定义:
#define NULL ((void *)0) 毫无疑问,NULL 是一个空指针常量。
有个问题,我们可以自定义NULL值吗?
事实上,NULL是标准库中的保留标识符,因此如果包含相应的标准头文件来引入NULL,那么在程序中将NULL重新定义为其他值(例如将NULL定义为3)。非法的。
一、空指针(null pointer)
1.空指针定义
如果将空指针常量转换为指针类型,则生成的指针(称为空指针)保证与任何对象或函数的指针比较不相等。
通过预备知识中空指针常量和NULL值的解释,我们可以知道:
只要将空指针常量赋给指针类型变量,该指针变量就是空指针。int *p;
p=0;
p=0L;
p=" ";
p=3 - 3;
p=0 * 17;
p=(无效*)0;
p=空;如上面的代码所示,在任何赋值操作之后,p都是空指针。并且,由系统保证空指针不指向任何实际的对象或函数。另一种方法是:任何对象或者函数的地址都不可能是空指针。
2.空指针的内存指向
该标准没有指定空指针在内存中指向的位置。也就是说,到底用0x0地址还是其他地址来表示空指针,取决于具体系统的实现。两种实现方式如下:
(1)零空指针(zero null pointer)这是一个常见的实现,即空指针内部用全0表示。
(2)非零空指针(nonzero null pointer)也有一些系统使用特殊的地址值或特殊的方法来表示空指针。
在实际编程中,我们不需要了解我们系统的空指针和内存指向的实现。我们只需要了解一个指针是否为空指针即可。 ——编译器会自动实现转换并使我们免受实现细节的影响。
3.空指针的使用
使用空指针主要是为了防止野指针和悬空指针。
防止野指针int *p=NULL;防止悬垂指针int *p=malloc(sizeof(int));
免费(p);
p=空; //有关null 的详细信息,请参阅下面的野指针和悬空指针。
二、野指针(wild pointer)
1.野指针概念
野指针:未初始化的指针
2.野指针产生原因
指针变量未初始化
以下程序:
int main()
{
整数*p;
printf(%p", p);
返回0;
这里的p没有初始化,它的默认值是随机的。
因此,当我们声明一个指针变量时,为了防止野指针的问题,我们可以将其初始化为NULL,即设置为空指针;我们也可以在初始化时确定指针。
如下图:
整数a=3;
int *p=a;
//或者
int *p=NULL;
整数*p=0;其中int *p=0 和int *p=NULL 都是常用的。
三、悬垂指针(dangling pointer)
1.悬垂指针概念
悬空指针:指向已释放的自由区内存(自由存储)的指针。
它与野指针的区别在于,悬空指针曾经有效,现在无效;但野指针从来都不是有效的。
2.悬垂指针产生原因
(1)指针指向的内存释放之后未置空
指针指向的内存被free或delete释放后,指针的值仍然是刚刚释放的内存的首地址,但此时指针已经失去了合法访问权限对该记忆的权利。
如下程序所示:
int main()
{
int *p=NULL;
p=malloc(sizeof(int));
*p=3;
printf("释放前,p=%p, *p=%dn", p, *p);
免费(p);
/* 注意此时p和p指向的内容并没有改变,只是释放内存后就丢失了。
堆上内存的合法操作*/
printf("释放后,p=%p, *p=%dn", p, *p);
返回0;
}程序输出:
释放前,p=0xe7a010,*p=3
free后,p=0xe7a010, *p=0 程序执行free(p)后,p是一个野指针。为了避免野指针可能带来的问题,我们应该在free(p) 之后添加:
p=空;此操作称为置空。
(2)指向同一块内存多个指针之一被释放
严格来说,这种情况和第一种情况是一样的。示例代码如下:
int *p=malloc(sizeof(int));
*p=3;
int *pd=p;
/* 当前p和pd指向同一个内存*/
免费(p);
p=空;
/* 释放p指向的内存,并将p设置为0 */从上面的代码可以看出,如果有两个指针指向同一个内存,一个指针被释放并置空后,另一个指针仍然指向对于释放的内存空间,这成为一个悬空指针。
这是一个典型的悬空指针示例,在实际生产环境中经常见到。
(3)指针操作超出变量生命周期
不要返回堆栈内存的指针或引用,因为堆栈内存在函数结束时被释放。
示例代码如下:
//定义一个函数
字符*getString()
{
char *p="你好世界!";
返回p;
}函数内部定义的字符串指针p指向的内容存放在栈中。当函数执行完退出时,这部分空间会被释放,返回的p指针就变成了悬空指针。
四、void指针(void pointer)
1.void指针概念
void 的意思是“无类型”,因此void指针也称为“无类型指针”。 void指针可以指向任何类型的数据,因此void指针一般称为通用指针或泛指针,也称为万能指针。
2.void指针的使用
(1)void指针变量p所指向的内容不能通过*p去访问
如果想通过void指针获取它所指向的变量的值,则需要先将void指针转换为与变量名类型匹配的数据类型指针,然后再继续,如下所示:
整数a=5;
无效*p=a;
printf("*p=%dn", *p);编译器会报错并提示:
不允许使用不完整的类型。
这意味着这个类型是不完整的,没有办法获取它所指向的变量的值。更改为:
printf("*p=%dn", *(int *)p);会成功的。
(2)void指针赋给其他类型的指针
常见的使用场景是:动态内存申请与释放。
首先,必须明确一件事。 C 语言和C++ 之间的某些语法实现存在差异。这里我们说的是C语言。
在C语言中,很自然地将void指针分配给任何其他类型的指针(函数指针除外,这将在下面讨论),并且不需要手动强制它;将任何其他类型的指针分配给void 指针也是很自然的。
例如:
类型定义结构{
.
.
复杂结构体;
//C语言正确的写法:
复杂结构=malloc(sizeof(复杂结构));
//C语言多余的写法:
//复杂结构=(复杂结构)malloc(sizeof(复杂结构));如果发现不手动强制传输,编译器会发出警告,请注意是否忘记添加malloc函数的头文件:
#include 因为在C语言中,编译器默认会将任何未定义的函数返回为int。因此,如果我们不添加stdlib.h头文件,则malloc函数默认返回int,因此我们会看到编译器警告。如果你看到的话,一不小心就会认为这两种类型不匹配,因为没有强制类型转换。其实编译器提示的是int * 和int 类型不匹配,而不是int * 和void * 类型不匹配!编译器警告
(3)void指针赋给函数指针
void 指针可以分配给除函数指针之外的所有指针。 malloc 函数就是一个例子。至于函数指针的问题,我们这里讨论一下。
Linux中有一个dlsym函数。函数声明如下:
void *dlsym(void *handle, const char *symbol); man手册中有一段示例代码:
int main()
{
.
无效*句柄;
双(*余弦)(双);
.
*(void **) (余弦)=dlsym(handle, "cos");
.
这里它想用一个函数指针来接收dlsym函数返回的void指针,但是它并没有使用下面两种更自然易懂的形式:
余弦=dlsym(句柄, "cos");
余弦=(double (*)(double)) dlsym(handle, "cos");这是因为C99标准的遗漏,导致void指针无法直接转换为函数指针。所以它使用了如下夸张的转换:
*(void **) (余弦)=dlsym(handle, "cos");首先将余弦的地址转换为二级指针,然后将其强制转换为(void **) void 二级指针,然后将指针通过运算符* 变成void 一级指针,这样左右类型就匹配了。
(4)其他类型的指针赋给void指针
void 指针可以用作泛型类型来接收任何数据类型指针。
void指针用于指向一个特定的地址,而不关心这个地址存储的是什么类型的数据。例如常见的memcpy等函数使用void*,函数原型如下:
好了,本文到此结束,如果可以帮助到大家,还望关注本站哦!
【深入解析C语言中的指针类型:空指针、野指针、悬垂指针与void指针】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
C语言中指针简直是个神级工具,不过要好好理解各种类型的指针,不然容易出错
有8位网友表示赞同!
我对空指针、野指针和悬垂指针的概念一直很模糊,这篇文章正好能帮我理清思路
有20位网友表示赞同!
终于可以解释一下为什么写代码时总是遇到莫名其妙的错误了!一定是这些指针搞的鬼吧
有17位网友表示赞同!
void指针真是挺神秘的,它能指向各种类型的数据,有点像万能钥匙的感觉
有11位网友表示赞同!
看这篇文章感觉指针的概念一点都不容易理解,还需要多加练习才能掌握
有10位网友表示赞同!
学习C语言的时候确实会被指针搞晕,还好现在有详细的文章解説了!
有13位网友表示赞同!
明白这几种指针的区别真的很关键,否则写出来的程序可能就很难调试了
有5位网友表示赞同!
感谢作者的讲解,让我对指针类型有了更全面了解!
有10位网友表示赞同!
希望可以找到一些练习题来巩固一下指针的概念
有5位网友表示赞同!
有时候代码出现问题,我总怀疑是指针的问题,现在看来确实有可能!
有11位网友表示赞同!
感觉学习指针就像是在解谜,需要不断思考和理解才能最终破解!
有9位网友表示赞同!
这篇文章写的非常清晰易懂,即使对指针不太了解的人也能看明白
有16位网友表示赞同!
指针真是C语言的核心概念之一,掌握好它可以写出更健壮的程序
有5位网友表示赞同!
我决定从今天开始每天都会练习一下指针,争取尽快把它们弄明白
有7位网友表示赞同!
这篇文章让我对C语言的深入理解有了新的认识
有9位网友表示赞同!
学习指针是编程必经之路,感谢老师和作者的帮助
有11位网友表示赞同!
我发现自己还有很多地方需要学习关于指针的地方!
有10位网友表示赞同!
以后在写代码的时候一定要更加谨慎地使用指针,避免出现错误
有9位网友表示赞同!
这篇文章是我学习C语言以来见过最棒的解释文章之一!
有5位网友表示赞同!
我要把这篇文章分享给我的朋友们,让他们也能更好地理解指针的概念
有15位网友表示赞同!