gcc 在C中使用位字段时的字段顺序

qc6wkl3g  于 2022-11-12  发布在  其他
关注(0)|答案(6)|浏览(181)

我有一个如下类型的结构体

typedef struct
{
unsigned int a : 8;
unsigned int b : 6;
unsigned int c : 2;
}x, *ptr;

我想做的是,改变c域的值。
我会执行类似以下的操作

x structure = { 0 };
x->c = 1;

当我查看内存Map时,我希望找到 00 01,但实际上我找到了 00 40。看起来像是在排列第二个字节时,它将 c 字段放在最低位,将 b 字段放在最高位。我在GCC和Windows编译器上都看到过这种情况。
现在,我所做的是以下工作,这是正常的。

unsigned char ptr2 = (unsigned char*) ptr
*(ptr2 + 1)  &= 0xFC
*(ptr2 + 1)  |= 0x01

我是不是看错记忆Map了?谢谢你的帮助。

tzxcd3kk

tzxcd3kk1#

C标准允许编译器以任意顺序放置位字段。没有可靠和可移植的方法来确定顺序。
如果你需要知道确切的位位置,最好使用无符号变量和位掩码。
下面是使用位字段的一种可能的替代方法:

#include <stdio.h>

#define MASK_A    0x00FF
#define MASK_B    0x3F00
#define MASK_C    0xC000
#define SHIFT_A   0
#define SHIFT_B   8
#define SHIFT_C   14

unsigned GetField(unsigned all, unsigned mask, unsigned shift)
{
    return (all & mask) >> shift;
}

unsigned SetField(unsigned all, unsigned mask, unsigned shift, unsigned value)
{
    return (all & ~mask) | ((value << shift) & mask);
}

unsigned GetA(unsigned all)
{
    return GetField(all, MASK_A, SHIFT_A);
}

unsigned SetA(unsigned all, unsigned value)
{
    return SetField(all, MASK_A, SHIFT_A, value);
}

/* Similar functions for B and C here */

int main(void)
{
    unsigned myABC = 0;
    myABC = SetA(myABC, 3);
    printf("%u", GetA(myABC)); // Prints 3
}
fwzugrvs

fwzugrvs2#

我知道这是一个老问题,但我想补充我的想法。

  1. C语言中的头文件应该可以跨对象使用,这意味着编译器必须保持一定的一致性。
    1.根据我的经验,我总是看到LSB顺序的位域。这将把c:b:a的位按MSB-〉LSB顺序放置
    按字节顺序读取的16位是“00 40”。从小端字节序转换过来,这是一个16位值0x 4000。
    这是:
c == [15:14] == b'01
b == [13:8] == 0
a == [7:0] == 0
kpbwa7wx

kpbwa7wx3#

存储器总是取决于底层机器结构(字节序)和编译器正在执行的打包/排列结构的策略。

uqxowvwt

uqxowvwt4#

只要您的程序有意不跨平台,您就可以依赖位字段的顺序来确定。
虽然C规范没有规定位字段的顺序,但平台的ABI规定了。在System V AMD 64的情况下,位字段的分配顺序定义为“从右到左”(第3.1.2节)。对于ARM 64,它取决于数据类型的字节序(www.example.com一节8.1.8.2)。由于您的C编译器遵循目标架构的平台ABI,因此,您可以使用在编译时,您可以依赖于给定平台中位字段的固定分配顺序

yquaqz18

yquaqz185#

您将C结构设置为原始位,这是您的风险。
你知道比特是什么,它们的意思是什么,所以你可以填写结构的字段。是的,它比memcpy代码多,但是如果有人添加了一个字段,它不会中断,而且它有助于在通信级别强制比特级别的特殊性。

q43xntqr

q43xntqr6#

当我查看内存Map时,我希望找到00 01,但实际上我找到了00 40。看起来像是在排列第二个字节时,它将c字段放在最低位,将b字段放在最高位。我在GCC和Windows编译器上都看到过这种情况。
先不考虑字节序,如果你只是把所有字段排列成一个递增位的纯顺序流,你会在下面的“位流顺序”列中得到分配,你会看到结构体c中的最后一个字段是流中的最后一个字段。得到包括小端顺序(其实际上与纯比特流顺序相同)和三个大端顺序的四个排列。
Windows编译器只支持小尾序机器(过去,内核必须处理一些大尾序机器,但所有当前的x86/x64/arm 64架构都是小尾序),并且向字段c(下面的位c 0)写入1将在字节1中产生1〈〈6或0 b 01000000(0x 40)。
在大字节序机器上,有更多可能的排序:
1.按照从字的后面到前面的递减顺序来分配位域。2你可以在Linux上的gcc中找到这种顺序。
1.像LE一样按升序分配位域,但在字中交换整个字节。你可以在C51 8051 microcontroller compiler中找到这一点。
| 绝对位|比特流顺序|字节数|字节中的位|LE顺序|BE订单#1| BE订单#2|
| - -|- -|- -|- -|- -|- -|- -|
| 第0页|a0|第0页|第0页|a0| a0| b 0值|
| 一个|a1型|第0页|一个|a1型|a1型|第一层|
| 2个|a2型|第0页|2个|a2型|a2型|二层|
| 三个|a3型|第0页|三个|a3型|a3型|三层|
| 四个|a4格式|第0页|四个|a4格式|a4格式|b4位|
| 五个|a5格式|第0页|五个|a5格式|a5格式|b5层|
| 六个|a6格式|第0页|六个|a6格式|a6格式|零|
| 七个|a7格式|第0页|七个|a7格式|a7格式|c1型|
| 八个|b 0值|一个|第0页|b 0值|零|a0|
| 九个|第一层|一个|一个|第一层|c1型|a1型|
| 10个|二层|一个|2个|二层|b 0值|a2型|
| 十一|三层|一个|三个|三层|第一层|a3型|
| 十二个|b4位|一个|四个|b4位|二层|a4格式|
| 十三个|b5层|一个|五个|b5层|三层|a5格式|
| 十四|零|一个|六个|零|b4位|a6格式|
| 十五个|c1型|一个|七个|c1型|b5层|a7格式|

相关问题