uint12 结构中的字节顺序

问题描述 投票:0回答:2

假设我有数据,如下所示:

union
{
  struct
  {
    char flags : 4;
    uint16_t   : 12;
  }
  char data[2];
}

我了解如何使该代码在平台上运行而不管字节顺序如何。我要求确保我对如何将其存储在不同字节序上的理解是正确的。

据我了解: 如果我将 uint16 存储在 12 位 uint 中,则两个字节序都会丢弃 4 个最高位。 Big-endian 将剩余的 4 个高位存储在与标志相同的字节中,其余的存储在单独的字节中。 Little-endian 会将 4 个最低位存储在与标志相同的字节中,其余的存储在单独的字节中。

这样对吗?

c++ struct endianness unions
2个回答
1
投票

这取决于编译器和目标平台的 ABI。参见,例如GCC 位域的规则:单元内位域的分配顺序由 ABI 确定。此外,每个字段都应该声明为

int
unsigned int
,而不是
uint16_t

如果要控制数据格式,可以使用移位和掩码将数据组装成

uint16_t
。如果您的目标是以明确定义的格式写出数据,则可以按照所需的字节顺序写入
uint16_t
字节,或者将数据组装成 2 个字节并按所需的顺序写入。

除非您找到语言规范文档承诺您想要的内容,或者您的编译器文档做出明确承诺,并且您对大端和小端 CPU 使用相同的编译器,否则不要依赖 C/C++ 编译器来执行类似的操作同样的方式。


1
投票

Little-endian 会将 4 个最低位存储在与标志相同的字节中

🤔 我熟悉的所有编译器都只组合相邻的位域如果它们的基本存储单元是相同的(尽管是那些可爱的实现定义的细节之一)。因此,在您的示例中,将

char
uint16_t
混合会破坏它们的组合,这意味着该结构将使用 3 个字节(对于任一字节顺序)。对两个字段使用相同的基类型可以让你想要你想要的(但我会
static_assert(sizeof(...) == 2)
以防万一):

union
{
  struct
  {
    uint16_t flags : 4;
    uint16_t value : 12;
  }
  uint8_t data[2];
}

对于小尾数,位域总是按照存储单元的内存递增顺序添加。

对于大端机器,我遇到过两种可能的位域顺序:

  1. 按降序从整个字的高位到低位(msb 到 lsb)分配位域。或者用内存术语来说,您可以认为它从字节 0 开始,打包每个byte(从 msb 到 lsb)的后面,直到填满,然后前进到下一个字节。您可以在具有大端机器的 Linux 上的 gcc 中找到此顺序。
  2. 像 LE 一样按升序分配位域,但交换字内的整个字节。在内存方面,这就像从最后一个字节开始,打包每个字节的前面(从 lsb 到 msb),然后向后移动到前一个字节。您可以在C51 8051 微控制器编译器中找到它。 *我不确定未完全填充时的行为。

每个位的布局是:

绝对比特流 字节 以字节为单位 LE订单 是订单#1 是订单#2
0 0 0 标志0 值8 值4
1 0 1 标志1 值9 值5
2 0 2 标志2 值A 值6
3 0 3 标志3 值B 值7
4 0 4 值0 标志0 值8
5 0 5 值1 标志1 值9
6 0 6 值2 标志2 值A
7 0 7 值3 标志3 值B
8 1 0 值4 值0 标志0
9 1 1 值5 值1 标志1
10 1 2 值6 值2 标志2
11 1 3 值7 值3 标志3
12 1 4 值8 值4 值0
13 1 5 值9 值5 值1
14 1 6 值A 值6 值2
15 1 7 值B 值7 值3
© www.soinside.com 2019 - 2024. All rights reserved.