谁会何时使用工会?是仅C语言时代的残余吗?


133

我学到了,但并没有真正得到工会。我遍历的每一个C或C ++文本都对它们进行了介绍(有时会进行介绍),但是它们却很少给出为什么或在何处使用它们的实际示例。工会在现代(甚至是传统)案例中什么时候有用?我仅有的两个猜测是,当您的工作空间非常有限时,或者当您开发API(或类似的东西)时,您想强迫最终用户在多个对象/类型中只有一个实例时,对微处理器进行编程一度。这两个猜测是否接近正确?


31
C / C ++不是语言。联合在C中适度有用,而在C ++中则无济于事。正确地说,它们在C ++中是“基于C的C ++的残留物”,而不是说它们是“仅C的残留物”,就好像C ++取代了C.
R .. GitHub停止帮助ICE,

12
您能否详细说明用c ++代替工会,或者为什么在c ++中工会无效?
罗素

3
C ++替代联合的方法是类和继承-C中的联合几乎专门用于类型安全的多态性。某些课程比较擅长。(有关C型多态性,请参见vz0的答案)
tobyodavies

6
@R ..:联合在C ++中仍然有用。请参阅下面的答案。
迈克尔

2
工会在操作系统的内胆中,或在例如组装/反汇编声音文件的包中,可能是非常有价值的。在这种情况下,可以通过多种不同方式使用它们-数据/字节序转换,低级多态性等。是的,对于相同的问题,还有其他解决方案(主要是在指针类型之间进行转换),但是联合通常更干净,更好地自我记录。
Hot Licks 2014年

Answers:


105

联合通常与鉴别公司一起使用:一个变量,指示联合的哪些字段有效。例如,假设您要创建自己的Variant类型:

struct my_variant_t {
    int type;
    union {
        char char_value;
        short short_value;
        int int_value;
        long long_value;
        float float_value;
        double double_value;
        void* ptr_value;
    };
};

然后,您将使用它,例如:

/* construct a new float variant instance */
void init_float(struct my_variant_t* v, float initial_value) {
    v->type = VAR_FLOAT;
    v->float_value = initial_value;
}

/* Increments the value of the variant by the given int */
void inc_variant_by_int(struct my_variant_t* v, int n) {
    switch (v->type) {
    case VAR_FLOAT:
        v->float_value += n;
        break;

    case VAR_INT:
        v->int_value += n;
        break;
    ...
    }
}

这实际上是一个非常常见的习惯用法,尤其是在Visual Basic内部。

有关真实示例,请参见SDL的SDL_Event联合。(此处为实际源代码)。type联合的顶部有一个字段,并且在每个SDL_ * Event结构上重复相同的字段。然后,要处理正确的事件,您需要检查该type字段的值。

好处很简单:只有一种数据类型可以处理所有事件类型,而无需使用不必要的内存。


2
大!在那种情况下,我现在想知道为什么Sdl函数不只是实现为类层次结构。是为了使其与C兼容,而不仅仅是与C ++兼容?
罗素

12
@Russel C ++类不能在C程序中使用,但是可以使用'extern“ C”块从C ++轻松访问C结构/联合。
vz0 2011年

1
这种不同的模式也常常用于编程语言的翻译,如定义struct objectgithub.com/petermichaux/bootstrap-scheme/blob/v0.21/scheme.c
亚当罗森菲尔德

1
很棒的解释。我一直都知道工会是什么,但从未见过现实世界中为什么有人会疯狂使用工会的原因:)感谢示例。
riwalk 2011年

@ Stargazer712,谷歌的于codesearch:google.com/...
kagali山

87

我发现C ++联合非常酷。似乎人们通常只想到用例,该用例是要“就地”更改联合实例的值(似乎仅用于节省内存或执行可疑的转换)。

实际上,即使您从未更改任何工会实例的值,工会也可以作为软件工程工具发挥巨大作用

用例1:变色龙

使用并集,您可以将多个任意类重新组合为一个面额,这与基类及其派生类的情况并非没有相似之处。但是,对于给定的联合实例,可以做或不能做的是:

struct Batman;
struct BaseballBat;

union Bat
{
    Batman brucewayne;
    BaseballBat club;
};

ReturnType1 f(void)
{
    BaseballBat bb = {/* */};
    Bat b;
    b.club = bb;
    // do something with b.club
}

ReturnType2 g(Bat& b)
{
    // do something with b, but how do we know what's inside?
}

Bat returnsBat(void);
ReturnType3 h(void)
{
    Bat b = returnsBat();
    // do something with b, but how do we know what's inside?
}

