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的更多相关文章

  1. void指针和NULL指针

    Void指针和NULL指针 Void指针: Void指针我们称之为通用指针,就是可以指向任意类型的数据.也就是说,任何类型的指针都可以赋值给Void指针. 举例: #include<stdio. ...

  2. invalid conversion from 'void* (*)()' to 'void* (*)(void*)'

    void *thread1() ], NULL, thread1, NULL)) != ) 提示:invalid conversion from 'void* (*)()' to 'void* (*) ...

  3. 深刻理解void,void*和sizeof关键字

    void的字面值是“无类型”,void*则是"无类型指针".void*可以指向任何类型的数据.void几乎只有"注释"和限制程序的作用,因为从来没有人会定义一个 ...

  4. 通过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 ...

  5. 简述static关键字、void与void *(void指针)、函数指针

    static关键字1.修饰局部变量,延长局部变量的生命周期.使变量成为静态局部变量,在编译时就为变量分配内存,直到程序退出才释放存储单元.2.修饰全局变量,限制全局变量的使用范围为本文件中.全局变量默 ...

  6. js & void() & void(0)

    js & void() & void(0) https://www.runoob.com/js/js-void.html void() <a href="javascr ...

  7. void指针、NULL指针和未初始化指针

    一个指针可以被声明为void类型,比如void *x.一个指针可以被赋值为NULL.一个指针变量声明之后但没有被赋值,叫做未初始化指针. 1 2 3 4 5 6 7 8 9 10 11 12 13 1 ...

  8. void void*

    void类型及void指针 1.概述 许多初学者对C/C 语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误.本文将对void关键字的深刻含义进行解说,并 详述void及void指 ...

  9. void与NULL详解

    void 是 “空”类型(无值型),意思是这种类型的大小无法确定. 并不存在void类型的对象,所以也就不能声明void类型的对象或者将sizeof()运算符用于void类型,C++/C语言不能对一个 ...

随机推荐

  1. (转)java性能调优

    本文转自:http://blog.csdn.net/lilu_leo/article/details/8115612 一.类和对象使用技巧 1.尽量少用new生成新对象 用new创建类的实例时,构造雨 ...

  2. 【问题】和NULL比较遇到的问题

    1.问题描述: select FName from teacher where FId not in( select distinct FTeacherId from student ) 子查询返回的 ...

  3. 【原创】Sliding Window Maximum 解法分析

    这道题是lintcode上的一道题,当然leetcode上同样有. 本题需要寻找O(N)复杂度的算法. 解体思路比较有特点,所以容易想到参考 最小栈 的解题办法. 但是最小栈用栈维护最小值很直观,这道 ...

  4. Android HandlerThread 使用

    HandlerThread 继承了 Thread,添加了 looper,queue 的支持.可以为 Handler 提供线程服务,并可对 执行的任务进行简单的管理. Handler 默认工作在主线程, ...

  5. Zookeeper核心机制

    (如果感觉有帮助,请帮忙点推荐,添加关注,谢谢!你的支持是我不断更新文章的动力.本博客会逐步推出一系列的关于大型网站架构.分布式应用.设计模式.架构模式等方面的系列文章) Zookeeper是Hado ...

  6. IOS自带json解析类解析json

    - (IBAction)test:(id)sender { NSString *result = @"{\"code\":\"S00000\",\&q ...

  7. struts2中利用POI导出Excel文档并下载

    1.项目组负责人让我实现这个接口,因为以前做过类似的,中间并没有遇到什么太困难的事情.其他不说,先上代码: package com.tydic.eshop.action.feedback; impor ...

  8. BZOJ 1297 迷路(矩阵)

    题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1297 题意:给出一个带权有向图,权值为1-9,顶点个数最多为10.从1出发恰好在T时刻到 ...

  9. Android开发之单例模式

    参考:http://blog.csdn.net/guolin_blog/article/details/8860649 http://www.cnblogs.com/liyiran/p/5283690 ...

  10. ie下jquery ajax 80020101错误的解决方法

    <script language="javascript">    <!--    function checkAll(name,isCheck){       ...