【问题标题】:Bit field's memory management in CC中位域的内存管理
【发布时间】:2015-09-20 08:11:47
【问题描述】:

为了理解位域内存存储,我创建了下面的测试程序。

#include <stdio.h>

int main()
{
    int a;
    typedef struct {
        int b7 : 1;
        int b6 : 1;
        int b5 : 1;
        int b4 : 1;
        int b3 : 1;
        int b2 : 1;
        int b1 : 1;
        int b0 : 1;
    } byte;

    byte ab0 = {0,0,0,0,0,0,0,1};
    a = *(int*)&ab0;
    printf("ab0 is %x \n",a);

    byte ab1 = {0,0,0,0,0,0,1,0};
    a = *(int*)&ab1;
    printf("ab1 is %x \n",a);

    byte ab2 = {0,0,0,0,0,1,0,0};
    a = *(int*)&ab2;
    printf("ab2 is %x \n",a);

    byte ab3 = {0,0,0,0,1,0,0,0};
    a = *(int*)&ab3;
    printf("ab3 is %x \n",a);

    byte ab4 = {0,0,0,1,0,0,0,0};
    a = *(int*)&ab4;
    printf("ab4 is %x \n",a);

    byte ab5 = {0,0,1,0,0,0,0,0};
    a = *(int*)&ab5;
    printf("ab5 is %x \n",a);

    byte ab6 = {0,1,0,0,0,0,0,0};
    a = *(int*)&ab6;
    printf("ab6 is %x \n",a);

    byte ab7 = {1,0,0,0,0,0,0,0};
    a = *(int*)&ab7;
    printf("ab7 is %x \n",a);

    return 0;
}

编译运行

gcc -Wall test.c
./a.out 
ab0 is 80 
ab1 is 40 
ab2 is 20 
ab3 is 10 
ab4 is 8 
ab5 is 4 
ab6 is 2 
ab7 is 1 

当我在线运行代码时,输​​出相同 http://codepad.org/ntqyuixp

我无法理解它的输出。

预期输出: 根据我的理解,输出应该是

ab0 is 1 
ab1 is 2 
ab2 is 4 
ab3 is 8 
ab4 is 10 
ab5 is 20 
ab6 is 40 
ab7 is 80
  • 请让我知道我缺少什么。

  • 字节序有什么作用吗?

  • 应如何为我的预期行为编写代码?

【问题讨论】:

  • 我知道您只是为了实验而这样做,但是您将转换为 int* 然后取消引用的方法是错误的,如果您将其应用到生产代码中,将会给您带来很多麻烦.不要这样做。标准预见的进行此类检查的方法是union

标签: c linux memory


【解决方案1】:

位域中位的顺序由实现定义。该实现的定义与您预期的不同——这几乎就是所有关于它的内容。

几乎所有关于位域的内容都是由实现定义的。

ISO/IEC 9899:2011 §6.7.2.1 结构和联合说明符

¶4 指定位域宽度的表达式应为整数常量 具有不超过对象宽度的非负值的表达式 如果省略了冒号和表达式,将指定的类型。122) 如果值为 零,声明不应有声明符。

¶5 位域的类型应为_Boolsigned intunsigned int 或其他一些实现定义的类型的合格或非合格版本。这是 实现定义是否允许原子类型。

¶9 结构或联合的成员可以具有任何完整的对象类型,而不是 可变修改类型。123) 此外,成员可以声明为由 指定的位数(包括符号位,如果有的话)。这样的成员称为 bit-field;124) 其宽度以冒号开头。

¶10 位域被解释为有符号或无符号整数类型,包括 指定位数。125) 如果值 0 或 1 存储到 类型_Bool,位域的值应与存储的值比较; _Bool 位域具有_Bool 的语义。

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

¶12 没有声明符但只有冒号和宽度的位域声明表示 未命名的位域。126) 作为一种特殊情况,宽度为 0 的位域结构成员表示不再将位域打包到前一个位域所在的单元中,如果有,已放置。

122) 虽然_Bool 对象中的位数至少为CHAR_BIT,但_Bool 的宽度(符号和值位数)可能只有1 位.

123) 结构或联合不能包含具有可变修改类型的成员,因为成员名称不是 6.2.3 中定义的普通标识符。

