位域如何在C中工作?

时间:2015-03-15 09:31:16

标签: c

我刚刚了解了C中的位字段,我对编译器如何实现此功能感到好奇。就我对C编译器的了解而言,单个位不能单独访问。

1 个答案:

答案 0 :(得分:5)

通过读取周围可寻址的存储器单元(字节或字),屏蔽和移位来实现位字段。

更准确地说,读取位字段是作为读取移位掩码实现的,写入位字段实现为读取掩码移位值以进行写入或写入。

这非常昂贵,但是如果您打算紧凑地存储数据并且愿意为按位操作付出代价,那么位字段在源级别提供更清晰,更轻的语法,用于您可能具有的相同操作用手写的。您失去的是对布局的控制(标准没有指定如何从包含的字中分配位字段,这将因编译器与编译器的不同而不同于按位操作的含义)。

每当您怀疑C编译器对给定构造的作用时,您总是可以读取汇编代码:

struct s { 
  unsigned int a:3;
  unsigned int b:3;
} s;

void f(void)
{
  s.b = 5;
}

int g(void)
{
  return s.a;
}

这是由gcc -O -S编译为:

_f:                                     ## @f
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp4:
    .cfi_def_cfa_register %rbp
    movq    _s@GOTPCREL(%rip), %rax 
    movb    (%rax), %cl ; read
    andb    $-57, %cl ; mask 
    orb $40, %cl ; since the value to write was a constant, 5, the compiler has pre-shifted it by 3, giving 40
    movb    %cl, (%rax) ; write
    popq    %rbp
    retq
    .cfi_endproc

    .globl  _g
    .align  4, 0x90
_g:                                     ## @g
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp7:
    .cfi_def_cfa_offset 16
Ltmp8:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp9:
    .cfi_def_cfa_register %rbp
    movq    _s@GOTPCREL(%rip), %rax
    movzbl  (%rax), %eax
    andl    $7, %eax
    popq    %rbp
    retq
    .cfi_endproc