我需要使用不同的 C 结构(网络头描述符)对内存中一个字节的 2 位(来自线路)进行相同的解释。这是最小的可重现示例:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
struct some {
uint8_t reserved : 6;
uint8_t flags : 2;
} __attribute__((packed));
struct some2 {
uint16_t b : 12;
uint16_t reserved : 2;
uint16_t flags : 2;
} __attribute__((packed));
int main(void)
{
char *ptr = malloc(8);
ptr[0] = 0xC0;
ptr[1] = 0x00;
struct some *hdr = (struct some *)ptr;
printf("%lu %u\n", sizeof(struct some), hdr->flags);
struct some2 *hdr2 = (struct some2 *)ptr;
printf("%lu %u\n", sizeof(struct some2), hdr2->flags);
return 0;
}
输出:
1 3
2 0
将第二个结构体中的 flags 解释为 0 的原因是什么? 在 C 中使用位字段时的字段顺序 假设
C 标准允许编译器以任意顺序放置位域。没有 可靠且便携的方式来确定订单。
但是编译器代码没有通过位域机制,这只是来自网络的一个字节,我正在通过位域机制解释它..
在
struct some
中,您已告诉编译器布置一个六位字段和一个两位字段。在 struct some2
中,您已告诉编译器布置一个 12 位字段、一个两位字段和另一个两位字段。
编译器这样做了。对于第一个结构,它将六位
reserved
字段布局为第一个(也是唯一)字节的位 0-5,并将两位 flags
字段布局为该字节的位 6-7 。 (这里,位的编号为 0 作为二进制数字解释时具有最低有效值的位置,7 为最高有效值。)
对于第二个结构,它将 12 位
b
字段布局为第一个字节的位 0-7 和第二个字节的位 0-3(等效地,由 uint16_t
形成的位 0-11)两个字节(按小端顺序),两位 reserved
字段作为第二个字节的位 4-5,两位 flags
字段作为第二个字节的位 6-7。
在
hdr->flags
中,您使用struct some
类型访问了一个字节,该字节已设置为C016。这将字节的第 6-7 位解释为两位
flags
字段,产生 3.
在
hdr2->flags
中,您使用struct some2
类型访问了两个字节,其中第二个字节尚未设置为任何值。这将第二个字节的第 6-7 位解释为两位 flags
字段。该字节显然在这些位中包含零,至少在效果上是这样,产生 0。
如果您想依赖编译器的位字段布局,那么您需要以不同的方式定义结构,以便
flags
的 struct some2
字段对应于第一个字节的位 6-7。或者,您可以使用字符类型访问字节并使用移位运算符提取位 6-7。
在具有 8 位
char
的小端机器上,uint16_t
将为 0x00C0。
如果字段按从最不重要到最重要的顺序排列,
hdr->flags
相当于
( x >> 6 ) & 0x3 # 3
hdr2->flags
相当于
( x >> ( 12 + 2 ) ) & 0x3 # 0