似乎程序员在要使用给定的联合实例时必须确定其内容的类型。f上面的函数就是这种情况。但是,如果函数要接收联合实例作为传递的参数,例如g,则它将不知道该如何处理。返回联合实例的函数也是如此,请参阅h:调用方如何知道内部内容?

如果联合实例永远不会作为参数或返回值传递,那么它必然具有非常单调的生活,当程序员选择更改其内容时会兴奋不已:

Batman bm = {/* */};
Baseball bb = {/* */};
Bat b;
b.brucewayne = bm;
// stuff
b.club = bb;

这是工会最(不受欢迎)的用例。另一个用例是,联合实例附带一些可以告诉您其类型的东西。

用例2:“很高兴认识您,我object来自Class

假设一个程序员选择总是将一个联合实例与一个类型描述符配对(我将由读者自行决定想象一个这样的对象的实现)。如果程序员想要的是节省内存,并且类型描述符的大小相对于联合的大小不可忽略,则这会破坏联合本身的目的。但是,让我们假设至关重要的是,在被调用者或调用者不知道内部内容的情况下,可以将联合实例作为参数或返回值进行传递。

然后,程序员必须编写一个 switch控制流语句,以告知Bruce Wayne除了木棍或类似的东西。当联合体中只有两种类型的内容时,还算不错,但显然,联合体不再扩展。

用例3:

正如ISO C ++标准建议书的作者早在2008年就指出的那样,

许多重要的问题域需要大量的对象或有限的内存资源。在这些情况下,节省空间非常重要,而合并通常是实现此目的的理想方法。实际上,一个常见的用例是工会在其生命周期内从未更改其活动成员的情况。可以像仅包含一个成员的结构一样对其进行构造,复制和销毁。此方法的典型应用是创建不相关类型的异构集合,这些不相关类型不会动态分配(也许它们是在映射中或数组成员中就地构造的)。

现在,举一个带有UML类图的示例:

A类的许多成分

用简单的英语来说就是这样的:类A的对象可以具有B1,...,Bn中的任何类的对象,并且每种类型中最多可以有一个对象,其中n是一个很大的数字,至少为10。

我们不想像这样向A添加字段(数据成员):

private:
    B1 b1;
    .
    .
    .
    Bn bn;

因为n可能会有所不同(我们可能想在混合中添加Bx类),并且因为这会导致构造函数混乱,并且因为A对象会占用很多空间。

我们可以使用带有void*指针的古怪指针容器来Bx强制转换以检索它们,但这很笨拙,所以采用C风格……但是更重要的是,这将使我们可以管理许多动态分配的对象。

相反,可以执行以下操作:

union Bee
{
    B1 b1;
    .
    .
    .
    Bn bn;
};

enum BeesTypes { TYPE_B1, ..., TYPE_BN };

class A
{
private:
    std::unordered_map<int, Bee> data; // C++11, otherwise use std::map

public:
    Bee get(int); // the implementation is obvious: get from the unordered map
};

然后,要从中获取联合实例的内容data,请使用a.get(TYPE_B2).b2等,其中aA实例为。

由于联合在C ++ 11中不受限制,因此功能更加强大。有关详细信息,请参见上面链接的文档本文


这非常有帮助,第二篇文章的系列非常有用。谢谢。
安德鲁

38

一个示例是在嵌入式领域,其中寄存器的每一位可能表示不同的含义。例如,使用8位整数和具有8个单独的1位位域的结构的并集,可以更改一位或整个字节。


7
这在设备驱动程序中也很常见。几年前,我在一个项目中使用诸如此类的联合编写了很多代码。通常不建议使用它,并且在某些情况下可以是特定于编译器的,但是它可以工作。
thkala 2011年

11
我不会称其为“不推荐”。在嵌入式空间中,它通常比其他替代方案更干净,更不易出错,而替代方案通常涉及大量的显式强制转换和void*s或掩码和移位。
2011年

h?有很多明确的演员表吗?在我看来,像REG |= MASK和的简单陈述REG &= ~MASK。如果那容易出错,则将它们放在#define SETBITS(reg, mask)和中#define CLRBITS(reg, mask)。不要依赖编译器来获得一定的顺序位(stackoverflow.com/questions/1490092/...
迈克尔

26

香草萨特在写GOTW大约六年前,与强调说:

