ChatGPT解决这个技术问题 Extra ChatGPT

结构和联合之间的区别

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


S
Saurabh Hooda

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

举一个具体的例子来说明它们的使用,不久前我正在研究一个 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

编辑:如果您想知道将 xb 设置为 'c' 会将 xa 的值更改为什么,从技术上讲,它是未定义的。在大多数现代机器上,一个 char 是 1 个字节,一个 int 是 4 个字节,所以给 xb 值 'c' 也会给 xa 的第一个字节同样的值:

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

印刷

99, 99

为什么这两个值相同?因为 int 3 的最后 3 个字节全为零,所以它也读作 99。如果我们为 xa 输入一个更大的数字,你会发现情况并非总是如此:

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 的位置。

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

有关字节顺序的更多信息,请查看 endianness


使用这个例子,在联合中,如果 xb='c' 什么会存储在 xa 中?它是char的参考#吗?
希望这可以更详细地解释设置 xb 时存储在 xa 中的内容
@KyleCronin 我想我明白了。在你的情况下,你有一组类型,知道你只需要使用一个,但直到运行时你才知道哪一个 - 所以联合允许你这样做。谢谢
@user12345613 联合可以用作结构的一种基类。您可以使用结构的联合来模拟 OO 层次结构
@Lazar 多字节类型的字节顺序取决于字节序。我建议阅读有关它的维基百科文章。
C
Charlie Martin

这是简短的答案:结构是记录结构:结构中的每个元素分配新空间。所以,像这样的结构

