内存中的位存储顺序

时间:2014-12-16 08:45:57

标签: c memory bit

X86机器,操作系统:Linux 2.6 RH。 这是我的代码:

    #include "stdio.h"
    typedef struct ch_t
    {
        int c0:1;
        int c1:1;
        int c2:1;
        int c3:1;
        int c4:1;
        int c5:1;
        int c6:1;
        int c7:1;
    } ch;

    typedef union chh_u
    {
        char a;
        ch chat;
    } chh;
    int main(void)
    {
        chh uu;
        uu.a = 6;
        printf("\n%d", uu.chat.c0);
        printf("\n%d", uu.chat.c1);
        printf("\n%d", uu.chat.c2);
        printf("\n%d", uu.chat.c3);
        printf("\n%d", uu.chat.c4);
        printf("\n%d", uu.chat.c5);
        printf("\n%d", uu.chat.c6);
        printf("\n%d", uu.chat.c7);
        printf("\n%d\n", uu.a);
        return 0;
    }

正如我所料,输出应为:

  

0 0 0 0 0 1 1 0 6

但实际输出是:

  

0   -1   -1 0 0 0 0 0 6

我无法理解为什么输出上面。 我认为6位顺序是0000 0110,而在内存中,在我的选项中,位顺序也是0000 0110.但输出显示不同。

有人可以解释一下吗?

4 个答案:

答案 0 :(得分:4)

标准的相关部分是6.7.2(5)。

  

每个逗号分隔的集合指定相同的类型,除了对于位字段,它是实现定义的,指定符int是否指定与signed int相同的类型或者与unsigned int相同的类型。

...这解释了为什么你(可以)为设置位获得-1而不是1。另一个是6.7.2.1(10):

  

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

这样,也可以采取任何一种方式。

附录:由于似乎有一些混淆:如果函数期望内部有int,则调用带有位字段的变量参数列表的函数是正常的,原因与它相同说

char c = '\xff';
printf("%d\n", c); // will usually print 255 or -1

并且它可以出于完全相同的原因给出不同的结果,因为就像位字段一样,char可以根据实现进行签名或无符号。我引用了相关部分,这次删除了不相关的部分,因为这些规则被埋没在C99标准中有些繁琐的法律部分中。它们见于6.5.2.2(6):

  

如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将类型为float的参数转换为double。这些被称为默认参数促销。 (...)

和6.5.2.2(7):

  

(...)函数原型声明符中的省略号表示法导致参数类型转换在最后声明的参数之后停止。 默认参数促销是在尾随参数上执行的

整数促销在6.3.1.1中定义;相关位见第2和第3段:

  

2如果可以使用intunsigned int,可以使用以下内容:

     
      
  • (...)
  •   
  • 类型为_Boolintsigned intunsigned int的位字段。
  •   
     

如果int可以表示原始类型的所有值,则该值将转换为int;否则,它将转换为unsigned int。 (...)

     

3整数提升保留包括符号在内的值。 (...)

所以,这发生了:

  1. 您的编译器将int位字段视为已签名
  2. 这意味着宽度为1的位字段的位设置值为-1;从某种意义上讲,它只是标志,而且是设定的。
  3. 传递给printf时,会将此值保留为int
  4. 此int由printf根据%d格式字符串打印,并为您提供" -1"。
  5. 利润?

答案 1 :(得分:1)

尝试在您的位域unsigned中创建值。这可以避免您遇到的意外行为。

typedef struct ch_t {
    unsigned int c0:1;
    unsigned int c1:1;
    unsigned int c2:1;
    unsigned int c3:1;
    unsigned int c4:1;
    unsigned int c5:1;
    unsigned int c6:1;
    unsigned int c7:1;
} ch;

答案 2 :(得分:0)

在这里,您要创建一个signed int [通常int默认为signed int]类型bitfield变量并尝试使用%d打印该值格式说明符。遗憾的是,这不会为单比特位域提供适当的值。

正如@wintermute在他的评论中澄清的那样,在printf()中直接使用位置变量时,它们被视为signed integer。 OTOH,根据规则,单个位signed integer可以具有0-1的值,因此,在这种情况下,表示将是-1

另外,在您的情况下,我认为您理解c0LSBc7将是MSB,对吗?

您需要在打印前取出所需的位。尝试类似下面的内容。

#include <stdio.h>
typedef struct ch_t
{
        int c0:1;
        int c1:1;
        int c2:1;
        int c3:1;
        int c4:1;
        int c5:1;
        int c6:1;
        int c7:1;
} ch;

typedef union chh_u
{
        char a;
        ch chat;
} chh;
int main(void)
{
        chh uu;
        uu.a = 6;
        printf("\n%d", (uu.chat.c0) && 0x01);
        printf("\n%d", (uu.chat.c1) && 0x01);
        printf("\n%d", (uu.chat.c2) && 0x01);
        printf("\n%d", (uu.chat.c3) && 0x01);
        printf("\n%d", (uu.chat.c4) && 0x01);
        printf("\n%d", (uu.chat.c5) && 0x01);
        printf("\n%d", (uu.chat.c6) && 0x01);
        printf("\n%d", (uu.chat.c7) && 0x01);
        printf("\n%d\n", uu.a);
        return 0;
}

否则,在结构定义中使用unsigned int数据类型。

答案 3 :(得分:0)

struct ch_t中,位域c0 .. c7占据低8位,c0为最低位(2 ^ 0)和c7最高(2 ^ 7)。从最高到最低排列,就像写二进制数一样,你有c7 c6 c5 c4 c3 c2 c1 c0。数字6(以8位二进制数字表示)为00000110,因此表示c2c1包含1位,其余位包含0位。这与您获得的结果相匹配,这些字段显示-1

他们显示为-1而不是1的原因是因为您的位域使用了int而不是unsigned int。这有效地为您提供了1位的有符号整数类型,对于2的补码,它只能保持0或-1。