任何使用位域的可移植代码似乎都能区分小端和大端平台.有关此类代码的示例,请参见declaration of struct iphdr in linux kernel.我不明白为什么比特端性是个问题.

据我所知,位域是纯粹的编译器构造,用于促进位级操作.

For instance, consider the following bitfield:

struct ParsedInt {
    unsigned int f1:1;
    unsigned int f2:3;
    unsigned int f3:4;
};
uint8_t i;
struct ParsedInt *d = &i;
Here, writing d->f2 is simply a compact and readable way of saying (i>>1) & (1<<4 - 1).

然而,bit操作是定义良好的,无论体系 struct 如何,都能正常工作.那么,为什么比特域不可移植呢?

推荐答案

根据C标准,编译器可以任意存储位字段.你可以对比特的分配位置做出任何假设.以下是C标准中没有规定的一些与字段相关的内容:

未指明的行为

  • 分配用于保存位字段的可寻址存储单元的对齐方式(6.7.2.1).

实现定义的行为

  • 位字段是否可以跨越存储单元边界(6.7.2.1).
  • 单元内位字段的分配顺序(6.7.2.1).

当然,Big/little-endian也是实现定义的.这意味着可以通过以下方式分配 struct (假设16位整数):

PADDING : 8
f1 : 1
f2 : 3
f3 : 4

or

PADDING : 8
f3 : 4
f2 : 3
f1 : 1

or

f1 : 1
f2 : 3
f3 : 4
PADDING : 8

or

f3 : 4
f2 : 3
f1 : 1
PADDING : 8

哪一个适用?猜猜看,或者深入阅读编译器的后端文档.再加上32位整数的复杂性,以大端或小端为单位.然后添加一个事实,即编译器可以在位字段的任何位置添加任意数量的填充bytes,因为它被视为一个 struct (它不能在 struct 的最开始添加填充,但可以在其他任何地方添加填充).

然后我甚至没有提到,如果使用普通的"int"作为位字段类型=实现定义的行为,或者如果使用(无符号)int=实现定义的行为以外的任何其他类型,会发生什么.

所以为了回答这个问题,没有可移植的位域代码,因为C标准对应该如何实现位域非常含糊.位字段唯一可以信任的是布尔值的块,程序员不关心位在内存中的位置.

唯一可移植的解决方案是使用按位运算符而不是位字段.生成的机器码将完全相同,但是是确定性的.位运算符在任何系统的任何C编译器上都是100%可移植的.

C++相关问答推荐

问关于C中的指针和数组

Pure Win32 C(++)-除了替换控件的窗口程序之外,还有其他方法可以在输入时禁用按钮吗?

为什么海湾合作委员会在共享对象中的. init_data的虚拟内存地址之前留出一个空白

C/C++中的状态库

是否有任何情况(特定类型/值),类型双关在所有符合标准的C实现中产生相同的行为?

什么C代码将确定打开的套接字正在使用的网络适配器?

如果包含路径不存在,我的编译器可以被配置为出错吗?(GCC、MSVC)

为什么I2C会发送错误的数据?

在一个小型玩具项目中实现终端历史记录功能

整型文字后缀在左移中的用途

使用TCL C API导航到列表中的元素

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

为什么我在C代码中得到一个不完整的类型?

合并对 struct 数组进行排序

我正在使用c学习数据 struct ,在学习堆栈时,我试图将中缀转换为后缀,并编写了这段代码.代码未给出输出

C中的空指针是什么(_N)?

为什么一个在线编译器拒绝这个VLA代码,而本地的Apple clang却不拒绝;t?

访问未对齐联合的成员是否为未定义行为,即使被访问的成员已充分对齐?

在NASM中链接Linux共享库时出错-';将R_ X86_64_;foo';

变量的指针右对齐,函数的指针左对齐