C 联合比特场铸造

C 联合比特场铸造,c,unions,bit-fields,C,Unions,Bit Fields,我不明白在施法位域时会发生什么 假设我们有一个联盟和一个例子: union { unsigned char data; int d : 3; unsigned char m : 3; }x; int main() { x.data = 182; // 182 (binary) -> 1 0 1 1 0 1 1 0 printf("sizeof(x) = %lu\n", sizeof(x)); printf("x.data = %d\n", x.data);

我不明白在施法位域时会发生什么

假设我们有一个联盟和一个例子:

union {
  unsigned char data;
  int d : 3;
  unsigned char m : 3;
}x;

int main() {
  x.data = 182;
  // 182 (binary) -> 1 0 1 1 0 1 1 0
  printf("sizeof(x) = %lu\n", sizeof(x));
  printf("x.data = %d\n", x.data);
  printf("x.d = %d\n", x.d);
  printf("x.m = %d\n", x.m);
  printf("(unsigned char)x.d = %d\n", (unsigned char)x.d);
  printf("(signed char)x.d = %d\n", (signed char)x.d);
  printf("(signed char)x.m = %d\n", (signed char)x.m);
  printf("(unsigned char)x.m = %d\n", (unsigned char)x.m);
  return 0;
}
这是输出:

/*
sizeof(x) = 4
x.data = 182
x.d = -2
x.m = 6
(unsigned char)x.d = 254 //?
(signed char)x.d = -2    //?
(signed char)x.m = 6     //?
(unsigned char)x.m = 6   //?
*/
现在,我理解了
x.data
x.d
,和
x.m
输出,但我不理解的是我们在施法时得到的结果

当铸造时,记忆中会发生什么?为什么我们会得到这些结果:

  • (无符号字符)x.d
    =254
  • (签名字符)x.d
    =-2
  • (有符号字符)x.m
    =6
  • (无符号字符)x.m
    =6
编辑:
我不明白的是,这在内存中是如何处理的,以及在铸造时读取哪些零件。我把182放在x.data里,二进制的是10110。x、 data、x.m和x.d为我提供了预期的结果,但为什么(unsigned char)x.d返回254?为什么它不返回182,因为x.data和x.d在同一个内存位置,我将x.d转换为与x.data类型相同的无符号字符

通过一个成员设置
联合
并读回另一个
联合
成员的行为在C中未定义

因此,试图分析这种特殊情况下的行为是毫无意义的

您应该使用基于
memcpy
的解决方案进行重建:至少这样输出将是可分析的


另外,请注意,
%zu
sizeof
值的适当格式说明符:目前,
printf
行为也未定义,因为您使用了
%lu
x.d
x.m
通过强制转换不会显示任何更改,这是意料之中的
x.d
在这两种情况下都具有相同的位模式,但当它被视为有符号时,被解释为-2,当这些相同的位被解释为无符号时,被解释为254


如果您在联合体中读写一个变量,这些变量分组在一个联合体中这一事实将节省空间,但当您写一个变量又读另一个变量时,这是未定义的行为。

特别是最后一句话,请在第二段中提供引用。最新的标准允许类型双关语,但有一些警告。它不是未定义的,它是未指定的(在附录J中提到,涉及便携性问题)。未指定稍后解释为实现定义(这是完全不同的)。我不理解的是如何在内存中处理这一点,以及在强制转换时读取哪些部分。我把182放在x.data里,二进制的是10110。x、 data、x.m和x.d为我提供了预期的结果,但为什么(unsigned char)x.d返回254?为什么它不返回182,因为x.data和x.d在同一个内存位置,并且我将x.d转换为与x.data类型相同的无符号字符。@dante,因为您已经明确指出
d
m
都是只有3位的位字段。因此,这些字段中的数据仅为位“110”。这可以解释为6或-2,这取决于它是否已签名。如果将其强制转换为(unsigned char),它将首先进行符号扩展,然后报告为unsigned char 1111111 0,十进制为254。Undefined并不等于无意义。这是未定义的行为,因为它依赖于架构。(主要是因为持久性和内存对齐)。在x86上,它是完全可预测的,可以使用和被使用。@xvan:IMHO你的有用评论确实具有一定的合法性:引用联合读写的不确定性总是引起注意。但是编译器的角色是决定做什么,而不是体系结构。说真的,别冒险。
memcpy
方法不再复杂,而且是正确的做法。@xvan我不同意。当我将一些代码从一个编译器移植到同一台机器上的另一个编译器并获得不同的行为时,我以艰难的方式学到了这一课。问题是这两个编译器采用了不同的方法来对齐联合和结构中的字段。两者都是完全有效的。请为第一段提供引文。最新标准允许使用带有某些警告的类型双关。@Logicrat这在C99第6.7.2.1节中不应该发生[…]一个指向联合对象的指针,经过适当转换,指向它的每个成员(或者如果一个成员是位字段,则指向它所在的单元),反之亦然。在结构中声明只有一个位范围的位字段是毫无意义的(例如,
int d:3;
,可以是
int d;
)一个位域允许在一个无符号类型内指定多个范围。@DavidC.Rankin我知道这样做没有意义,但我正在为一个没有太多意义的比赛而练习。