关于C中比特字排序语义的澄清

时间:2013-09-06 07:22:50

标签: c struct bit-fields memory-layout

我很难理解C99标准草案(N1256)关于位域(6.7.2.1:10)的确切含义:

  

6.7.2.1结构和联合说明符

     

[...]

     

语义

     

[...]

     

实现可以分配足够大的任何可寻址存储单元来保存位字段。如果剩余足够的空间,则紧跟在结构中的另一个位字段之后的位字段将被打包到相同单元的相邻位中。如果剩余的空间不足,则是否将不适合的位域放入下一个单元或重叠相邻单元是实现定义的。 单元内位域(高位到低位或低位到高位)的分配顺序是实现定义的。 对齐可寻址存储单元未指定。

强调的句子将我的英语技能提升到极限:我不明白它是指单元内的各个位字段,还是单个位字段内的位排序或其他内容。

我会试着通过一个例子让我更加清楚。假设无符号整数是16位,实现选择unsigned int作为可寻址存储单元(并且字节宽度为8位),并且不会出现其他对齐或填充问题:

struct Foo {
    unsigned int x : 8;
    unsigned int y : 8;
};

因此,假设xy字段存储在同一单元内,那么根据该句子实现定义的是什么?据我了解,这意味着在无符号int单元内,x可以存储在低于y的地址,反之亦然,但我不确定,因为直觉上我是认为如果没有位字段与两个底层存储单元重叠,则声明顺序将对底层位字段强加相同的顺序。

注意:我担心我在这里遗漏了一些术语(或者更糟糕的是,一些技术性的),但我无法理解它。

任何指针都赞赏。谢谢!

3 个答案:

答案 0 :(得分:6)

我真的没有看到

的不清楚
  

单元内位域分配的顺序(高位到   低阶或低阶到高阶)是实现定义的。

它讨论了位字段的分配,而不是字段内的位。因此,除了非位字段成员之外,您无法确定可寻址单元内的位字段的排序顺序。

否则,位字段本身的表示保证与基础类型“相同”,并且分为值位和符号位(如果适用)。

本质上它说包含位字段的存储单元的解剖结构是实现定义的,你不应该试图通过其他方式(union左右)来访问这些位,因为这会使你的代码不可移植。

答案 1 :(得分:1)

我的看法是,C99规范讨论的是位字段的位端,以及它们是如何以“单位”(字节字等)排序的。如果你开始施放结构,基本上你就是你自己。

实施例

bit  ex1    ex2   ex3
D7   x3     y0    x0
D6   x2     y1    x1
D5   x1     y2    x2
D4   x0     y3    x3
D3   y3     x0    y0
D2   y2     x1    y1
D1   y1     x2    y2
D0   y0     x3    y3

以上三种不同的方案,用于对字节“单元”中的两个4位字段进行排序。就C99标准而言,所有这些都是合法的。

答案 2 :(得分:1)

Gibbon1的回答是正确的,但我认为示例代码对这类问题很有用。

#include <stdio.h>

int main(void)
{
    union {
        unsigned int x;
        struct {
            unsigned int a : 1;
            unsigned int b : 10;
            unsigned int c : 20;
            unsigned int d : 1;
        } bits;
    } u;

    u.x = 0x00000000;
    u.bits.a = 1;
    printf("After changing a: 0x%08x\n", u.x);
    u.x = 0x00000000;
    u.bits.b = 1;
    printf("After changing b: 0x%08x\n", u.x);
    u.x = 0x00000000;
    u.bits.c = 1;
    printf("After changing c: 0x%08x\n", u.x);
    u.x = 0x00000000;
    u.bits.d = 1;
    printf("After changing d: 0x%08x\n", u.x);

    return 0;
}

在使用MinGW&GCC的小端x86-64 CPU上,输出为:

  

更改后:0x00000001

     

更改b:0x00000002

后      

更改c:0x00000800

后      

更改d:0x80000000

由于这是一个联合,因此unsigned int(x)和位字段结构(a / b / c / d)占用相同的存储单元。 [em]位域的分配顺序决定u.bits.a是指x的最低有效位还是x的最高有效位。通常,在小端机器上:

u.bits.a == (u.x & 0x00000001)
u.bits.b == (u.x & 0x000007fe) >> 1
u.bits.c == (u.x & 0xeffff800) >> 11
u.bits.d == (u.x & 0x80000000) >> 31

并在大端机器上:

u.bits.a == (u.x & 0x80000000) >> 31
u.bits.b == (u.x & 0x7fe00000) >> 21
u.bits.c == (u.x & 0x001ffffe) >> 1
u.bits.d == (u.x & 0x00000001)

标准所说的是C编程语言不需要任何特定的字节序 - 大端和小端机器可以按照其寻址方案最自然的顺序放置数据。