使用类型双关进行位获取。意外行为

Bit fetching with type punning. Unexpected behaviour

预期结果

该程序仅以其二进制表示形式打印 anyNum
我们通过“前端弹出”值的第一位并将其发送到标准输出来实现这一点。 在 ≤32 次迭代后 i (=anyNum) 最终会降为零。循环结束。

问题

此代码的 v1 版本产生了预期的结果 (111...),
但是,在 v2 中,当我使用 mask 结构来获取第一位时,它就像最后一位被抓取一样工作(1000.. .).
也许不是最后一次?无论如何,第二版代码中发生了什么?

#include <iostream>

typedef struct{
  unsigned b31:   1;
  unsigned rest:  31;
} mask;

int main()
{  
  constexpr unsigned anyNum= -1; //0b111...
  for (unsigned i= anyNum; i; i<<=1){
    unsigned bit;
    //bit= (i>>31); //v1
    bit= ((mask*)&i)->b31;  //v2 (unexpected behaviour)
    std::cout <<bit;
  }
}

环境

unsigned b31: 1; 是最低有效位。

Maybe not the last?

最后一个。

why

因为编译器选择这样做。顺序由编译器决定。

例如,在 GCC 上,位域中的位顺序由 BITS_BIG_ENDIAN 配置 选项控制。

x86 ABI 指定 bit-fields are allocated from right to left.

what is happing in second version of the code?

未定义的行为,例如,代码无效。您不应该 期望 代码会做任何理智的事情。编译器恰好生成了从 i.

打印最低有效位的代码

感谢@KamilCuk 我找到了答案。 在代码中做一点line-swap就可以修复它。
我用注释标记了 struct (LTR) 的内存布局和 unsigned int (RLT – Little endian) 的位布局。 没有考虑到他们是我的错误。

#include <iostream>

//struct uses left-to-right memory layout
typedef struct{
  //I've swapped the next 2 lines as a correction.
  unsigned rest:  31;
  unsigned b31:   1; //NOW `b31` is the most significant bit.
} mask;

int main()
{
  //unsigned is Little Endian on Linux-5.11.0-1029-gcp-x86_64-with-glibc2.27
  //,which is right-to-left bit layout.
  constexpr unsigned anyNum= -1; //0b111...
  for (unsigned i= anyNum; i; i<<=1){
    unsigned bit;
    //bit= (i>>31); //v1
    bit= ((mask*)&i)->b31;  //v2 (NOW expected behaviour)
    std::cout <<bit;
  }
}