C编译器如何处理位域?

3
以下的位域示例代码取自这里,声称具有更好的存储效率。但我想知道编译器如何处理位域?
我猜测C编译器不得不生成额外的指令进行位操作。因此虽然数据大小减小了,但代码大小增加了。
有没有熟悉C编译器的人能够解答一下?
#include <stdio.h>

// A space optimized representation of date
struct date
{
   // d has value between 1 and 31, so 5 bits
   // are sufficient
   unsigned int d: 5;

   // m has value between 1 and 12, so 4 bits
   // are sufficient
   unsigned int m: 4;

   unsigned int y;
};

int main()
{
   printf("Size of date is %d bytes\n", sizeof(struct date));
   struct date dt = {31, 12, 2014};
   printf("Date is %d/%d/%d", dt.d, dt.m, dt.y);
   return 0;
} 

5
你可以查看生成的汇编代码来找出答案。 - Oliver Charlesworth
1
这取决于您的平台(底层硬件架构+指定编译器),但通常情况下 - 是的,编译器很可能需要添加位运算(这通常会导致更大的代码段和/或更慢的运行时执行)。 - barak manos
这个 unsigned int m: 4; 在语法上是否正确? - nbro
1
@Nuncameesquecideti:在你喜欢的搜索引擎上搜索“C位域”... - barak manos
1
@Nuncameesquecideti:如果我看起来很粗鲁,那是无意的;只是让我发笑的是有人质疑整个问题的特性参数是否真的是有效的语法。 - Matteo Italia
显示剩余10条评论
2个回答

6
尽管数据大小减少了,但代码大小增加了。
总体而言,这是正确的:它是更紧凑的存储与更快的访问之间的权衡。
例如,这就是我的编译器为您的位域示例中的 printf 语句生成的代码:
    movq    _dt@GOTPCREL(%rip), %rax
    movzwl  (%rax), %edx
    movl    %edx, %esi
    andl    $31, %esi     ; -- extract the 5 bits representing day
    shrl    $5, %edx      ; -+ extract the four bits for the month
    andl    $15, %edx     ; /
    movl    4(%rax), %ecx ; -- year doesn't require any bit manipulation
    leaq    L_.str.1(%rip), %rdi
    xorl    %eax, %eax
    callq   _printf

为了比较,在 date 是一个简单的 struct 的情况下使用相同的代码:

    movq    _dt@GOTPCREL(%rip), %rax
    movl    (%rax), %esi  ; -- day
    movl    4(%rax), %edx ; -- month
    movl    8(%rax), %ecx ; -- year
    leaq    L_.str.1(%rip), %rdi
    xorl    %eax, %eax
    callq   _printf

当然,所有这些都是编译器和平台特定的。


2
据我记得,位域的文档说这只是编译器的建议。实现可以自由选择真正的位或一些不太有效(空间方面)的实现方式。
实际上,位域只是用于处理位的方便语法。
但事实证明,嵌入式编译器倾向于使用真正的位域,因为在嵌入式编程中,处理位是非常常见的任务。当然,如果想要使用此功能,则必须在编译器中进行记录。
关于汇编程序的复杂性,使用真正的位需要汇编程序更多的工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接