C语言之联合(union)的妙用

https://blog.csdn.net/debugzzj/article/details/81705755
相信学过C语言的程序员对C语言的各种数据类型都非常熟悉,像数组、结构体、结构体数组、结构体指针这些数据类型大家都会信手捏来。然而,有些数据类型被我们不经意的边缘化了。它们没有数组、结构体这些数据类型用的广泛,但是却不容忽视,例如,枚举、联合等。
 
今天我们就讨论一下union(联合)这个数据类型。C语言的发明者为什么要弄一个这种不常用甚至几乎不用的数据类型呢。下面给你答案。首先,看一下union的定义。

union Data {
  int i;
  char str[4];
} data; 

它有什么属性呢? 这段代码定义了一个名为data的union变量。它有什么属性呢?

1.这个变量在内存中占用4个字节的空间而不是8个;
2.有两个数据成员:int类型变量的i和char类型的数组str;
3.虽然有两个数据成员,但是这两个成员的存储空间是一样的。

上面三点是union变量的最基本也是最重要的属性。详细说一下第三点。因为union不论包含多少个多少种数据类型,它实例化为变量后,这个变量的长度是这个union中最长的数据类型的长度。下面的代码定义了一个union变量。它的长度是16个字节。

 union DEMO{ 
    char status;
    int a; 
    int serial[4]; 
}demo;

C语言之联合(union)的妙用

上面这个表格代表内存中的数据,demo.status 的值为0x1;demo.a的值为,0x4321。以此类推demo.serial的值就出来了。这个属性是union特有的。
C语言的发明者为什么要弄这么个数据类型呢?现在知道了吧。因为这个union特有的属性可以帮助我们节省好多行代码。而且还不用各种基础数据类型转换。

union的应用

设想用C语言实现这样一个功能。我需要用单片机读取一个监控温度的i2c slave的寄存器数据。这个寄存器是12位有效位寄存器。读出来之后我们要通过数据手册给定的公式计算成实际温度(设想这个公式为 temp = reg_val *10)。我们怎么实现呢?要知道,i2c的数据传输是按照byte传送的,也就是说,你只能用char类型结束数据,说白了,每个时序你只能接收8个bit的数据。所以12个bit需要读两次,用两个char类型变量或一个char类型数据接收。

  1. 读出寄存器数据(这个不在这篇文章的讨论范围内)
  2. 将读出的数据转换成可计算的数据类型(两个char类型转换成一个short或int或float类型)
  3. 根据公式计算

下面看一下不用union实现的函数

int fun( void )
{
    int tmp_value = 0; 
    char reg_val[2] = {0,0}; 
     .... 
    i2c.read(addr<<1, reg_val, 2); 
    tmp_value = (reg_val[1]<<8 | reg_val[0]); 
 
    return tmp_value*10; 
}

用union的

union REG_VAL { 
    int value; 
    char buf[2]; 
}reg_val; 
 
int fun( void )
{ 
    int tmp_value = 0; 
    char reg_val[2] = {0,0}; 
    .... 
    i2c.read(addr<<1, reg_val.buf, 2);
 
    return reg_val.value*10; 
}

不用union的函数也可以用sprintf实现,那就更麻烦了。

可以看到虽然在这里用union的代码比不用union的多了几行,但是i2c sensor如果多的话,那就会少很多,而且i2c sensor的寄存器有效位数不是一样的,这个用两个char类型就解决了,但是其他的可能需要用三个,所以用最上面定义的union变量可以很好的实现,不需要考虑各种转换问题。

union还是很有用处的,所以不要忽视它了。

上一篇:索引失效场景,sql查询优化


下一篇:SQL注入之基础知识及手工注入