Little Endian如何用Bitfields和Longwords存储?

时间:2016-04-11 16:01:16

标签: c struct bit-fields endianness

所以,我可以理解,当存储为little-endian时,0x1234的一个字在内存中变为0x3412。我也看到字节0x12作为位域a:4和b:4将存储为0x21。但是,如果我有更复杂的东西怎么办?数据如0x1700581001FFFFFF,具有以下结构排序?我看到数据存储为0x7180051001FFFFFF,这对我来说毫无意义。似乎'a'和'b'被交换了,但它们仍然保留在结构的开头,并且g与其他看似随机的掉期一起保持在最后。为什么?另外,我留下了“LONGWORD”的标记,因为代码中有。我不确定4位可以成为一个长字,但也许这与这种疯狂有关?

LONGWORD a: 4
LONGWORD b: 4
LONGWORD c: 4
LONGWORD d: 12
LONGWORD e: 8
LONGWORD f: 8
LONGWORD g: 24

3 个答案:

答案 0 :(得分:1)

以“实现定义的方式”。 {<3}}的 6.7.2.1结构和联合说明符,第11段:

  

实现可以分配任何可寻址的存储单元   足以容纳一个位域。如果剩余足够的空间,则为比特字段   紧跟在结构中的另一个位域之后应该是   打包到同一单元的相邻位。如果空间不足   仍然存在,是否将不适合的位域放入下一个   单元或重叠相邻单元是实现定义的。命令   单位内的位域分配(高位到低位或   低阶到高阶)是实现定义的。对齐   可寻址存储单元未指定。

回答你的问题但如果我有更复杂的东西怎么办?数据如0x1700581001FFFFFF,具有以下结构排序?

在这种情况下,如果您需要便携且可靠的代码,那么正确的答案是不使用位字段。您未能在问题中提供足够的信息以便任何人提供关于如何将数据放入您描述的位字段的答案这一事实应该告诉您使用位字段时出现的问题。

例如,给定您的位字段

LONGWORD a: 4
LONGWORD b: 4
LONGWORD c: 4
LONGWORD d: 12
LONGWORD e: 8
LONGWORD f: 8
LONGWORD g: 24

如果假定16位int - 类型值用于位字段,那么这样布置数据是完全正确的:

16-bit `int` with `c`,`b`,`a` - in that order
16-bit `int` with `d`
16-bit `int` with `f`,`e` - in that order
16-bit `int` with first 16 bits of `g`
16-bit `int` with last 8 bits of `g` - **after** 8 bits of padding.

这甚至没有进入存储的字节序。

答案 1 :(得分:1)

问题(以及评论中关于如何&#34;按顺序指定数据位的含义&#34;)不可避免地归结为:你想要做什么数据?

如果您正在声明一个数据结构以便某些C代码可以写入它,而其他C代码可以从中读取,那么您很少会关注字节顺序或位域顺序(或填充顺序) ,或对齐,或其中任何一种。)

它变得棘手 - 非常棘手 - 就是当你尝试采用那种数据结构时,你的C编译器将它放在内存中,并将其写入外部世界或从外部读取。当您尝试这样做时,您最终必须永远担心类型大小,字节顺序,填充和对齐以及分配位域的顺序。

事实上,有很多事情需要担心,并且将它们全部打倒是一件令人讨厌的事情,许多人(包括我自己)建议不要试图定义可以这种方式直接读写的数据结构,一点都不。

我的记忆是,big-endinan机器的编译器倾向于以一种方式在位域中布局位,而对于little-endian则是另一种方式。但我永远不会记得哪条路是哪条。 (即使我以为我能记住,你也不应该相信我。)如果由于某种原因你关心,你将不得不做我一直做的事情,那就是写一些小测试程序构造一些二进制数据并以十六进制打印出来,并弄清楚它是如何为你今天使用的机器/编译器组合完成的。 (当然,你还必须决定你的机器/编译器组合可能在下周改变的可能性。)

答案 2 :(得分:0)

重新阅读文档后,我没有看到任何顺序打包位域的任何限制。确实有一个指定的顺序,但它依赖于实现,它以何种方式完成。但它仍然是可以确定的。简而言之,从我所看到的情况来看,它是按照8 ​​IN ORDER的组来打包的。我们的Little Endian编译器(或某些选项可能在某处)的不同之处在于,位的串联将第一个定义的位置于下一个定义的位之后(即,使第一个定义的比下一个定义的更不重要)。例如:

a:3 = 111 (binary)
b:4 = 0000 (binary)
c:9 = 011111111 (binary)

我们的Little Endian编译器(或者,或许还有一些其他选项)将从&#39; a&#39;中取出3位。并通过添加&#39; a&#39;来与b连接。到了&#39; b&#39;我相信,这与Big Endian编译器所做的相反,它会使“一个”在&#39; b&#39;之前。所以我推测这是做到这一点的字节顺序,但是我们通过制作ba而不是ab得到7位0000111。然后它需要从c再创建一个完整的8位。它需要&#39; c&#39;这是一个1,并且再次,先前的位被添加到该新位的末尾。所以我们有10000111.这个字节,0x87,然后存储到内存,它抓取另外8位。在这个例子中,接下来的8位是01111111,因此它在0x87之后存储该字节0x7F。所以在内存中我们现在有0x877F。另一种方法(可能是Big Endian)最终会以0xE0FF结束。现在在内存中的0x877F,如果被解释为Little Endian中的一个字,则值为0x7F87,或者在二进制中,为0111111110000111。这恰好与上面连接的数据结构完全相反的&#39; cba&#39; 。

因此,让我们对前面提供的数据执行相同的反向排序: (0x1700581001FFFFFF意味着要解析如下,但我想这可能不是很明显,因为它是我假设的Big Endian结构)

LONGWORD a: 4 = 0x1
LONGWORD b: 4 = 0x7
LONGWORD c: 4 = 0x0
LONGWORD d: 12 = 0x058
LONGWORD e: 8 = 0x10
LONGWORD f: 8 = 0x01
LONGWORD g: 24 = 0xFFFFFF

使用Little Endian配置,通过以gfedcba的顺序连接,可以将其解释为一个值为0xFFFFFF0110058071的巨型结构。如果我们以Little Endian格式将其存储回内存,我们将获得0x7180051001FFFFFF,这是我所说的确切数据。从理论上讲,Big Endian会按照我认为明显的顺序(0x1700581001FFFFFF)进行解释和存储。

嗯,这对我来说很有意义。希望其他人试图找出同样的东西是有意义的!我仍然不明白为什么他们都会在他们面前说LONGWORD ...