union理解与妙用

来源:互联网 发布:透明计算造假 知乎 编辑:程序博客网 时间:2024/06/05 08:01

最近在看PCL中关于自定义PointT类型的文档,发现其中广泛使用了union,以前学习的时候用不到这个,也就没有留心,借此机会学习下union的使用方法,理解下union的本质。

一 struct和union内存结构

我们先来简单看下struct的内存结构,如下定义一个结构体:

struct student{    char mark;    long num;    float score;};

由于内存对齐的存在,这个student结构体的内存结构如下:
这里写图片描述
虽然mark理论上只有一个字节,但是却会占用4个字节的空间。
下面定义一个与student数据成员一样的union:

union union_student{    char mark;    long num;    float score;};

我们发现sizeof(union_student)的值为4,这就是union和struct的区别了。有时候,我们需要几种不同类型的变量存在在同一段的内存空间中,就像上面的,我们需要将一个char类型的mark、一个long类型的num变量和一个float类型的score变量存放在同一个地址开始的内存单元中。上面的三个变量,char类型和long类型所占的内存字节数是不一样的,但是在union中,它们都是从同一个地址存放的,也就是使用的覆盖技术,这三个变量互相覆盖,而这种使几个不同的变量共占同一段内存的结构,称为“共用体”类型的结构,也称“联合体”,也就是我们要说的union了,内存结构如下:
这里写图片描述
由上面,我们大致可以看出:结构体struct所占用的内存为各个成员的占用的内存之和(当然也需要考虑内存对齐的问题了)。而对于union来说,union变量所占用的内存长度等于最长的成员的内存长度,同样需要考虑内存对齐的问题。

二 union的使用

和struct一样,union只有先定义了共用体变量才能引用它。而且不能直接引用共用体变量,而只能引用共用体变量中的成员。
同时,在使用union的时候,还需要注意以下的几点:
1.同一个内存段可以用来存放几种不同类型的成员,但在每一个时刻只能存在其中一种,而不是同时存放几种。也就是说,每一瞬间只有一个成员起作用,其它的成员不起作用,即不是同时都存在和起作用。
2.共用体变量中起作用的成员是最后一个存放的成员,在存入一个新的成员后,原有的成员就失去作用。比如以下的代码:

union_student a;// cout<<a<<endl; // wronga.mark = 'b';cout << a.mark << endl; // 输出'b'cout << a.num << endl; // 98 字符'b'的ACSII值cout << a.score << endl; // 输出错误值a.num = 10;cout << a.mark << endl; // 输出空cout << a.num << endl; // 输出10cout << a.score << endl; // 输出错误值a.score = 10.0;cout << a.mark << endl; // 输出空cout << a.num << endl; // 输出错误值cout << a.score << endl; // 输出10

3.由于union中的所有成员起始地址都是一样的,所以&a.mark、&a.num和&a.score的值都是一样的。
4.union类型可以出现在结构体类型定义中,也可以定义union数组,反之,结构体也可以出现在union类型定义中,数组也可以作为union的成员。

三 union + 对象

当union遇到了C++中的对象时,一切又变得复杂起来。上面总结的union使用法则,在C++中依然适用。本来union本就是从C语言中的,如果我们在C++中继续按照C语言的那种方式使用union,那是没有问题的。如果我们在union中放一个类的对象呢?结果会怎么样?比如有以下代码:

#include <iostream>using namespace std;class CA{     int m_a;};union Test{     CA a;     double d;};int main(){     return 0;}

可以看到,没有问题;如果我们在再类CA中添加了构造函数,或者添加析构函数,我们就会发现程序就会出现错误。由于union里面的东西共享内存,所以不能定义静态、引用类型的变量。由于在union里也不允许存放带有构造函数、析构函数和复制构造函数等的类的对象,但是可以存放对应的类对象指针。编译器无法保证类的构造函数和析构函数得到正确的调用,由此,就可能出现内存泄漏。所以,我们在C++中使用union时,尽量保持C语言中使用union的风格,尽量不要让union带有对象。

四 理解PointT

我们先看一段代码:

#define PCL_ADD_UNION_NORMAL4D \  union EIGEN_ALIGN16 { \    float data_n[4]; \    float normal[3]; \    struct { \      float normal_x; \      float normal_y; \      float normal_z; \    }; \  };

这是PCL中定义normal(法向量)的源码。有了上面的知识,我们在来理解这个应该就没有问题了,其中:

  • data_n[4] 为了内存对齐设计的
  • normal[3] 和struct 里面的内容是主角,normal[3]方便我们进行整体的操作,而struct里面的内容则有助于我们访问normal中的各个分量,虽然直接normal[i]也可以,但是没有normal_x,normal_y,normal_z直观。这样的定义也有助于我们更好的理解代码。
1 0