c - 不能取位域的地址

sle*_*dog 14 c pointers syntax-error memory-address bit-fields

为什么不能取位域的地址?

如何制作指向位字段的指针?

这是代码......

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;
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*lia 29

位域成员(典型地)大于由指针所允许的粒度,这是的粒度较小的charS(由定义char,其中的方式的任务是为8位长的至少).因此,常规指针不会削减它.

此外,还不清楚指向位域成员的指针的类型是什么,因为要存储/检索这样的成员,编译器必须确切地知道它在位域中的位置(并且没有"常规"指针类型可以携带此类信息).

最后,它几乎不是一个要求的功能(首先不会经常看到位域); bitfields用于紧凑地存储信息或构建标志的打包表示(例如写入硬件端口),很少需要指向它们的单个字段的指针- 如果需要,你总是可以求助于常规struct并在最后一刻转换为位域.

由于所有这些原因,标准表明位域成员不可寻址,周期.这可能可以克服这些障碍(例如,通过定义存储访问位域成员所需的所有信息专项指针类型),但它会是又没有人使用的语言的另一过于复杂阴暗的角落.

  • +1,_"和没有"常规"指针类型可以携带这样的信息"_最终有人指出它不仅仅是_"标准说......"_ (7认同)
  • 实际上在64位系统上,指针可以引用特定位.截至2017年,指针仅使用64位中的48位,英特尔计划在不久的将来展开使用56位的硬件.因此,您可以使用最高3位来编码字节内的位偏移,并使用一些位操作对内部的所有内容进行编码(void*)或者键入特定的指针类型.您可以使用C样式的预编译器宏或C++中的模板来执行此操作.但是没有理由在标准中支持这种奇怪的功能.需要它的人可以实现它 (3认同)
  • Ps通常滥用64位架构中指针内的空闲位.Linux使用其中一些来编码指针+错误代码在单个(void*)中作为结构体构造函数的返回值.内核slab内存分配器还使用指针的最高8位来存储附加数据.但话又说回来,这是滥用一种不是标准的类型 (2认同)

pax*_*blo 7

您不能拥有位字段的地址,因为最小的可寻址单位是一个字节(记住C中的字节可能不一定是8位宽).

您可能希望的最好的是包含结构的地址.

(C11)标准的相关部分是6.5.3.2 Address and indirection operators(我的斜体)部分:

一元运算&符的操作数应该是函数指示符,[]一元或一元运算*符的结果 ,或者lvalue指定一个不是位字段且不用寄存器存储类说明符声明的对象.

鉴于最小的可寻址性是一个字节,你可能会发现你的bitfileds被压缩:

Addr\Bit   7   6   5   4   3   2   1   0
00001234 | a | b | c | d | ? | ? | ? | ? |
00001235 |   |   |   |   |   |   |   |   |
Run Code Online (Sandbox Code Playgroud)

你可以看到所有那些bitfileds的地址实际上是相同的,所以它并没有那么有用.

对于操作bitfileds,你真的应该直接访问它们并让编译器对它们进行排序.除非您知道编译器如何将它们放在内存中,否则即使使用按位运算符也不能保证工作.


1''*_*1'' 5

地址必须是整数个字节,但不一定是位字段,因此C标准指定地址运算符&不能与它们一起使用.当然,如果你真的想用位域地址做事情,你可以使用封闭结构的地址,并进行一些按位操作.