结构与联盟之间的区别

时间:2008-12-06 17:56:46

标签: c struct unions

是否有任何好的例子可以区分structunion? 基本上我知道struct使用其成员的所有内存,而union使用最大的成员内存空间。是否有其他操作系统级别的差异?

15 个答案:

答案 0 :(得分:637)

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

为了给出它们的使用的具体示例,我不久前在使用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个字节都是零,所以它也被读为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

答案 1 :(得分:76)

这是简短的回答:struct是一个记录结构:struct中的每个元素都分配了新的空间。所以,像

这样的结构
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)),再次为对齐添加一些可能性。

答案 2 :(得分:49)

  

是否有任何好的例子来区分'struct'和'union'?

虚构的通信协议

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

在这个虚构协议中,已经指定基于“消息类型”,标题中的以下位置将是请求号或四字符代码,但不是两者。简而言之,工会允许相同的存储位置代表多种数据类型,保证您只需要在任何时候存储一种类型的数据。

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

一般来说,操作系统并不关心或了解结构和联合 - 它们都只是简单的内存块。结构是一个存储器块,用于存储多个数据对象,这些对象不会重叠。 union是一个存储多个数据对象的内存块,但只有最大的存储块,因此任何时候都只能存储一个数据对象。

答案 3 :(得分:38)

正如您在问题中已经说明的那样,unionstruct之间的主要区别在于union成员覆盖了彼此的记忆,因此联合的大小是一个,struct成员一个接一个地布局(两者之间有可选的填充)。联合也足够大以包含其所有成员,并且具有适合其所有成员的对齐。因此,假设int只能存储在2字节地址并且宽度为2字节,而long只能存储在4字节地址并且长度为4字节。以下联盟

union test {
    int a;
    long b;
}; 

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

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

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

some_test_pointer可以指向int*bool*。如果您将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)。

答案 4 :(得分:17)

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

这方面的一个示例是使用float union来解析struct数字,其中包含位域和float。我在float中保存了一个号码,之后我可以通过float访问struct的特定部分。该示例显示了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;
}

查看维基百科上的single precision说明。我使用了这个例子和那里的幻数0.15625。


union也可用于实现具有多个备选方案的代数数据类型。我在O'Sullivan,Stewart和Goerzen的“真实世界Haskell”一书中找到了一个例子。 请在The discriminated union部分查看。

干杯!

答案 5 :(得分:11)

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

答案 6 :(得分:11)

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

但所有不同之处在于内存的使用需求。 在我们利用信号的unix过程中可以看到联合的最佳用法。 就像一个过程一次只能对一个信号起作用。 因此,一般声明将是:

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

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

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

答案 7 :(得分:11)

从技术上讲,意味着:

假设:主席=内存块,人=变量

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

联盟:如果有3个人只有一个主席可以坐下,所有人都需要在他们想坐的时候使用同一把椅子。

从技术上讲,意味着:

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

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

用于联合的bufferaddr + sizeof(UNIT32)的总MAIN_STRUCT大小= sizeof(UINT64)用于填充的32位(取决于处理器架构)= 128位。 对于结构,所有成员都连续获取内存块。

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

答案 8 :(得分:10)

你拥有它,就是这样。 但基本上,工会有什么意义呢?

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

为什么这很重要?不是为了空间收益。是的,你可以获得一些比特或做一些填充,但这不再是主要观点了。

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

答案 9 :(得分:9)

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

union只分配与其最大成员所需内存一样多的内存。

答案 10 :(得分:3)

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

我们通常使用union,当我们确定一次只使用其中一个变量并且你想要充分利用当前内存时,因为它只有一个内存块,它等于最大类型。

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

它得到的总内存=&gt; 5字节

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

它获得的总内存= 4字节

答案 11 :(得分:3)

联盟的用途 当需要专门的类型对话时,会经常使用联合。 了解工会的用处。 c / c标准库没有定义专门设计用于将短整数写入文件的函数。使用fwrite()会导致简单操作的过多开销。但是,使用union可以轻松创建一个函数,该函数一次将一个短整数的二进制写入文件。我假设短整数是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()。但我想展示一个证明如何使用联盟的例子

答案 12 :(得分:3)

结构和联盟有什么区别?

捷径答案是:顺从是内存分配。 说明: 在结构中,将为结构内的所有成员创建内存空间。 在union中,将仅为需要最大内存空间的成员创建内存空间。 请考虑以下代码:

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的内存空间为:32位操作系统中的8。

因此,对于struct 4 + 8 =将创建12个字节,同时将为union

创建8个字节

代码示例:

#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/

答案 13 :(得分:1)

在编写下面给出的字节排序函数时,联合会很方便。结构不可能。

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.

答案 14 :(得分:1)

一个联盟不同于一个结构,因为一个联盟重复了其他结构:它重新定义了相同的内存,而结构定义了一个接一个,没有重叠或重新定义。