大端的 C 联合赋值

问题描述

#include <stdio.h>

union data{
    int n;
    char ch;
    short m;
};

int main(){
    union data a;
    printf("%d,%d\\n",sizeof(a),sizeof(union data) );
    a.n = 0x40;
    printf("%X,%c,%hX\\n",a.n,a.ch,a.m);
    a.ch = '9';
    printf("%X,a.m);
    return 0;
}

我的电脑是小端的,所以答案是:

4,4
40,@,40
39,9,39

但是如果机器是大端的, 当 a.n=0x40 时,a 从最低到最高将是 0x00 0x00 0x00 0x40,

a.ch='9'时,整个4字节会被覆盖还是只覆盖最低字节变成0x39 0x00 0x00 0x40?

解决方法

C 2018 6.2.6.1 7 说:

当一个值存储在联合类型对象的成员中时,不对应于该成员但对应于其他成员的对象表示的字节采用未指定的值。

几点:

  • C 实现可以选择始终分配联合的所有字节。
  • C 实现可以选择始终只分配所分配成员的字节,而其他成员保持不变。
  • C 实现在不同情况下可能会做不同的事情。例如,如果存在隔离赋值,编译器可能只将被赋值成员的字节写入内存。但是,如果分配与其他用途混合使用,并且编译器在寄存器中已经有来自先前分配成员的字节,则编译器可能会将分配优化为寄存器加载或复制,从而更改整个寄存器,然后再写入寄存器到内存,从而覆盖整个联合,而不仅仅是新分配的成员的字节。
  • 在 C 标准的定义中,“未指定值”实际上根本不是一个值;它是一种无定形状态,其中字节每次使用时可能具有不同的值。这意味着,一旦您分配了一个较小的成员,当您尝试使用不同的成员时,实现实际上不必使用内存中的其他字节——它可以使用任何方便的字节,例如处理器寄存器中的任何字节.
,

根据标准,当一个联合成员被写入时,联合的所有与该成员的表示不对应的字节都采用未指定的值。 (C17 6.2.6.1/6-7)

这些未指定的值可能会被读回为与之前保存的位置相同的值,但严格遵守的程序不能依赖于这种情况。

在实践中,您可能会找到(例如)一个编译器来实现对示例联合的部分或全部写入作为从寄存器到内存的四字节写入,因此确实为 a.ch 赋值修改多于一个字节的 a.na.m

,

您应该使用 a.n=0x11223344,这样您就可以查看编译器是否会覆盖整个结构或仅覆盖其中的一部分。