我熟悉在处理器之间通信时使用位字段的问题——参见C/C++: Force Bit Field Order and Alignment以及为什么字节序会涉及这个问题.

但我的问题是关于使用位字段指定单字节寄存器中位的布局:它安全吗?

作为一个具体的例子,我正在使用一个具有字节宽度控制寄存器的设备,文档如下:

D7 (msb) D6 D5 D4 D3:D2 D1 D0 (lsb)
Vbias Mode Trigger 3-wire Fault Code Fault 50 Hz

定义以下struct个似乎很自然:

typedef struct {
  unsigned int vbias : 1;
  unsigned int mode : 1;
  unsigned int trigger : 1;
  unsigned int wire_3 : 1;
  unsigned int fault_code : 2;
  unsigned int fault : 1;
  unsigned int hz_50 : 1
} control_reg_t;

所以问题是:因为我只在一个给定的处理器中使用这个struct—它不会通过电线传输,等等—有没有理由不使用这种方法?

推荐答案

C标准规定:

一个单元内位字段的分配顺序(高阶到 低阶或低阶到高阶)是实现定义的.

这意味着vbias是否驻留在单元(int)的最高有效位或最低有效位是由实现定义的.因此,除非您确定您已经知道系统上的行为,并且您将只在该特定系统上使用代码,否则位字段将不会被选中.

据我所知,这实际上并不取决于系统的字节顺序.

假设您的控制寄存器指定哪些项目属于哪些位的有效位,但C位域不指定,我建议将您的标志存储为uint8_t(如果可用)或unsigned char,以及access them,如下所示:

#define get_vbias(x) (((x) >> 7) & 1)
#define get_mode(x) (((x) >> 6) & 1)
#define get_trigger(x) (((x) >> 5) & 1)
#define get_wire_3(x) (((x) >> 4) & 1)
#define get_fault_code(x) (((x) >> 2) & 3)
#define get_fault(x) (((x) >> 1) & 1)
#define get_hz_50(x) ((x) & 1)

如果以同样的方式设置它们,则struct的位字段为appropriate for your use case:

#define set(x, n, b) (((x) & ~(1 << (n))) | (!!(b) << (n)))
#define set_vbias(x, b) set((x), 7, (b))
#define set_mode(x, b) set((x), 6, (b))
#define set_trigger(x, b) set((x), 5, (b))
#define set_wire_3(x) set((x), 4, (b))
#define set_fault(x) set((x), 1, (b))
#define set_hz_50(x) set((x), 0, (b))
// Handle this one separately because the rest are just one bit
#define set_fault_code(x, n) (((x) & ~(3 << 2)) | (((n) & 3) << 2))

使用按位操作符手动提取它们将确保哪一项位于LSB、MSB和两者之间的每一位中.

C++相关问答推荐

DPDK-DumpCap不捕获端口上的传入数据包

为什么在C中进行大量的位移位?

当execvp在C函数中失败时杀死子进程

为什么删除CAP_DAC_OVERRIDE后创建文件失败?

GCC不顾-fno-Builtin-SINCOS旗帜向SINCOS发出呼唤

为什么我会收到释放后堆使用错误?

用C++从外部ELF符号读取值

#定义SSL_CONNECTION_NO_CONST

接受任何参数的函数指针是否与接受不同参数的函数兼容

变量的作用域是否在C中的循环未定义行为或实现定义行为的参数中初始化?

预处理器宏扩展(ISO/IEC 9899:1999(E)§;6.10.3.5示例3)

如何读取程序中嵌入的数据S自己的ELF?

try 查找带有指针的数组的最小值和最大值

c程序,让用户输入两类数字,并给出输出用户输入多少个数字

x86-64平台上的int_fast8_t大小与int_fast16_t大小

中位数和众数不正确

如何为avr atmega32微控制器构建C代码,通过光电二极管捕获光强度并通过串行通信传输数据

可以从指针数组中的值初始化指针吗?

如何在 C 中的 Postgres 函数的表中 for 循环

C 预处理器中的标记分隔符列表