有什么好的例子可以说明结构体和联合体的区别吗? 基本上我知道struct使用了它成员的所有内存,而union使用了最大的成员内存空间。还有其他操作系统级别的差异吗?


当前回答

有什么好的例子可以说明“结构体”和“联合体”之间的区别吗?

一种假想的通信协议

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

在这个假想的协议中,已经指定了基于“消息类型”的消息头中的下面位置要么是请求号,要么是四个字符的代码,但不是两者都是。简而言之,联合允许相同的存储位置表示多个数据类型,可以保证您在任何时候只希望存储其中一种类型的数据。

联合在很大程度上是基于C作为系统编程语言的传统的低级细节,其中“重叠”存储位置有时以这种方式使用。如果数据结构一次只保存几种类型中的一种,则有时可以使用联合来节省内存。

一般来说,操作系统不关心或不知道结构体和并体——它们都只是内存块。struct是存储多个数据对象的内存块,这些对象不重叠。联合是存储多个数据对象的内存块,但只能存储其中最大的一个,因此在任何时候只能存储其中一个数据对象。

其他回答

有什么好的例子可以说明“结构体”和“联合体”之间的区别吗?

一种假想的通信协议

struct packetheader {
   int sourceaddress;
   int destaddress;
   int messagetype;
   union request {
       char fourcc[4];
       int requestnumber;
   };
};

在这个假想的协议中,已经指定了基于“消息类型”的消息头中的下面位置要么是请求号,要么是四个字符的代码,但不是两者都是。简而言之,联合允许相同的存储位置表示多个数据类型,可以保证您在任何时候只希望存储其中一种类型的数据。

联合在很大程度上是基于C作为系统编程语言的传统的低级细节,其中“重叠”存储位置有时以这种方式使用。如果数据结构一次只保存几种类型中的一种,则有时可以使用联合来节省内存。

一般来说,操作系统不关心或不知道结构体和并体——它们都只是内存块。struct是存储多个数据对象的内存块,这些对象不重叠。联合是存储多个数据对象的内存块,但只能存储其中最大的一个,因此在任何时候只能存储其中一个数据对象。

结构分配其中所有元素的总大小。

一个联合只分配它最大的成员所需要的内存。

当我们需要对具有一组独立属性的事物进行建模时,建议使用结构;当一个实体具有多种形式且一次只能以一种形式存在时,建议使用联合。

让我们看看它们可以应用的两个地方

您需要存储和更新汽车的值。为了做到这一点,我们需要记录汽车的所有属性,如型号、里程、价格和燃料类型。这些值总是存在于一辆车中,它们不依赖于其他值。 因此,我们需要一种数据类型,它不仅存储所有属性,而且还确保它们的正确更新。这种类型的任务可以使用结构来完成。

struct car{
    char model[];
    int mileage;
    int price;
    char fuel_type[];
};

组织需要从大量客户那里收集您的数据以进行付款验证。现在,为了数据完整性和客户安全,一个组织被指示从一个人那里获得最少的详细信息。 这些详细信息可以是您的PAN号码或帐户号码或选民身份证。现在,因为我们需要收集这些细节中的任何一个,同时也要节省内存,我们可以在这里使用联合。它将只保存提供给它的单个值。

union verification_details{
    char account_number[10];
    char PAN[10];
    char voter_id[10];
};

我发现下面的文章解释得很好:结构和联合的区别

在编写下面给出的字节排序函数时,联合非常方便。这在结构体中是不可能的。

int main(int argc, char **argv) {
    union {
        short   s;
        char    c[sizeof(short)];
    } un;

    un.s = 0x0102;

    if (sizeof(short) == 2) {
        if (un.c[0] == 1 && un.c[1] == 2)
            printf("big-endian\n");
        else if (un.c[0] == 2 && un.c[1] == 1)
            printf("little-endian\n");
        else
            printf("unknown\n");
    } else
        printf("sizeof(short) = %d\n", sizeof(short));

    exit(0);
}
// Program from Unix Network Programming Vol. 1 by Stevens.

对于联合,您只应该使用其中一个元素,因为它们都存储在同一个位置。这在您想要存储可能是几种类型之一的内容时非常有用。另一方面,结构体的每个元素都有单独的内存位置,并且它们都可以一次使用。

为了给出它们使用的一个具体例子,我不久前正在研究一个Scheme解释器,我实际上是将Scheme数据类型覆盖到C数据类型上。这涉及到在一个结构体中存储一个指明值类型的枚举和一个用于存储该值的联合。

union foo {
  int a;   // can't use both a and b at once
  char b;
} foo;

struct bar {
  int a;   // can use both a and b simultaneously
  char b;
} bar;

union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!

struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK

编辑:如果你想知道将x.b设置为“c”会改变x.a的值,从技术上讲,这是未定义的。在大多数现代机器上,char是1字节,int是4字节,所以给x.b的值'c'也给x.a的第一个字节相同的值:

union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

打印

99, 99

为什么这两个值是一样的?因为int 3的最后3个字节都是0,所以它也被读为99。如果我们为x.a输入一个更大的数字,你会发现情况并非总是如此:

union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);

打印

387427, 99

为了更仔细地查看实际的内存值,让我们设置并打印十六进制的值:

union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);

打印

deadbe22, 22

您可以清楚地看到0x22覆盖了0xEF的位置。

BUT

在C语言中,int类型的字节顺序没有定义。这个程序在我的Mac上用0x22覆盖了0xEF,但是在其他平台上它会覆盖0xDE,因为组成int的字节的顺序颠倒了。因此,在编写程序时,永远不要依赖于覆盖联合中特定数据的行为,因为它是不可移植的。

有关字节排序的更多信息,请参阅endianness。