关于联合和多个整数值

Posted

技术标签:

【中文标题】关于联合和多个整数值【英文标题】:Regarding union and multiple integer values 【发布时间】:2017-06-06 19:55:23 【问题描述】:

我正在学习 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。

【问题讨论】:

我猜你只能访问最后修改的值,不是吗? @SouravGhosh:这是一个棘手的问题。我将 C 标准解释为保证您可以使用联合进行类型双关(如memcpy())。 @L.M 你期望month 包含什么? (没有双关语的意思,但您应该提及您希望看到的内容) @EOF 当然可以,当然,只需要加倍小心,你最终可能会陷入陷阱。 :) 我之前没说的是“安全”。 @SouravGhosh 假设您的实现中char 等效于unsigned char,您可以安全地访问.month.day 而不必担心陷阱表示。 【参考方案1】:

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

简而言之,联合可以让您将多种不同类型中的一种放入单个变量中。例如,假设您正在实现一个文件系统。假设您想要一个变量current_block,它可以引用一个超级块或一个数据块,分别由struct super_blockstruct data_block 定义。然后你可以这样做:

union block_generic
    struct super_block;
    struct data_block;


union block_generic current_block;

现在current_block 可以是超级块或数据块。

编辑:只是想添加一个关于联合实际使用的快速附录。继续上面的例子,将current_block 视为一个超级块,例如,为了访问文件系统的 inode 数量,您将执行 current_block.super_block.n_inodes(我的意思是要指出的是,您不直接处理union 变量,你可以指定它应该戴哪个“类型的帽子”。

【讨论】:

【参考方案2】:

引用C11,第 §6.7.2.1 章,(强调我的

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

所以,您的预期基础是错误的。您不能同时为联合的所有成员拥有值,只能拥有一个。

另外,来自章节 §6.5.2.3,脚注 95,

如果用于读取联合对象内容的成员与上次使用的成员不同 在对象中存储一个值,该值的对象表示的适当部分被重新解释 作为新类型中的对象表示,如 6.2.6 中所述(有时称为“类型 双关语'')。这可能是一个陷阱表示。

这里,最后分配的值day恰好与month的大小相同,所以当你尝试读取day时,它会返回month的值。

【讨论】:

非常感谢。那是我不明白的。 @L.M 不客气。 :) 你也可以考虑accepting an answer that helped you,这是 *** 表达谢谢的方式。【参考方案3】:

联合将其所有成员存储在同一空间中,并且该空间中存在的数据对应于最后写入的任何成员。工会与其最大的成员完全一样大。您没有提及您的特定平台,但假设现代 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;开始,我们有(记住x86是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 _/

应该清楚的是,写给工会的任何成员都会覆盖所有其他成员的至少一部分。

对比一个结构,它的所有成员都有唯一的存储空间,使其至少与其所有成员加在一起一样大(填充可能更大)。如果您将示例代码中的所有union 实例替换为struct,最后您会在内存中得到如下所示的内容:

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

每个成员的数据都完好无损,可以使用birth.yearbirth.monthbirth.day 正确检索。

【讨论】:

【参考方案4】:

联合是 C 中可用的一种特殊数据类型,它允许在同一内存位置存储不同的数据类型。

也就是说,您必须一次使用一个变量。

http://www.tutorialspoint.com/cprogramming/c_unions.htm

【讨论】:

【参考方案5】:

这完全误解了联合是什么。联合是一个值数组,它们都从相同的内存位置开始;如果要在一种数据类型中存储多个单独的值,则需要使用结构。

例如,使用联合,您可以这样做:

#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

基本上,您分配一个值,所有工会成员都会获得该值;如果它以不同的类型以不同的方式表示,那就这样吧。

在一个结构体中,每个变量都占用自己的内存位置,因此如果您想将包括年、月和日在内的完整日期作为单独的变量存储在结构体中,您可以毫无问题地这样做。

【讨论】:

以上是关于关于联合和多个整数值的主要内容,如果未能解决你的问题,请参考以下文章

C++ 联合与 reinterpret_cast

C语言-结构联合枚举

如何从联合、离散、概率分布函数中进行数值采样

Django怎么多表联合查询

关于SQL中两张表联合sum和group by的查询问题

C语言中关于联合体中成员变量的问题