union理解与妙用

6 篇文章 1 订阅

最近在看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; // wrong
a.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; // 输出10
cout << 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直观。这样的定义也有助于我们更好的理解代码。
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值