struct foobarbazquux_t {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

在内存中为每个实例分配至少 (sizeof(int)+sizeof(long)+sizeof(double)+sizeof(long double)) 个字节。 (“至少”因为架构对齐约束可能会强制编译器填充结构。)

另一方面,

union foobarbazquux_u {
    int foo;
    long bar;
    double baz; 
    long double quux;
}

分配一块内存并给它四个别名。所以sizeof(union foobarbazquux_u) ≥ max((sizeof(int),sizeof(long),sizeof(double),sizeof(long double)),同样有可能为对齐添加一些内容。


g
gsamaras

有没有很好的例子来说明“结构”和“联合”之间的区别?

一个虚构的通信协议

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

在这个虚构的协议中,根据“消息类型”,标头中的以下位置将是请求号或四字符代码,但不能同时是两者。简而言之,联合允许相同的存储位置表示一种以上的数据类型,从而保证您在任何时候都只想存储其中一种数据类型。

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

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


假设您有一个packetheader ph;,您如何访问 requestnumber? ph.request.requestnumber ?
S
Steven Kirkland

正如您在问题中已经陈述的那样,unionstruct 之间的主要区别在于 union 成员相互覆盖内存,因此联合的 sizeof 是 one ,而 struct 成员被布置一个接一个(中间有可选的填充)。此外,联合大到足以容纳其所有成员,并且具有适合其所有成员的对齐方式。所以假设 int 只能存储在 2 个字节的地址并且是 2 个字节宽,而 long 只能存储在 4 个字节的地址并且是 4 个字节长。下面的联合

union test {
    int a;
    long b;
}; 

sizeof 可能为 4,对齐要求为 4。联合和结构都可以在末尾有填充,但不能在开头。写入结构只会更改写入的成员的值。写给工会的成员将使所有其他成员的值无效。如果您之前没有写过它们,则无法访问它们,否则行为是未定义的。 GCC 提供了一个扩展,您实际上可以从工会成员那里阅读,即使您最近没有写信给他们。对于操作系统,用户程序是写入联合还是写入结构并不重要。这实际上只是编译器的问题。

union 和 struct 的另一个重要属性是,它们允许指向它们的指针可以指向其任何成员的类型。所以以下是有效的:

struct test {
    int a;
    double b;
} * some_test_pointer;

some_test_pointer 可以指向 int*double*。如果将 test 类型的地址转换为 int*,它实际上将指向其第一个成员 a。工会也是如此。因此,因为联合总是有正确的对齐方式,你可以使用联合来使指向某些类型有效:

union a {
    int a;
    double b;
};

该联合实际上将能够指向一个 int 和一个 double:

union a * v = (union a*)some_int_pointer;
*some_int_pointer = 5;
v->a = 10;
return *some_int_pointer;    

实际上是有效的,如 C99 标准所述:

对象的存储值只能由具有以下类型之一的左值表达式访问: 与对象的有效类型兼容的类型 ... 在其成员中包含上述类型之一的聚合或联合类型

编译器不会优化 v->a = 10;,因为它可能会影响 *some_int_pointer 的值(并且该函数将返回 10 而不是 5)。


K
Krzysztof Voss

union 在几个场景中很有用。 union 可以是非常低级操作的工具,例如为内核编写设备驱动程序。

这方面的一个示例是通过使用具有位域和 floatstructunion 来剖析 float 数字。我在 float 中保存了一个数字,然后我可以通过该 struct 访问 float 的特定部分。该示例显示了如何使用 union 以不同的角度查看数据。

#include <stdio.h>                                                                                                                                       

union foo {
    struct float_guts {
        unsigned int fraction : 23;
        unsigned int exponent : 8;
        unsigned int sign     : 1;
    } fg;
    float f;
};

void print_float(float f) {
    union foo ff;
    ff.f = f;
    printf("%f: %d 0x%X 0x%X\n", f, ff.fg.sign, ff.fg.exponent, ff.fg.fraction);

}

int main(){
    print_float(0.15625);
    return 0;
}

查看 wikipedia 上的 single precision 描述。我从那里使用了示例和幻数 0.15625。

union 也可用于实现具有多种选择的代数数据类型。我在 O'Sullivan、Stewart 和 Goerzen 的“Real World Haskell”一书中找到了一个例子。请在 The discriminated union 部分查看。

干杯!


A
Avag Sargsyan

是的, struct 和 union 之间的主要区别与您所说的相同。 struct 使用其成员的所有内存,而 union 使用最大的成员内存空间。

但所有区别都在于内存的使用需求。在我们使用信号的unix进程中可以看到联合的最佳用法。就像一个进程一次只能作用于一个信号。所以一般声明将是:

union SIGSELECT
{
  SIGNAL_1 signal1;
  SIGNAL_2 signal2;
  .....
};

在这种情况下,进程只使用所有信号的最高内存。但如果在这种情况下使用 struct ,内存使用量将是所有信号的总和。有很大的不同。

总而言之,如果您知道您一次访问任何一个成员,则应该选择 Union。


f
friol

“union”和“struct”是 C 语言的结构。谈论它们之间的“操作系统级别”差异是不恰当的,因为如果您使用一个或另一个关键字,编译器会产生不同的代码。


P
Piotr Lesnicki

你有它,仅此而已。但是,基本上,工会的意义何在?

您可以在同一位置放置不同类型的内容。您必须知道您在联合中存储的内容的类型(通常您将其放在带有类型标签的 struct 中......)。

为什么这很重要?不是真的为了空间收益。是的,您可以获得一些位或做一些填充,但这不再是重点。

这是为了类型安全,它使您能够进行某种“动态类型”:编译器知道您的内容可能具有不同的含义,而您在运行时如何解释它的确切含义取决于您。如果你有一个可以指向不同类型的指针,你必须使用联合,否则你的代码可能会因为别名问题不正确(编译器对自己说“哦,只有这个指针可以指向这个类型,所以我可以优化出那些访问......”,并且可能会发生坏事)。


s
skanda93

非技术上的意思是:

假设:椅子=内存块,人=变量

结构:如果有3个人,他们可以坐在相应大小的椅子上。

工会:如果三个人只有一张椅子可以坐,大家想坐的时候都需要用同一张椅子。

从技术上讲意味着:

下面提到的程序深入探讨了结构和联合。

struct MAIN_STRUCT
{
UINT64 bufferaddr;   
union {
    UINT32 data;
    struct INNER_STRUCT{
        UINT16 length;  
        UINT8 cso;  
        UINT8 cmd;  
           } flags;
     } data1;
};

总 MAIN_STRUCT 大小 =sizeof(UINT64) for bufferaddr + sizeof(UNIT32) for union + 32 bit for padding(取决于处理器架构)= 128 bits 。对于结构,所有成员连续获取内存块。

Union 获得一个最大大小成员的内存块(这里是它的 32 位)。在联合内部还有一个结构(INNER_STRUCT),它的成员得到一个总大小为 32 位(16+8+8)的内存块。在联合中,可以访问 INNER_STRUCT(32 位)成员或数据(32 位)。


C
Christian C. Salvadó

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

联合仅分配其最大成员所需的内存。


您可能还想添加联合成员相互“重叠”,因为它们都从分配的联合“结构”的起始地址开始。
A
Abdus Sattar Bhuiyan

结构和联合有什么区别?

捷径的答案是:尊重在于内存分配。说明:在结构体中,会为结构体内部的所有成员创建内存空间。在联合中,只会为需要最大内存空间的成员创建内存空间。考虑以下代码:

struct s_tag
{
   int a; 
   long int b;
} x;

union u_tag
{
   int a; 
   long int b;
} y;

这里 struct 和 union 内部有两个成员:int 和 long int。 int 的内存空间是:4 字节,long int 的内存空间是:8 在 32 位操作系统中。

所以对于 struct 4+8=12 bytes 将被创建,而 8 bytes 将为 union 创建

代码示例:

#include<stdio.h>
struct s_tag
{
  int a;
  long int b;
} x;
union u_tag
{
     int a;
     long int b;
} y;
int main()
{
    printf("Memory allocation for structure = %d", sizeof(x));
    printf("\nMemory allocation for union = %d", sizeof(y));
    return 0;
}

参考:http://www.codingpractise.com/home/c-programming/structure-and-union/


A
Ahmed

当需要专门类型的对话时,经常使用联合联合。了解联合的用处。 c/c 标准库没有定义专门用于将短整数写入文件的函数。使用 fwrite() 会导致简单操作的额外开销。但是,使用联合,您可以轻松创建一个函数,该函数一次将一个短整数的二进制写入文件一个字节。我假设短整数是 2 字节长

这个例子:

#include<stdio.h>
union pw {
short int i;
char ch[2];
};
int putw(short int num, FILE *fp);
int main (void)
{
FILE *fp;
fp fopen("test.tmp", "wb ");
putw(1000, fp); /* write the value 1000 as an integer*/
fclose(fp);
return 0;
}
int putw(short int num, FILE *fp)
{
pw word;
word.i = num;
putc(word.c[0] , fp);
return putc(word.c[1] , fp);
}    

虽然 putw() 我用短整数调用,但可以使用 putc() 和 fwrite()。但我想展示一个例子来说明如何使用联合


Y
Yun

结构是不同数据类型的集合,其中可以驻留不同类型的数据,并且每个数据都有自己的内存块。

当我们确定一次只使用一个变量并且您希望充分利用当前内存时,我们通常使用联合,因为它只获得一个等于最大类型的内存块。

struct emp
{
    char x; //1 byte
    float y; //4 byte
} e;

它获得的总内存:=> 5 个字节。

union emp
{
    char x; //1 byte
    float y; //4 byte
} e;

它获得的总内存:4 个字节。


A
Aniket Suryavanshi

在编写下面给出的字节排序函数时,联合会派上用场。结构是不可能的。

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.

3
3 revs, 3 users 50%

Union 与结构不同,因为 Union 在其他方面重复:它重新定义相同的内存,而结构一个接一个地定义,没有重叠或重新定义。


Y
Yuvraj Singh Jadon

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

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

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

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

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

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

我发现以下文章很好地解释了它:Difference Between Structure and Union


K
Keno

有没有很好的例子来说明结构和联合之间的区别?

来自嵌入式系统应用程序的真实示例如下。它仅使用 union,但清楚地显示了 union 的功能。

I2C 通信协议的写函数使用 union 类型,用于数据存储,当从传递给它的数组中检索数据时。

union data
{
    uint32_t packet;
    uint8_t  packetbyte[4];
} txdata;

通过指针传递给写入函数的数组包含一个字节大小的元素。在 for 循环中,这些字节在四个步骤中被一一提取并存储到 txdata.packetbyte 成员的各个元素中。

循环结束后,txdata.packet 保存 4 个字节的数据,这些数据被连续存储到 txdata 联合中。作为通过通信总线发送数据的最后一步,txdata.packet 被写入 32 位缓冲区,写入后启动写入序列。然后在下一个 for 循环开始执行之前通过 txdata.packet = 0 重置内容。

这样 I2C 主机可以重新传输 32 位数据包,直到发送传递的输入数据并终止写入功能。