【C/C++】联合union的用法和说明

    看了几篇c语言中union的文章,整理记录一下

    ============分=======隔=======线============

    1、什么是联合?

    “联合”是一种特殊的类,也是一种构造类型的数据结构。在一个“联合”内可以定义多种不同的数据类型, 一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,已达到节省空间的目的(还有一个节省空间的类型:位域)。 这是一个非常特殊的地方,也是联合的特征。另外,同struct一样,联合默认访问权限也是公有的,并且,也具有成员函数。



    2、联合与结构的区别?

    “联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和(空结构除外,同时不考虑边界调整)。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。

    结构变量可以作为函数参数,函数也可返回指向结构的指针变量。而联合变量不能作为函数参数,函数也不能返回指向联合的指针变量。但可以使用指向联合变量的指针,也可使用联合数组。

    3、匿名联合

    匿名联合仅仅通知编译器它的成员变量共同享一个地址,而变量本身是直接引用的,不使用通常的点号运算符语法.在C++中被支持,编译时需要使用g++,使用gcc进行编译会提示"warning: unnamed struct/union that defines no instances"。

    #include <stdio.h>
    void main()
    {
        union{
            int i;
            char c;
        };
        i = 5;
        c = 'a';
        printf("%d %c", i, c);
    }

    4、联合使用注意点

    联合里面的东西共享内存,所以静态、引用都不能用,因为他们不可能共享内存。

    联合里不允许存放带有构造函数、析够函数、复制拷贝操作符等的类,因为他们共享内存,编译器无法保证这些对象不被破坏,也无法保证离开时调用析够函数。

    使用联合可以节省内存空间,但是也有一定的风险:通过一个不适当的数据成员获取当前对象的值!为了防止这样的错误,我们必须定义一个额外的对象,来跟踪当前被存储在联合中的值的类型,我们称这个额外的对象为:union的判别式。一个比较好的经验是,在处理作为类成员的union对象时,为所有union数据类型提供一组访问函数。

    以上部分整理自C / C++ 中union的使用.详解.实例

    ============分=======隔=======线============

    在C/C++程序的编写中,当多个基本数据类型或复合数据结构要占用同一片内存时,我们要使用联合体;当多种类型,多个对象,多个事物只取其一时(我们姑且通俗地称其为“n 选1”),我们也可以使用联合体来发挥其长处。首先看一段代码:

    union myun
    {
     struct { int x; int y; int z; }u;
     int k;
    }a;
    int main()
    {
     a.u.x =4;
     a.u.y =5;
     a.u.z =6;
     a.k = 0;
     printf("%d %d %d\n",a.u.x,a.u.y,a.u.z);
     return 0;
    }

    union类型是共享内存的,以size最大的结构作为自己的大小,这样的话,myun这个结构就包含u这个结构体,而大小也等于u这个结构体的大小,在内存中的排列为声明的顺序x,y,z从低到高,然后赋值的时候,在内存中,就是x的位置放置4,y的位置放置5,z的位置放置6,现在对k赋值,对k的赋值因为是union,要共享内存,所以从union的首地址开始放置,首地址开始的位置其实是x的位置,这样原来内存中x的位置就被k所赋的值代替了,就变为0了,这个时候要进行打印,就直接看内存里就行了,x的位置也就是k的位置是0,而y,z的位置的值没有改变,所以应该是0,5,6

    再看两个试题:

    试题一:编写一段程序判断系统中的CPU 是Little endian 还是Big endian 模式?

    分析:

    作为一个计算机相关专业的人,我们应该在计算机组成中都学习过什么叫Little endian 和Big endian。Little endian 和Big endian 是CPU 存放数据的两种不同顺序。对于整型、长整型等数据类型,Big endian 认为第一个字节是最高位字节(按照从低地址到高地址的顺序存放数据的高位字节到低位字节);而Little endian 则相反,它认为第一个字节是最低位字节(按照从低地址到高地址的顺序存放数据的低位字节到高位字节)。

    例如,假设从内存地址0x0000 开始有以下数据:
    0x12 0x34 0xab 0xcd

    如果我们去读取一个地址为0x0000 的四个字节变量,若字节序为big-endian,则读出结果为0x1234abcd;若字节序位little-endian,则读出结果为0xcdab3412。如果我们将0x1234abcd 写入到以0x0000 开始的内存中,则Little endian 和Big endian 模式的存放结果如下:
    地址               0x0000 0x0001 0x0002 0x0003
    big-endian         0x12   0x34   0xab   0xcd
    little-endian      0xcd   0xab   0x34   0x12

    一般来说,x86 系列CPU 都是little-endian 的字节序,PowerPC 通常是Big endian,还有的CPU 能通过跳线来设置CPU 工作于Little endian 还是Big endian 模式。

    解答:

    显然,解答这个问题的方法只能是将一个字节(CHAR/BYTE 类型)的数据和一个整型数据存放于同样的内存
    开始地址,通过读取整型数据,分析CHAR/BYTE 数据在整型数据的高位还是低位来判断CPU 工作于Little
    endian 还是Big endian 模式。得出如下的答案:

    typedef unsigned char BYTE;
    int main(int argc, char* argv[])
    {
    unsigned int num,*p;
    p = &amp;num;
    num = 0;
    *(BYTE *)p = 0xff;
    if(num == 0xff)
    {
    printf("The endian of cpu is little\n");
    }
    else //num == 0xff000000
    {
    printf("The endian of cpu is big\n");
    }
    return 0;
    }
    

    除了上述方法(通过指针类型强制转换并对整型数据首字节赋值,判断该赋值赋给了高位还是低位)外,还有没
    有更好的办法呢?我们知道,union 的成员本身就被存放在相同的内存空间(共享内存,正是union 发挥作用、做贡献的去处),因此,我们可以将一个CHAR/BYTE 数据和一个整型数据同时作为一个union 的成员,得出
    如下答案:

    int checkCPU()
    {
     {
      union w
     {
      int a;
      char b;
     } c;
     c.a = 1;
     return (c.b == 1);
     }
    }

    实现同样的功能,我们来看看Linux 操作系统中相关的源代码是怎么做的:

    static union { char c[4]; unsigned long mylong; } endian_test = {{ 'l', '?', '?', 'b' } };
    #define ENDIANNESS ((char)endian_test.mylong)

    Linux 的内核作者们仅仅用一个union 变量和一个简单的宏定义就实现了一大段代码同样的功能!由以上一段代码我们可以深刻领会到Linux 源代码的精妙之处!(如果ENDIANNESS=’l’表示系统为little endian,为'b’表示big endian )

    试题二:假设网络节点A 和网络节点B 中的通信协议涉及四类报文,报文格式为“报文类型字段+报文内容的结构体”,四个报文内容的结构体类型分别为STRUCTTYPE1~ STRUCTTYPE4,请编写程序以最简单的方式组
    织一个统一的报文数据结构。

    分析:

     报文的格式为“报文类型+报文内容的结构体”,在真实的通信中,每次只能发四类报文中的一种,我们可以将四类报文的结构体组织为一个union(共享一段内存,但每次有效的只是一种),然后和报文类型字段统一组织成一个报文数据结构。

    解答:

    根据上述分析,我们很自然地得出如下答案:

    typedef unsigned char BYTE;
    //报文内容联合体
    typedef union tagPacketContent
    {
    STRUCTTYPE1 pkt1;
    STRUCTTYPE2 pkt2;
    STRUCTTYPE3 pkt1;
    STRUCTTYPE4 pkt2;
    }PacketContent;
    //统一的报文数据结构
    typedef struct tagPacket
    {
    BYTE pktType;
    PacketContent pktContent;
    }Packet;

    以上部分转自C语言中的联合体union



    本博客所有文章如无特别注明均为原创。
    复制或转载请以超链接形式注明转自枫芸志,原文地址《【C/C++】联合union的用法和说明
    标签:
    分享:

还没有人抢沙发呢~

无觅相关文章插件,快速提升流量