“但是,不要以为联盟只是早先的保留。联盟可能通过允许数据重叠来节省空间最有用,而这在C ++和当今的现代世界中仍然是理想的。例如,其中一些最高级C ++现在,世界上的标准库实现都只使用这种技术来实现“小字符串优化”,这是一种伟大的优化选择,它可以重用字符串对象本身内部的存储:对于大字符串,字符串对象内部的空间通常存储动态指向的指针。分配的缓冲区和整理信息,例如缓冲区的大小;对于小字符串,取而代之的是重复使用相同的空间直接存储字符串内容,并完全避免任何动态内存分配。有关小字符串优化(以及其他相当深入的字符串优化和悲观化)的更多信息,请参阅...。”

关于一个不太有用的示例,请参见gcc,严格混叠和通过union进行转换这一冗长但不确定的问题。


23

好吧,我能想到的一个示例用例是:

typedef union
{
    struct
    {
        uint8_t a;
        uint8_t b;
        uint8_t c;
        uint8_t d;
    };
    uint32_t x;
} some32bittype;

然后,您可以访问该32位数据块的8位单独部分;但是,请准备好被字节序咬住。

这只是一个假设的示例,但是每当您想要将字段中的数据拆分为这样的组成部分时,都可以使用并集。

也就是说,还有一种字节顺序安全的方法:

uint32_t x;
uint8_t a = (x & 0xFF000000) >> 24;

例如,由于该二进制操作将由编译器转换为正确的字节序。


我认为最好的问题是何时应该使用工会。您提供了有关工会不是正确工具的答案,我认为应该在答案中更清楚地说明这一点。
迈克尔

15

工会的一些用途:

  • 提供到未知外部主机的常规字节序接口。
  • 处理外部CPU体系结构浮点数据,例如从网络链接接受VAX G_FLOATS并将其转换为IEEE 754长实数进行处理。
  • 提供对更高级别类型的简单直接访问。
union {
      unsigned char   byte_v[16];
      long double     ld_v;
 }

