我刚刚了解了C中的位字段,我对编译器如何实现此功能感到好奇。就我对C编译器的了解而言,单个位不能单独访问。
答案 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