位域如何在C中工作?

Dek*_*ler 1 c

我刚刚学习了C中的位域,我对编译器如何实现这个功能感到好奇.就我对C编译器的了解而言,单个位不能单独访问.

Pas*_*uoq 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;
}
Run Code Online (Sandbox Code Playgroud)

这是通过以下方式编译的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
Run Code Online (Sandbox Code Playgroud)