• 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模式
    

    enter description here

    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;
    

    enter description here

    反过来,如果把无类型指针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;
    

    enter description here
    可以看到,指针移动了4个字节。如果是double类型的指针,则做++操作后移动的是8个字节。字符型指针比较奇特,直接看代码和结果:

        char *pa = "abc";
    
        cout<<"pa = "<<pa<<endl;
    
        pa++;
        cout<<"After pa++"<<endl;
        cout<<"pa = "<<pa<<endl;
    

    enter description here
    输出的是指向的内容。(这里不再继续扩展了,感觉有要跑偏了。。),我想说的是,对于void * ,不能进行++ 操作。很明显,对int*, dobule* 做++操作时,指针移动的大小是其指向对象的大小,而我们已经知道void*指向的对象是未知的,因此无法进行指针的下移,如果对void*做++操作,会报错: error C2036: “void *”: 未知的大小

    NULL

    NULL字面意思是“空”,也就是啥都没有,它通常表示空值,无结果,或是空集合,其ASCII码是0(十进制),我们可以在程序中输出NULL的值,如下:

       cout<<"NULL = "<<NULL<<endl;
    

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

    由此可以看到,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之后,都会这么做。

    enter description here

    总结

    越是探索C++中的一些细节,就越是觉得自己学得很粗浅。。。但近来能够静下心来慢慢看看这些细微的东西,感觉还是挺受益的,无论以后使用什么语言进行编程,都能够静下心来,多多思考,坚持下去,总是好的。

     

  • 相关阅读:
    B-树和B+树
    线程与内核对象的同步-2
    线程与内核对象的同步
    高级线程同步 临界区
    Levenshtein Distance (编辑距离) 算法详解
    平衡二叉树
    静态查找表
    C++中的容器类详解
    How do I list all tables/indices contained in an SQLite database
    SmartGit STUDY 2
  • 原文地址:https://www.cnblogs.com/scut-linmaojiang/p/5297891.html
Copyright © 2020-2023  润新知