2017-06-06 71 views
2

我正在进入C和我试验联盟。我的代码如下:关于联合和多个整数值

#include <stdio.h> 

union date { 
int year ; 
char month; 
char day ; 
}; 

int main() { 
union date birth; 
birth.year = 1984; 
birth.month = 7; 
birth.day = 28; 
printf("%d, %d, %d\n",birth.year, birth.month, birth.day); 
// return 1820,28,28 
return 0; 
} 
  • 1984被写入二进制为0111 1100 0000

  • 7被写入二进制如0110

  • 28被写入二进制如0001 1100

我明白,因为工会,birth.year的值为0111 0001 1100这是1820.但我不明白为什么birth.month返回值28. 28.

+1

我想你只能访问最后修改的值,不是吗? –

+0

@SouravGhosh:这是一个棘手的问题。我将C标准解释为保证你可以使用工会来进行类型窜改(比如'memcpy()')。 – EOF

+0

@ L.M你期望'month'包含什么? (没有双关语意,但你应该提及你期望看到的内容) – rustyx

回答

2

引用C11,章§6.7.2.1,(重点煤矿

工会的大小足以容纳最大的成员。 在 的值可以随时存储在联合对象中的大多数成员。指向 联合对象的指针经过适当转换后指向其每个成员(或者如果成员是位域,则返回到其所在的单位),反之亦然。

所以,你期望的基础是错误的。你不能同时拥有全部的值,你只能有一个。

此外,从章节§6.5.2。3,脚注95,

如果构件用于读取联合对象的内容是不一样最后在对象用来 存储的值的构件,该对象的适当部分如6.2.6(有时称为'' punning'')所述的新类型中的对象表示将该值的表示重新解释为 。这可能是一个陷阱表示。

这里,最后分配的值,day恰好是那的month同样大小的,所以当你尝试读取day,它返回的month的价值。

+1

非常感谢。那就是我不明白的地方。 –

+0

@ L.M不客气。 :)你也可以考虑[接受一个帮助你的答案](https://meta.stackexchange.com/q/5234/244062),这是StackOverflow表达谢意的方式。 –

4

我认为你误解了工会的目的。如果您需要一个存储一组属性的对象(例如d.year,d.month,d.day),则需要一个结构。

简而言之,工会允许您将多种不同类型中的一种放入单个变量中。比如说你正在实现一个文件系统。假设您需要一个变量current_block,它可以指代超级块或数据块,分别由struct super_blockstruct data_block定义。然后,你可以这样做:

union block_generic{ 
    struct super_block; 
    struct data_block; 
} 

union block_generic current_block; 

现在current_block可以是super_block的或DATA_BLOCK。

编辑:只是想添加关于工会的实际使用快速补充。继续上面的例子,为了将current_block视为一个超级块,例如,为了访问文件系统的inode数量,你会这样做current_block.super_block.n_inodes(我的意思是指出你不直接处理union变量,您指定“类型的帽子,”可以这么说,应该佩戴。

0

这完全误解了工会是什么。联合是所有从相同内存位置开始的值的数组;如果你想在一个数据类型中存储几个单独的值,你需要使用什么结构。

例如,工会,你可以这样做:

#include <stdio.h> 
#include <stdint.h> 

typedef union _myunion 
{ 
    int32_t s; 
    uint32_t u; 
} myunion; 

int main() 
{ 
    myunion u; 
    u.s = -1; 
    printf("%d %u\n", u.s, u.u); 
    return 0; 
} 

输出:

-1 4294967295 

基本上,你分配一个值,所有工会会员获取价值;如果它用不同的类型表示,就这样吧。

在一个结构体中,每个变量占用自己的内存位置,所以如果你想在结构中存储一个完整的日期,包括年份,月份和日期作为单独的变量,你可以没有任何问题。

1

联合将其所有成员存储在同一空间中,并且该空间中存在的数据对应于最后写入的任何成员。一个工会和其最大的成员一样大。你没有提到你的具体平台,但假设在现代的x86 Windows,MacOS或桌面Linux上有一个相当新的GCC,char可能是8位,而int可能是32位,使得你的日期联合看起来像这样:

0000 0000 0000 0000 0000 0000 0000 0000 
\_______________ year ________________/ 
\ month/
\_ day _/ 

让我们通过您的使用union date birth,我们?与birth.year = 1984;开始,我们(请记住,86是little endian

1100 0000 0000 0111 0000 0000 0000 0000 
\_______________ year ________________/ 
\ month/
\_ day _/ 

然后birth.month = 7;

0000 0111 0000 0111 0000 0000 0000 0000 
\_______________ year ________________/ 
\ month/
\_ day _/ 

最后,birth.day = 28;

0001 1100 0000 0111 0000 0000 0000 0000 
\_______________ year ________________/ 
\ month/
\_ day _/ 

应该明确的是,写作给工会的任何成员将覆盖所有其他成员的至少一部分。

对比一个struct,它对所有成员都有唯一的存储空间,使其至少与所有成员一起添加(与填充相比可能更大)。如果您有struct替换的union所有实例的示例代码,你会得到的东西,看起来像这样在内存中底:

1100 0000 0000 0111 0000 0000 0000 0000 0000 0111 0001 1100 
\_______________ year ________________/ \ month/\_ day _/ 

每个成员的数据是完好无缺,并可以与正确检索birth.yearbirth.monthbirth.day