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. hdu 4465 Candy

    题解: 由题意得 需要运用: C(m,n)=exp(logC(m,n)) f[]=; ; i<=; i++) f[i]=f[i-]+log(i*1.0); double logC(int m,i ...

  2. s.charAt()

    public class ish{public static void main(String[]args){ String s="call me ishmae";System.o ...

  3. JavaScript基础精华03(String对象,Array对象,循环遍历数组,JS中的Dictionary,Array的简化声明)

    String对象(*) length属性:获取字符串的字符个数.(无论中文字符还是英文字符都算1个字符.) charAt(index)方法:获取指定索引位置的字符.(索引从0开始) indexOf(‘ ...

  4. Linux卸载系统自带的httpd的方法

    卸载linux自带的httpd服务: 方法一: #rpm -e httpd 结果,出现以下错误 httpd-mmn = 20020628 is needed by (installed) mod_pe ...

  5. 机器学习 —— 概率图模型(Homework: Exact Inference)

    在前三周的作业中,我构造了概率图模型并调用第三方的求解器对器进行了求解,最终获得了每个随机变量的分布(有向图),最大后验分布(双向图).本周作业的主要内容就是自行编写概率图模型的求解器.实际上,从根本 ...

  6. linux下文件夹的创建、复制、剪切、重命名、清空和删除命令

    在home目录下有wwwroot目录,wwwroot下有sinozzz目录,即/home/wwwroot/sinozzz 一.目录创建 在/home/wwwroot目录下新建一个sinozzz123的 ...

  7. VS2005控制台程序修改nb0文件

    VS2005控制台程序修改nb0文件 我们要实现的功能就是通过CMD传递进来的值来在nb0文件末尾增加版本信息,新建控制台程序,自动生成的main函数如下,默认的代码非常简单: int _tmain( ...

  8. Codeforces 672

    题目链接:http://codeforces.com/contest/672/problem A. Summer Camp(打表) 题意:123456789...一串字符串,问第n个是什么数字. 塞一 ...

  9. float label 提示

    很多时候,我们写input 都会添加 placeholder 属性,用于提示用户这里该输入什么,怎么输入,但是当用户一旦输入了字符串,该提示就会消失,相信会有人,输入内容后可能会忘记这里要输入的是什么 ...

  10. 进程间通信的WM_COPYDATA的使用

    http://blog.csdn.net/ao929929fei/article/details/6316174 接收数据的一方 ON_WM_COPYDATA() afx_msg BOOL OnCop ...