124) 一元&amp; (address-of) 运算符不能应用于位域对象;因此,没有指向位域对象的指针或数组。

125) 如上面6.7.2所述,如果实际使用的类型说明符是int或定义为int的typedef-name,则该位是否为实现定义- 字段有符号或无符号。

126) 未命名的位域结构成员可用于填充以符合外部强加的布局。

特别注意¶11:

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

还要注意,“实现定义”意味着实现必须定义它的作用。也就是说,您可以检查文档,并且文档必须告诉您编译器做了什么(如果编译器符合标准)。这与“未指定”以及您将遇到的其他一些术语不同——编译器编写者几乎肯定不会随意更改位域的行为。相比之下,评估函数参数的方式,例如,可能会因版本而异,或者根据编译时选择的优化选项等而变化。

§6.5.2.2 函数调用

在函数指示符和实际参数的评估之后但在实际调用之前有一个序列点。调用函数(包括其他函数调用)中的每个求值,如果在被调用函数的主体执行之前或之后没有特别排序,则相对于被调用函数的执行是不确定的。94)支持>

94) 换句话说,函数执行不会相互“交错”。

6.7.2 类型说明符

5 每个逗号分隔的多重集都指定相同的类型,除了位域,说明符int 指定与signed int 相同的类型还是与unsigned int 相同的类型是实现定义的.

【讨论】:

  • 你的意思是它会因编译器而异?我不应该依赖这个?
  • @JonathanLeffler:我认为位域内存管理因机器而异,而不是因编译器而异。不同机器上的相同编译器可能会表现不同,但否则不会。
  • 您需要查看机器(硬件和操作系统组合)的 ABI(应用程序二进制接口)是否要求位域布局。如果是这样,那么编译器很可能都符合 ABI。如果 ABI 是静默的,不同的编译器可以随心所欲; C 标准说他们可以并且 ABI 没有否决它。您仍然需要阅读编译器文档以了解它是否符合 ABI(以及它的作用,无论它遵循哪个标准)。
  • GCC 文档指出单元内位域的分配顺序[是]由 ABI 确定 [4.9 结构、联合、枚举和位- fields],ELF x86_64 psABI 声明 位域从右到左分配。 [3.1.2 数据表示]
  • @ninjalj:谢谢——这是有用的信息。很想嘲笑“这就是标准的美妙之处;有这么多可供选择的东西”。很难知道哪个标准影响了编译器的每个决定。
【解决方案2】:

位字段是不可移植的并且依赖于机器。

使用位域的限制

使用位域时,请注意以下问题:

  1. 代码将不可移植,因为 bits-within-bytes 和 bytes-within-words 是机器相关的
  2. 您不能获取位域的地址;所以表达式 &mystruct.x 如果 x 是位字段标识符是非法的,因为有 不能保证 mystruct.x 位于字节地址。
  3. 位域用于将更多变量打包成更小的数据 空间,但会导致编译器生成额外的代码 操纵这些变量。这在代码大小和 执行时间。

【讨论】:

  • 1.代码是可移植的,只要它是内部数据结构,并且您不需要额外检查位的顺序。
  • 位域一个标准的C工具,所以只要你使用标准提供的,你的代码就可以移植了。
【解决方案3】:

对于 x86 上的 linux,可以找到相关的 ABI 文档here(pdf)

具体与位域相关:

“普通”位域(即既不是signed 也不是unsigned)总是有非负值。尽管它们可能具有类型charshortintlong(可以有负值),但这些位域与具有相同大小的位域具有相同的范围和对应的@ 987654328@类型。

位域遵循与其他结构和联合成员相同的大小和对齐规则,并添加以下内容:

位域从右到左分配(从最低到最高)。 位域必须完全驻留在适合其声明的存储单元中 类型。因此,位域永远不会越过它的单元边界。

位域可以与其他struct/union成员共享一个存储单元, 包括不是位域的成员。当然,struct 会员 占据存储单元的不同部分。 未命名位域的类型不影响结构的对齐方式或 union,虽然各个位域的成员偏移量服从对齐 约束。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-06
    • 2018-12-17
    • 2023-04-08
    • 2013-04-13
    • 2011-06-28
    相关资源
    最近更新 更多