使用union时可以使用union实现相同的优点

时间:2010-07-28 11:02:48

标签: c data-structures struct unions

我很难理解C中union的用法。我在这里读了很多关于这个主题的帖子。但是,当使用结构可以实现同样的事情时,他们都没有解释为什么union是首选的。

引自K& R

  

作为一个可能被发现的例子   在编译器符号表管理器中,   假设一个常量可能是一个int,   浮点数或字符指针。该   特定常数的值必须是   存储在适当的变量中   类型,但它是最方便的   如果值占用,表管理   相同的存储量和   无论如何都存放在同一个地方   它的类型。这是一个目的   联合一个可以的变量   合法地持有任何一个   几种类型。语法基于   结构:

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

用法将是

if (utype == INT)
    printf("%d\n", u.ival);
if (utype == FLOAT)
    printf("%f\n", u.fval);
if (utype == STRING)
    printf("%s\n", u.sval);
else
    printf("bad type %d in utype\n", utype);

使用结构可以实现相同的功能。像,

struct u_tag {
    utype_t utype;
    int ival;
    float fval;
    char *sval;
} u;

if (u.utype == INT)
    printf("%d\n", u.ival);
if (u.utype == FLOAT)
    printf("%f\n", u.fval);
if (u.utype == STRING)
    printf("%s\n", u.sval);
else
    printf("bad type %d in utype\n", utype);

这不一样吗? union有什么好处?

有什么想法吗?

11 个答案:

答案 0 :(得分:9)

在你发布的例子中,union的大小将是float的大小(假设它是最大的 - 如注释中所指出的,它可以在64位编译器中变化),而struct的大小将是是float,int,char *和utype_t(以及填充,如果有的话)的大小之和。

我的编译器上的结果:

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

int main()
{
    printf("%d\n", sizeof(union u_tag));  //prints 4
    printf("%d\n", sizeof(struct s_tag)); //prints 12
    return 0;
}

答案 1 :(得分:8)

当一次只需要访问一个成员时,可以使用联合。这样,您可以节省一些内存而不是使用结构。

工会可能会有一个整洁的“作弊”:写一个字段并从另一个字段读取,检查位模式或以不同方式解释它们。

答案 2 :(得分:4)

Union使用更少的内存,让你做更危险的事情。它表示一个连续的内存块,可以解释为整数,浮点值或字符指针。

答案 3 :(得分:4)

联合用于一次仅保存一种类型的数据。如果重新分配值,则旧值将被覆盖且无法访问。 在您的示例中,当用作结构时,float和char成员可以随时具有不同的值。它不是联合的情况。 所以这取决于您的计划要求和设计。 检查此article何时使用union。 Google可能会带来更多结果。

答案 4 :(得分:2)

该语言为程序员提供了许多工具,可将高级抽象应用于最低级别的机器数据和操作。

然而,仅仅存在某些东西并不会自动表明它的使用是最佳实践。他们的出现使语言变得强大而灵活。但是行业需求促使编程技术的发展有利于清晰度和可维护性,而不是最佳的代码效率或存储效率。

因此,如果一个问题的解决方案集包含了工会和结构,那么程序员就有责任决定是否需要紧凑的存储来超过成本。

近来,记忆成本一直非常低。 bool类型的引入(甚至在此之前,int变量)允许32位系统的程序员使用32位来表示二进制状态。您可以在编程中经常看到,即使程序员可以使用掩码并将32个真/假值放入变量中。

所以为了回答你的问题,联盟为一个单一的价值实体提供了比传统结构更多的紧凑存储,但代价是清晰度和可能的细微程序缺陷。

答案 5 :(得分:1)

使用联合来节省内存大多数都不是在现代系统中完成的,因为访问联合成员的代码将比仅向内存中添加另一个字大小的变量快速占用更多空间(并且更慢)。但是,当您的代码必须支持具有不同字节序的多个体系结构时(哇哇,一句话),工会可以很方便。我倾向于使用endian实用程序库(函数),但有些人喜欢使用联合。

内存映射的硬件寄存器通常也可以通过联合访问。 C中的位字段(不要使用它们,它们的意思是)可以使用联合作为单词传递。

答案 6 :(得分:1)

工会有两个主要用途:

首先提供一种变体类型,如您所概述的那样。与struct方法相比,union中的所有成员之间共享一个内存单元。如果内存不是问题,结构也将提供此功能。

我通常在结构中嵌入联合 - 结构确保类型和数据存储在一起,而联合意味着只存储一个值。

struct any_tag {
    utype_t utype;
    union {
        int ival;
        float fval;
        char *sval;
    } u;
} data;

其次,联盟对低级访问原始数据非常有用 - 将一种类型重新解释为另一种类型。我用它的目的是读写二进制编码数据。

float ConvertByteOrderedBufferTo32bitFloat( char* input ) {
union {
    float f;
    unsigned char buf[4];
} data;

#if WORDS_BIGENDIAN == 1
data.buf[0] = input[0];
data.buf[1] = input[1];
data.buf[2] = input[2];
data.buf[3] = input[3];
#else
data.buf[0] = input[3];
data.buf[1] = input[2];
data.buf[2] = input[1];
data.buf[3] = input[0];
#endif

return dat1.f;
}

在这里,您可以根据平台字节顺序写入各个字节,然后将这4个原始字节解释为IEEE float。将char数组转换为float将不会产生相同的结果。

答案 7 :(得分:0)

如前所述:工会可以节省内存。 但这不是唯一的区别。 Stuncts用于保存所有给定的子类型,而联合用于保存一个给定的子类型。因此,如果你想存储一个整数或一个浮点数,那么联盟可能就是你需要的东西(但是你需要记住其他地方你保存的那种数字)。如果你想存储两者,那么你需要一个结构。

答案 8 :(得分:0)

从您发布的报价中借用“... 中的任何一种......一次性的工会成员。这正是工会的结果;而结构成员可以一次分配和访问。

union在执行某些系统级(os)程序(如进程通信/并发处理)时更有意义。

答案 9 :(得分:0)

工会很棘手。多年来,我无法理解它们,然后我开始用网络协议做事,有人向我展示了光明。假设您有一个标题,然后在标题之后,有各种不同类型的数据包,例如:

| type(4 bytes)| uid(8字节)|有效载荷长度(2字节)|有效载荷(变长)|

然后会有各种类型的数据包有效负载......为了争论,可能会有hello,goodbye和message packet ......

好吧,你可以构建一组嵌套的结构/联合,它们可以完全代表该协议中的数据包,就像这样......

struct packet {
  uint type;
  char unique_id [8];
  ushort payload_length;
  union payload {

    struct hello {
      ushort version;
      uint status;
    };

    struct goodbye {
      char reason[20];
      uint status;
    };

    struct message {
      char message[100];
    };

  };
};

不可避免地,你通过read()调用从操作系统获得这个协议,它只是一个混乱的字节。但是如果你仔细考虑你的结构定义,并且所有类型都是正确的大小,你可以简单地创建一个指向struct的指针,将它指向填充了随机数据的缓冲区,然后......

char buf[100];
packet *pkt;
read(outsideworld,&buf,1000);
pkt = (struct packet *)&buf;

并且读取数据包就像......一样简单。

switch(pkt->type){

  case PACKET_MESSAGE:
    printf("message = %s\n",
           pkt->payload.message.message);
    break;

  case PACKET_HELLO:
    printf("hello! version = %d status = %d\n",
           pkt->payload.hello.version,
           pkt->payload.hello.status);
    break;
  case PACKET_GOODBYE:
    printf("goodbye! reason = %s status = %d\n",
           pkt->payload.goodbye.reason,
           pkt->payload.goodbye.status);
    break;
}

没有徘徊,计算字节等...你可以根据需要深入嵌套(为ip地址建立一个联合,它可以将整个事物作为无符号整数或单个字节,因此更容易打印192.168.0.1)。

工会不会减慢您的代码速度,因为它只会被转换为机器代码中的偏移量。

答案 10 :(得分:0)

这里有一个例子。请参阅以下示例:

union xReg
{
    uint allX;
    struct
    {
        uint x3      : 9;
        uint x2      : 9;
        uint x1      : 14;
    };
};

uint是unsigned int的typedef。

这里,这个联合代表一个32位寄存器。您可以使用allX读取寄存器,然后使用struct操作它。

如果我们使用allX进行位操作,这可以避免不必要的位移。