使用此声明,可以很容易地显示a的十六进制字节值long double,更改指数的符号,确定它是否为非正规值或对不支持它的CPU实施long double运算等。

  • 当字段取决于某些值时,节省存储空间:

    class person {  
        string name;  
    
        char gender;   // M = male, F = female, O = other  
        union {  
            date  vasectomized;  // for males  
            int   pregnancies;   // for females  
        } gender_specific_data;
    }
  • Grep包含文件,供您的编译器使用。您会发现数十种到数百种用途union

    [wally@zenetfedora ~]$ cd /usr/include
    [wally@zenetfedora include]$ grep -w union *
    a.out.h:  union
    argp.h:   parsing options, getopt is called with the union of all the argp
    bfd.h:  union
    bfd.h:  union
    bfd.h:union internal_auxent;
    bfd.h:  (bfd *, struct bfd_symbol *, int, union internal_auxent *);
    bfd.h:  union {
    bfd.h:  /* The value of the symbol.  This really should be a union of a
    bfd.h:  union
    bfd.h:  union
    bfdlink.h:  /* A union of information depending upon the type.  */
    bfdlink.h:  union
    bfdlink.h:       this field.  This field is present in all of the union element
    bfdlink.h:       the union; this structure is a major space user in the
    bfdlink.h:  union
    bfdlink.h:  union
    curses.h:    union
    db_cxx.h:// 4201: nameless struct/union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:  union
    elf.h:typedef union
    _G_config.h:typedef union
    gcrypt.h:  union
    gcrypt.h:    union
    gcrypt.h:    union
    gmp-i386.h:  union {
    ieee754.h:union ieee754_float
    ieee754.h:union ieee754_double
    ieee754.h:union ieee854_long_double
    ifaddrs.h:  union
    jpeglib.h:  union {
    ldap.h: union mod_vals_u {
    ncurses.h:    union
    newt.h:    union {
    obstack.h:  union
    pi-file.h:  union {
    resolv.h:   union {
    signal.h:extern int sigqueue (__pid_t __pid, int __sig, __const union sigval __val)
    stdlib.h:/* Lots of hair to allow traditional BSD use of `union wait'
    stdlib.h:  (__extension__ (((union { __typeof(status) __in; int __i; }) \
    stdlib.h:/* This is the type of the argument to `wait'.  The funky union
    stdlib.h:   causes redeclarations with either `int *' or `union wait *' to be
    stdlib.h:typedef union
    stdlib.h:    union wait *__uptr;
    stdlib.h:  } __WAIT_STATUS __attribute__ ((__transparent_union__));
    thread_db.h:  union
    thread_db.h:  union
    tiffio.h:   union {
    wchar.h:  union
    xf86drm.h:typedef union _drmVBlank {

5
sk!两次投票,没有任何解释。真令人失望。
wallyk

在我眼里,一个可以容纳一个男人和一个女人的人的例子是非常糟糕的设计。为什么一个人基层阶级和一个男人和女人派生一个阶级?抱歉,但是手动寻找一个变量来确定数据字段中的存储类型绝对不是一个好主意。这是多年未见的手工C代码。但是请不要低声投票,这只是我的观点:-)
克劳斯

4
我猜你对“ cast割”或“怀孕”工会持反对态度。有点恶心
阿卡塔尔

2
是的,我想那是黑暗的一天。
wallyk '18

14

联合在处理字节级(低级)数据时很有用。

我最近的用法之一是IP地址建模,如下所示:

// Composite structure for IP address storage
union
{
    // IPv4 @ 32-bit identifier
    // Padded 12-bytes for IPv6 compatibility
    union
    {
        struct
        {
            unsigned char _reserved[12];
            unsigned char _IpBytes[4];
        } _Raw;

        struct
        {
            unsigned char _reserved[12];
            unsigned char _o1;
            unsigned char _o2;
            unsigned char _o3;
            unsigned char _o4;    
        } _Octet;    
    } _IPv4;

    // IPv6 @ 128-bit identifier
    // Next generation internet addressing
    union
    {
        struct
        {
            unsigned char _IpBytes[16];
        } _Raw;

        struct
        {
            unsigned short _w1;
            unsigned short _w2;
            unsigned short _w3;
            unsigned short _w4;
            unsigned short _w5;
            unsigned short _w6;
            unsigned short _w7;
            unsigned short _w8;   
        } _Word;
    } _IPv6;
} _IP;

7
但是请记住,访问此类原始内容不是标准的,并且可能无法在所有编译器中正常工作。

3
同样,很常见的情况是,这种用法不能保证对齐,这是未定义的行为。
Mooing Duck 2013年

10

我使用工会的一个例子:

class Vector
{
        union 
        {
            double _coord[3];
            struct 
            {
                double _x;
                double _y; 
                double _z;
            };

        };
...
}

这使我可以将数据作为数组或元素进行访问。

我使用了一个联合,使不同的术语指向相同的值。在图像处理中,无论我是在处理列还是在X方向上的宽度或大小,它都可能会造成混淆。为了解决这个问题,我使用了一个并集,所以我知道哪些描述一起使用。

   union {   // dimension from left to right   // union for the left to right dimension
        uint32_t            m_width;
        uint32_t            m_sizeX;
        uint32_t            m_columns;
    };

    union {   // dimension from top to bottom   // union for the top to bottom dimension
        uint32_t            m_height;
        uint32_t            m_sizeY;
        uint32_t            m_rows;
    };

12
请注意,尽管此解决方案可在大多数可观察的平台上运行,但将值设置为_x,_y,_z并访问_coord是未定义的行为。工会的主要目的是节省空间。您必须访问与先前设置完全相同的并集元素。
anxieux

1
这也是我使用它的方式,通过使用std :: array forr coords和一些static_asserts
Viktor Sehr

1
此代码违反严格的别名规则,因此不建议使用。
Walter

也许有办法改善工会,使之可靠吗?
安德鲁

8

联合在C中提供多态性。


18
我以为void*是这样^^

2
@ user166390多态性使用相同的接口来操纵多种类型。void *没有接口。
爱丽丝

2
在C语言中,多态通常通过不透明的类型和/或函数指针来实现。我不知道您将如何或为何使用工会来实现这一目标。听起来这真是个坏主意。
伦丁

7

联合的一个出色用法是内存对齐,这是我在PCL(点云库)源代码中找到的。API中的单个数据结构可以针对两种体系结构:具有SSE支持的CPU和不具有SSE支持的CPU。例如:PointXYZ的数据结构是

typedef union
{
  float data[4];
  struct
  {
    float x;
    float y;
    float z;
  };
} PointXYZ;

3个浮子用一个额外的浮子填充以进行SSE对齐。因此对于

PointXYZ point;

用户可以访问point.data [0]或point.x(取决于SSE支持)以访问x坐标。以下链接提供了更多类似的更好的用法详细信息:PCL文档PointT类型


7

union关键字,同时仍然在C中使用++ 03 1,是大多的C-天的残余。最明显的问题是它仅适用于POD 1

但是,联合的想法仍然存在,并且Boost库确实具有类似联合的类:

boost::variant<std::string, Foo, Bar>

它具有union(如果不是全部)的大部分优点,并添加:

  • 正确使用非POD类型的能力
  • 静态安全

在实践中,已经证明,这是相当于组合union+ enum,和基准,这是因为快速(而boost::any更的境界dynamic_cast,因为它使用RTTI)。

1联合在C ++ 11中进行了升级(不受限制的联合),现在可以包含带有析构函数的对象,尽管用户必须手动调用析构函数(在当前活动的联合成员上)。使用变体仍然容易得多。


在最新版本的c ++中,这不再是正确的。例如,请参阅jrsala的答案。
安德鲁

@Andrew:我更新了答案,提到C ++ 11具有不受限制的联合,允许将具有析构函数的类型存储在联合中。我还在我的立场,你真的是关闭使用要好得多站在标签联合,如boost::variant不是试图对自己使用的工会。工会周围存在太多未定义的行为,以至于您无法正确实现工会的机会。
Matthieu M.

3

对工会维基百科的文章

联合的主要用途是节省空间,因为它提供了一种让许多不同类型存储在同一空间中的方法。联合还 提供了粗略的多态性。但是,没有类型检查,因此程序员必须确保在不同的上下文中访问正确的字段。并集变量的相关字段通常由其他变量的状态(可能在封闭结构中)确定。

一种常见的C编程习惯用法通过将联合赋值给联合的一个字段并从另一个字段中读取,来执行C ++称为reinterpret_cast的操作,就像在代码中所做的那样,这取决于值的原始表示形式。


2

在C的早期(例如1974年记录),所有结构都为其成员共享一个公共名称空间。每个成员名称都与一个类型和一个偏移量相关联;如果“ wd_woozle”是偏移量12处的“ int”,则给定p任何结构类型的指针p->wd_woozle都将等效于*(int*)(((char*)p)+12)。该语言要求所有结构类型的所有成员都必须具有唯一的名称,只是在使用它们的每个结构将它们视为共同的初始序列的情况下,它明确允许成员名称的重用。

可以混杂使用结构类型的事实使结构的行为好像它们包含重叠的字段一样。例如,给定的定义:

struct float1 { float f0;};
struct byte4  { char b0,b1,b2,b3; }; /* Unsigned didn't exist yet */

代码可以声明“ float1”类型的结构,然后使用“成员” b0 ... b3访问其中的各个字节。当更改语言以使每个结构为其成员接收一个单独的命名空间时,依赖于以多种方式访问​​事物的能力的代码将被破坏。为不同的结构类型分隔出名称空间的值足以要求更改此类代码以容纳它,但是此类技术的价值足以证明扩展语言以继续支持它是合理的。

这已经被写入利用到内的访问存储的能力代码struct float1,就好像是一个struct byte4:可以在新的语言文字工作通过增加宣发union f1b4 { struct float1 ff; struct byte4 bb; };,声明的对象类型union f1b4;,而不是struct float1和替换访问f0b0b1,等随着ff.f0bb.b0bb.b1等。虽然有更好的方法这样的代码可能已被支持,该union方法是至少在某种程度上可行的,至少的别名规则C89时代的解释。


1

可以说您有n种不同类型的配置(只是一组定义参数的变量)。通过使用配置类型的枚举,可以定义一个具有配置类型ID的结构,以及所有不同类型配置的联合。

这样,无论您在何处传递配置,都可以使用ID来确定如何解释配置数据,但是,如果配置庞大,您将不必为每种潜在的类型浪费空间都具有并行结构。


1

严格的别名规则工会的重要性已经有了新的提高。最近版本的C标准引入新的提高。

您可以在不违反C标准的情况下使用union do进行类型修饰
该程序具有未指定的行为(因为我已经假设了float并且unsigned int具有相同的长度),但是没有未定义的行为(请参见此处)。

#include <stdio.h> 

union float_uint
{
    float f;
    unsigned int ui;
};

int main()
{
    float v = 241;
    union float_uint fui = {.f = v};

    //May trigger UNSPECIFIED BEHAVIOR but not UNDEFINED BEHAVIOR 
    printf("Your IEEE 754 float sir: %08x\n", fui.ui);

    //This is UNDEFINED BEHAVIOR as it violates the Strict Aliasing Rule
    unsigned int* pp = (unsigned int*) &v;

    printf("Your IEEE 754 float, again, sir: %08x\n", *pp);

    return 0;
}

类型访问规则不仅限于标准的“最新”版本。每个C版本都包含基本相同的规则。所发生的变化是,编译器曾经将脚注视为“此列表的目的是指定对象可能会别名或可能不会别名的那些情况”。表示该规则不是要在不涉及书面别名的情况下适用,但他们现在将其视为邀请重写代码以在没有别名的情况下创建别名。
超级猫

1

我想添加一个使用联合的良好实践示例-实现公式计算器/解释器或在计算中使用某种形式(例如,您想计算公式的运行时部分使用可修改的方法-数字求解方程式-仅例如)。因此,您可能需要定义不同类型的数字/常量(整数,浮点数甚至是复数),如下所示:

struct Number{
enum NumType{int32, float, double, complex}; NumType num_t;
union{int ival; float fval; double dval; ComplexNumber cmplx_val}
}

因此,您可以节省内存,更重要的是-避免对可能数量极多(如果使用大量运行时定义的数量)的小型对象(与通过类继承/多态性实现的对象相比)进行动态分配。但是更有趣的是,对于这种类型的结构,您仍然可以使用C ++多态性的功能(例如,如果您喜欢双重分派;)。只需将“虚拟”接口指针添加到所有数字类型的父类作为此结构的字段,而不是原始类型(除了原始类型)/ 指向此实例,或使用良好的旧C函数指针即可。

struct NumberBase
{
virtual Add(NumberBase n);
...
}
struct NumberInt: Number
{
//implement methods assuming Number's union contains int
NumberBase Add(NumberBase n);
...
}
struct NumberDouble: Number
{
 //implement methods assuming Number's union contains double
 NumberBase Add(NumberBase n);
 ...
}
//e.t.c. for all number types/or use templates
struct Number: NumberBase{
 union{int ival; float fval; double dval; ComplexNumber cmplx_val;}
 NumberBase* num_t;
 Set(int a)
 {
 ival=a;
  //still kind of hack, hope it works because derived classes of   Number    dont add any fields
 num_t = static_cast<NumberInt>(this);
 }
}

因此,如果需要,您可以使用多态性来代替switch(type)的类型检查-具有内存有效的实现(没有动态分配小对象)。


制作动态语言时,这可能很有用。我认为它将解决的问题是在不执行N次修改的情况下大规模修改未知类型的变量。宏对此是可怕的,并且模板化实际上是不可能的。
安德鲁

0

http://cplus.about.com/od/learningc/ss/lowlevel_9.htm

工会的用途很少而且相差甚远。在大多数计算机上,指针和int的大小通常是相同的-这是因为两者通常都适合CPU中的寄存器。因此,如果您想对int指针进行快速而肮脏的转换,或者相反,请声明一个并集。

union intptr {   int i;   int * p; }; 
union intptr x; x.i = 1000; 
/* puts 90 at location 1000 */ 
*(x.p)=90; 

联合的另一种用法是在命令或消息协议中发送和接收大小不同的消息。每种消息类型将保存不同的信息,但是每种消息将具有固定部分(可能是结构)和可变部分位。这就是您可能会实现的方式。

struct head {   int id;   int response;   int size; }; struct msgstring50 {    struct head fixed;    char message[50]; } struct

struct msgstring80 {struct head fixed; 字符消息[80]; }
struct msgint10 {struct head fixed; int讯息[10]; } struct msgack {struct head fixed; 好的 } union messagetype {
struct msgstring50 m50; struct msgstring80 m80; struct msgint10 i10; struct msgack ack; }

实际上,尽管并集大小相同,但仅发送有意义的数据而不浪费空间是有意义的。msgack的大小仅为16个字节,而msgstring80的大小为92个字节。因此,在初始化messagetype变量时,将根据其类型设置其size字段。然后其他功能可以使用它来传输正确数量的字节。


0

联合提供了一种在单个存储区域中处理不同类型数据的方式,而无需在程序中嵌入任何与机器无关的信息。它们类似于pascal中的变体记录

例如,在编译器符号表管理器中可能找到一个示例,假设常量可以是int,float或字符指针。特定常数的值必须存储在适当类型的变量中,但是,如果该值占据相同的存储量并且无论其类型如何都存储在相同的位置,则对于表管理最方便。这是联合的目的-单个变量可以合法地容纳几种类型之一。语法基于以下结构:

union u_tag {
     int ival;
     float fval;
     char  *sval;
} u;

变量u足够大,可以容纳三种类型中的最大值。具体大小取决于实现方式。这些类型中的任何一种都可以分配给u,然后在表达式中使用,只要用法一致

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.