6

我很难理解unionC 中的使用。我在这里阅读了很多关于这个主题的帖子。但是他们都没有解释为什么union可以使用结构来实现相同的目标。

引用 K&R

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

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

有什么想法吗?

4

11 回答 11

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;
}
于 2010-07-28T11:05:54.380 回答
8

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

联合可能有一个巧妙的“欺骗”:写入一个字段并从另一个字段读取,以检查位模式或以不同方式解释它们。

于 2010-07-28T11:17:18.177 回答
4

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

于 2010-07-28T11:04:36.277 回答
4

联合用于一次只保存一种类型的数据。如果重新分配一个值,则旧值将被覆盖并且无法访问。在您的示例中, int 、float 和 char 成员在用作结构时可以随时具有不同的值。工会的情况并非如此。所以这取决于你的程序要求和设计。查看这篇关于何时使用联合的文章。 谷歌可能会给出更多结果。

于 2010-07-28T11:13:19.827 回答
2

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

然而,仅仅存在某物并不能自动表明它的使用是最佳实践。它们的存在使语言强大而灵活。但是行业需求导致了编程技术的发展,这些技术有利于清晰度和可维护性,而不是绝对最佳的代码效率或存储效率。

因此,如果一个问题的解决方案集同时包含联合和结构,则程序员有责任决定对紧凑存储的需求是否超过成本。

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

因此,为了回答您的问题,与传统结构相比,联合为多种可能类型中的单个值实体提供了更紧凑的存储,但代价是清晰度和可能的细微程序缺陷。

于 2010-07-28T11:16:48.743 回答
1

在现代系统中,大多数情况下不会使用联合来节省内存,因为访问联合成员的代码将很快占用更多空间(并且速度更慢),而不是仅仅将另一个字大小的变量添加到内存中。但是,当您的代码必须支持具有不同字节顺序的多种架构时(哇,多么棒的词),联合可能会很方便。我倾向于使用 endian 实用程序库(不是函数),但有些人喜欢联合。

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

于 2010-07-28T12:01:42.070 回答
1

工会有两个主要用途:

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

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

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 个原始 char 字节解释为 IEEE 浮点数。将该 char 数组转换为 float 不会有相同的结果。

于 2010-07-28T15:54:42.437 回答
0

正如之前经常提到的:联合节省内存。但这不是唯一的区别。结构用于保存所有给定的子类型,而联合则用于保存给定子类型中的一个。因此,如果您想存储整数或浮点数,那么联合可能就是您需要的东西(但您需要在其他地方记住您保存了哪种数字)。如果你想存储两者,那么你需要一个结构。

于 2010-07-28T13:57:36.073 回答
0

一次借用您发布的“......几种类型中的任何一种......”工会成员的报价。这正是 union 的含义。而结构成员都可以一次分配和访问。

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

于 2010-07-28T14:44:36.020 回答
0

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

| 类型(4 字节) | uid (8 字节) | 有效载荷长度(2 个字节) | 有效载荷(可变长度) |

然后会有各种类型的数据包有效载荷......为了争论,可能有你好,再见和消息包......

好吧,您可以构建一组嵌套的结构/联合,它们可以像这样准确地表示该协议中的数据包......

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() 调用从操作系统获取此协议,而这只是一堆乱七八糟的字节。但是如果你对你的结构定义很小心,并且所有类型的大小都是正确的,你可以简单地创建一个指向结构的指针,将它指向你填充随机数据的缓冲区,然后......

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)。

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

于 2010-07-29T06:34:17.677 回答
0

一个例子在这里是有意义的。请参见下面的示例:

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

uint是 unsigned int 的 typedef。

这里,这个联合代表一个 32 位的寄存器。您可以使用 allX 读取寄存器,然后使用结构对其进行操作。

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

于 2011-07-01T20:02:04.310 回答