为什么不能取位域地址?
如何创建指向位域的指针?
这是代码...
struct bitfield {
unsigned int a: 1;
unsigned int b: 1;
unsigned int c: 1;
unsigned int d: 1;
};
int main(void)
{
struct bitfield pipe = {
.a = 1, .b = 0,
.c = 0, .d = 0
};
printf("%d %d %d %d\n", pipe.a,
pipe.b, pipe.c, pipe.d);
printf("%p\n", &pipe.a); /* OPPS HERE */
// error: cannot take address of bit-field ...
return 0;
}
Bitfields 成员(通常)小于指针允许的粒度,即
char
的粒度(通过 char
的 definition,顺便说一下,它的长度至少为 8 位)。所以,普通的指针并不能解决这个问题。
此外,还不清楚指向位域成员的指针的类型是什么,因为要存储/检索这样的成员,编译器必须确切知道它在位域中的位置(并且没有“常规”指针类型)可以携带这样的信息)。
最后,这几乎不是一个被请求的功能(位域首先并不常见);位字段用于紧凑地存储信息或构建标志的打包表示(例如写入硬件端口),您很少需要指向其中的“单个字段”的指针 - 如果需要,您始终可以求助于常规 struct
并在最后一刻转换为位字段。
出于所有这些原因,该标准规定位字段成员不可寻址,就这样。克服这些障碍是可能的(例如,通过定义特殊的指针类型来存储访问位域成员所需的所有信息),但这将是无人使用的语言的另一个过于复杂的黑暗角落。
您实际上无法拥有
位字段的地址,因为 C 中的最小可寻址单元是字节(请记住,C 中的字节不一定是 8 位宽)。
标准的相关部分(本例中为 C11)是第 6.5.3.2 Address and indirection operators
一元
&
运算符的操作数应为函数指示符、[]
或一元你可以看到所有这些位域的地址实际上是相同的(1234),所以区分它们并不是很有用..
*
运算符的结果,或者指定不是位域的对象的lvalue
并且未使用寄存器存储类说明符进行声明。鉴于寻址单位是字节,您可能会发现位字段存储为(例如):Bit 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+ Address: 1234 | a | b | c | d | ? | ? | ? | ? | +---+---+---+---+---+---+---+---+ 1235 | | | | | | | | | +---+---+---+---+---+---+---+---+
对于操作位字段,您确实应该直接访问它们并让编译器对其进行排序。即使使用按位运算符也不能保证有效,除非你
知道
编译器如何将它们布置在内存中。
地址必须是整数字节,但位字段不必如此,因此 C 标准指定
地址运算符类似地,如果您只想寻址单个字节,您可以这样做:
union PAIR {
struct { uint8_t l, h; } b;
uint16_t w;
};
PAIR ax;
编辑:固定示例