void、void*以及NULL
void、void*以及NULL
写在前面
在使用C++的过程中,void和NULL用到的频率挺高的,但是从来没有去探索过这两个关键字的联系和区别,也没有对它们做更多的探索。对于void*,说实话,实际应用中貌似没有用到过这个东西。那这三者到底是什么呢?应该怎么用呢?
void
void是指无类型。我们可以把它理解为“不存在”
我们在写代码的时候,用到void的地方无非两个:
1、函数没有返回值的时候,将函数的返回类型声明为void
如:void f(int a);
在C语言中,如果一个函数没有写返回类型,编译器默认这种函数的返回类型是整型,而不是void.
2、函数没有参数的时候,在参数列表中注明void
如:int getSum(void),当然也可以写成int getSum()。对编译器而言,这两种形式都没有区别,如果在程序中同时声明这两种形式的话,编译器不会视为函数重载,而是会报重复声明的错误。尽管对于上述两种参数类型为空的声明,编译器的处理都相同,但是为了让程序具有良好的可读性,同时也为了满足编程规范的要求,还是加上void为好。
接下来我们试着定义一个void类型的变量:
void a;
编译器会报错:error C2182: “a”: 非法使用“void”类型
也就是说,void是没有类型的,如果定义这样的变量,编译器并不知道应该给这个变量开辟多大的空间,因此只能报错。
void*
说实话,目前为止,除了在写一些内存拷贝、移动相关的样例函数时用过void*,其他时候一次都没用过。(事实上void*的就是在内存复制、内存拷贝这些场景中用到的)void*看起来看起来比较神秘而高冷啊。它到底是个啥呢?
既然void是无类型,那么很自然的推理出,void*就是无类型指针。
在程序中定义一个void*的变量:
void *p;
程序可以通过编译。我们可以输出sizeof(p),其结果为4.这很容易理解,因为p是一个指针,只是它指向的对象的类型是未知的,在win32中,指针占4个字节,因此sizeof(p) = 4.我们可以打印出p的地址,我们甚至可以直接打印出p指向的对象的地址,尽管此时并不知道p指向的地址里面放的是什么:
cout<<"sizeof p = "<<sizeof(p)<<endl;
cout<<"&p = "<<&p<<endl;
cout<<"p = "<<p<<endl;//这句可能会在执行时出现异常,这是可以使用Release模式

void*是无类型的,也就是说,它可以是任意类型的,我们可以把任意类型的指针赋给void类型的指针。
下面这段代码声明了一个double类型的指针pb,然后将pb赋给p。
void *p;
double b = 0.2;
double *pb = &b;
cout<<"Befor initialization"<<endl;
cout<<"p = "<<p<<endl;
cout<<"pb = "<<pb<<endl;
cout<<"After initialization"<<endl;
p = pb;
cout<<"p = "<<p<<endl;

反过来,如果把无类型指针p赋给double型指针pb,
pb = p;
则无法通过编译,报错:无法从“void *”转换为“double *”,如果需要进行这类的转换,必须进行强制类型转换:
pb = (double *)p;
此外,对于有明确类型的指针,比如int *,我们可以直接操作指针,对指针做++、+=操作,如下:
int a = 2;
int *pa = &a;
cout<<"pa = "<<pa<<endl;
pa++;
cout<<"After pa++"<<endl;
cout<<"pa = "<<pa<<endl;

可以看到,指针移动了4个字节。如果是double类型的指针,则做++操作后移动的是8个字节。字符型指针比较奇特,直接看代码和结果:
char *pa = "abc";
cout<<"pa = "<<pa<<endl;
pa++;
cout<<"After pa++"<<endl;
cout<<"pa = "<<pa<<endl;

输出的是指向的内容。(这里不再继续扩展了,感觉有要跑偏了。。),我想说的是,对于void * ,不能进行++ 操作。很明显,对int*, dobule* 做++操作时,指针移动的大小是其指向对象的大小,而我们已经知道void*指向的对象是未知的,因此无法进行指针的下移,如果对void*做++操作,会报错: error C2036: “void *”: 未知的大小
NULL
NULL字面意思是“空”,也就是啥都没有,它通常表示空值,无结果,或是空集合,其ASCII码是0(十进制),我们可以在程序中输出NULL的值,如下:
cout<<"NULL = "<<NULL<<endl;

我们可以直接转到NULL的声明,然后stdio.h就会被打开,同时鼠标将被聚焦到下图中的代码上。

