【问题标题】:Explanation of output of this program (Bitfield in Structure)该程序的输出说明(结构中的位域)
【发布时间】:2013-06-16 05:01:15
【问题描述】:
#include <stdio.h>

int main()
{
    struct bitfield 
    {
         unsigned a:5;
         unsigned c:5;
         unsigned b:6;
    } bit;

    char *ptr;
    struct bitfield bit1={1,3,3};

    ptr=&bit1;
    ptr++;

    printf("%d",*ptr);
    return 0;
}

这个问题的输出是 12。它是怎么来的?谁能解释一下? 我尽力解释它。

【问题讨论】:

  • 我从来没有使用过位域,但由于 'ptr' 的类型是 'char*' 递增它会将它指向的地址更改 1 个字节(即 sizeof(char))
  • @nishantjr- 你能解释一下这对输出有何影响吗?
  • 给我一点时间...我要写几个测试程序。你使用哪个编译器?
  • gcc 4.3.2 ideone.com/zazd1T

标签: c coding-style structure


【解决方案1】:

这个问题的解释很简单:

Binary value of 1 is 00001 (as "a" have 5 bitfield)
Binary value of 3 is 00011 (as "c" have 5 bitfield)
Binary value of 3 is 000011 (as "b" have 6 bitfield)

内存布局可以这样可视化:

前 5 位由 a 占用,值为 00001。然后 5 位由 b 占用,值为 00011,后 6 位由 c 占用,值为 000011。

所以,在起始指针 ptr 位于内存位置 1000 处,现在当您执行 ptr++ 时。由于sizeof(char) 为1,ptr 将移动1 个内存位置。所以ptr 移动到内存位置 1001。

因此*ptr 将为您提供存储在内存位置 1001 的值, 因此答案将是 12

【讨论】:

  • @Shashank_Jain- 这解释得更清楚了!谢谢解释
  • 谢谢。我想用图会很容易!
  • 位域的布局和组织完全依赖于系统(假设系统有一个 ABI——应用程序二进制接口——它涵盖了位域的行为)。在 64 位 Intel 系统 (Mac OS X 10.8.4) 上,struct bitfield 的大小为 4。毫不奇怪,在大端系统(例如 SPARC 或 PPC)上,打印的结果为零。
  • @JonathanLeffler- 是的,这是正确的。 “结果取决于字节顺序”
  • @Shashank_Jain - 为什么我不能奖励这个答案?
【解决方案2】:

如何用 5 位表示 1?它将是 00001,而 3 将是 00011 (请注意,b 有 6 个位域,因此它会有额外的零:000011)。

现在假设bit1 的地址是1000。ptr++ 是什么?它将是 1001(因为 sizeof ptr 是 1)。

*ptr 是什么意思?这意味着位置 1001 的 内容 将是 00001100。

二进制 00001100 是十进制 12

【讨论】:

    【解决方案3】:

    初始化后位域有以下值:

    000011 00011 00001
    ^^^^^^ ^^^^^ ^^^^^
    b = 3  c = 3 a = 1
    

    假设char是8位宽,你可以把这16位分成两个8位的部分:

    00001100 01100011
    ^^^^^^^^ ^^^^^^^^
     ptr + 1   ptr
    

    因此,您将在 ptr + 1 处打印八位字节,即 12。

    但是,我很确定这会调用 未定义的行为, 因为不应该使用位域的地址(更不用说通过甚至是不兼容的类型...)

    【讨论】:

    • 它没有使用位域的地址(那将是&amp;bit1.a,等等,并且是非法的)。它正在使用结构的地址。代码是可疑的,并且实现是定义的,因为几乎所有与位字段有关的东西都是实现定义的。
    • @JonathanLeffler 你确定不是 UB 吗?我知道它不是直接使用位字段的地址,但它仍然忽略偶尔的填充,通过不兼容类型的指针对值进行别名(这打破了 本身 未定义的严格别名规则)等。
    • 它可能是UB而不是IDB,但这不是因为你说的“位域地址”(因为它没有正式采用位域的地址;它需要包含位字段的结构的地址,并且采用结构的地址是完全定义的)。这是丑陋的代码;所有带有位字段的代码都比较难看,然后键入双关语和别名只会使事情复杂化。
    • @JonathanLeffler 没错。位字段是邪恶的(特别是如果被滥用......):(
    【解决方案4】:

    一个程序来演示 a、b 和 c 的存储位置。请注意,由于字节序,它会有点混乱。

    #include<stdio.h>
    #include<string.h>
    
    struct bitfield 
    {
         unsigned a:5;
         unsigned c:5;
         unsigned b:6;
    };
    
    void print_bitfield(unsigned a, unsigned c, unsigned b)
    {
        struct bitfield bf;
        memset(&bf, 0, sizeof(bf));
        bf.a = a;
        bf.b = b;
        bf.c = c;
    
        unsigned char* ptr = (unsigned char*)&bf;
        unsigned i;
    
        printf("%2x %2x %2x: ", a, c, b);
        for (i = 0; i < sizeof(bf); i++)
        {
            printf("%02x ", ptr[i]);
        }
        printf("\n");
    }
    
    int main()
    {
        printf("sizeof bitfield: %u\n",sizeof(struct bitfield));
    
        printf(" a  c  b:  0  1  2  3\n");
        print_bitfield(0,  0, 0);
        print_bitfield(1,  0, 0);
        print_bitfield(31, 0, 0);
        print_bitfield(0,  1, 0);
        print_bitfield(0, 31, 0);
        print_bitfield(0,  0, 1);
        print_bitfield(0,  0, 63);
        print_bitfield(1, 3, 3);
        return 0;
    }
    

    输出:

    sizeof bitfield: 4
     a  c  b:  0  1  2  3
     0  0  0: 00 00 00 00 
     1  0  0: 01 00 00 00 
    1f  0  0: 1f 00 00 00 
     0  1  0: 20 00 00 00 
     0 1f  0: e0 03 00 00 
     0  0  1: 00 04 00 00 
     0  0 3f: 00 fc 00 00 
     1  3  3: 61 0c 00 00 
    

    【讨论】:

      猜你喜欢
      • 2021-05-06
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多