由此可以看到,NULL实际上是一个宏定义,在C++中,它被替换成0,而在C中,它被替换成一个无类型指针,且值为0.
因此我们把NULL赋给任意类型的指针,如下:
int t = 9;
int *a = &t;
cout<<"Before a = NULL"<<endl;
cout<<"a = "<<a<<endl;
a = NULL;
cout<<"After a = NULL"<<endl;
cout<<"a = "<<a<<endl;
在这段代码中,我们把NULL赋给了int型的指针a,这样a指向的对象就变成了0,这中做法称为指针的悬空。这时候a已经不指向向任何有效存储区,在指针初始化和指针delete之后,都会这么做。

总结
越是探索C++中的一些细节,就越是觉得自己学得很粗浅。。。但近来能够静下心来慢慢看看这些细微的东西,感觉还是挺受益的,无论以后使用什么语言进行编程,都能够静下心来,多多思考,坚持下去,总是好的。
void、void*以及NULL的更多相关文章
- void指针和NULL指针
Void指针和NULL指针 Void指针: Void指针我们称之为通用指针,就是可以指向任意类型的数据.也就是说,任何类型的指针都可以赋值给Void指针. 举例: #include<stdio. ...
- invalid conversion from 'void* (*)()' to 'void* (*)(void*)'
void *thread1() ], NULL, thread1, NULL)) != ) 提示:invalid conversion from 'void* (*)()' to 'void* (*) ...
- 深刻理解void,void*和sizeof关键字
void的字面值是“无类型”,void*则是"无类型指针".void*可以指向任何类型的数据.void几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个 ...
- 通过qsort(void * lineptr[], int left, int rifht, int (*comp)(void *, void *))解读指针函数和void指针
原函数是<The C programint language >5.11文本行排序的程序,如下: void qsort(void *v[], int left, int right, i ...
- 简述static关键字、void与void *(void指针)、函数指针
static关键字1.修饰局部变量,延长局部变量的生命周期.使变量成为静态局部变量,在编译时就为变量分配内存,直到程序退出才释放存储单元.2.修饰全局变量,限制全局变量的使用范围为本文件中.全局变量默 ...
- js & void() & void(0)
js & void() & void(0) https://www.runoob.com/js/js-void.html void() <a href="javascr ...
- void指针、NULL指针和未初始化指针
一个指针可以被声明为void类型,比如void *x.一个指针可以被赋值为NULL.一个指针变量声明之后但没有被赋值,叫做未初始化指针. 1 2 3 4 5 6 7 8 9 10 11 12 13 1 ...
- void void*
void类型及void指针 1.概述 许多初学者对C/C 语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误.本文将对void关键字的深刻含义进行解说,并 详述void及void指 ...
- void与NULL详解
void 是 “空”类型(无值型),意思是这种类型的大小无法确定. 并不存在void类型的对象,所以也就不能声明void类型的对象或者将sizeof()运算符用于void类型,C++/C语言不能对一个 ...
随机推荐
- (转)java性能调优
本文转自:http://blog.csdn.net/lilu_leo/article/details/8115612 一.类和对象使用技巧 1.尽量少用new生成新对象 用new创建类的实例时,构造雨 ...
- 【问题】和NULL比较遇到的问题
1.问题描述: select FName from teacher where FId not in( select distinct FTeacherId from student ) 子查询返回的 ...
- 【原创】Sliding Window Maximum 解法分析
这道题是lintcode上的一道题,当然leetcode上同样有. 本题需要寻找O(N)复杂度的算法. 解体思路比较有特点,所以容易想到参考 最小栈 的解题办法. 但是最小栈用栈维护最小值很直观,这道 ...
- Android HandlerThread 使用
HandlerThread 继承了 Thread,添加了 looper,queue 的支持.可以为 Handler 提供线程服务,并可对 执行的任务进行简单的管理. Handler 默认工作在主线程, ...
- Zookeeper核心机制
(如果感觉有帮助,请帮忙点推荐,添加关注,谢谢!你的支持是我不断更新文章的动力.本博客会逐步推出一系列的关于大型网站架构.分布式应用.设计模式.架构模式等方面的系列文章) Zookeeper是Hado ...
- IOS自带json解析类解析json
- (IBAction)test:(id)sender { NSString *result = @"{\"code\":\"S00000\",\&q ...
- struts2中利用POI导出Excel文档并下载
1.项目组负责人让我实现这个接口,因为以前做过类似的,中间并没有遇到什么太困难的事情.其他不说,先上代码: package com.tydic.eshop.action.feedback; impor ...
- BZOJ 1297 迷路(矩阵)
题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1297 题意:给出一个带权有向图,权值为1-9,顶点个数最多为10.从1出发恰好在T时刻到 ...
- Android开发之单例模式
参考:http://blog.csdn.net/guolin_blog/article/details/8860649 http://www.cnblogs.com/liyiran/p/5283690 ...
- ie下jquery ajax 80020101错误的解决方法
<script language="javascript"> <!-- function checkAll(name,isCheck){ ...