使用联合标准化字段(使其可移植)

时间:2017-05-04 11:49:20

标签: c embedded

显示的页面来自 “编程嵌入式系统” 迈克尔巴尔,安东尼马萨, 由O'Reilly出版, ISBN:0-596-00983-6,第204页。

enter image description here

我要求你提供更多细节和解释,如:

  1. 这是否意味着位字段可以在所有编译器中移植?

  2. 对于(不同的)体系结构,这适用于大小超过一个字节的位字段或(考虑到字节序差异,我不认为使用此方法将克服此问题)?

    < / LI>
  3. 对于(相同)架构,这适用于大小超过一个字节的位字段吗?

  4. 如果它们在所有编译器中标准化,如本书所述,我们是否可以指定它们如何对齐?

  5. Q.1.2如果位字段只是一个字节,那么字节序问题不会影响它,对吧?那么位字段是否可以在不同体系结构和字节序的所有编译器上移植?

4 个答案:

答案 0 :(得分:4)

  

这是否意味着位字段将在所有编译器中可移植

不,引用的关于工会以某种方式使位域可移植的文字很奇怪。 union不会增加任何进一步的可移植性保证。比特字段的许多方面使它们完全不可移植,因为它们在标准中很难指定。一些例子here

例如,标准不涵盖对位字段使用uint8_tchar类型。尽管它出现了这样一个非标准的例子,但这本书没有提到这一点。

  

对于(不同的)体系结构,这适用于大小超过一个字节的位字段

     

对于(相同)架构,这适用于大小超过一个字节的位字段

不,根本没有保证。

  

如果它们在所有编译器中标准化,如本书所述,我们可以指定它们将如何对齐吗?

他们不是,这本书有误导性。我的建议是停止阅读&#34;位域不可移动&#34;然后忘记你曾经听说过比特场。无论如何,这是一个100%多余的功能。相反,使用按位运算符。

答案 1 :(得分:1)

这是一个非常令人不安的文字,我想我会折腾整本书。

请阅读规范,你没有必要支付它有可用的旧版本和草稿版本(当然不是最终版本)可用,但几十年来它们更常见的不同。< / p>

如果我能为Lundin的回答添加更多的赞成,我会不值得用电子邮件地址创建新用户...

我已经/可能会引发争论,但是......规范确实说如果你在结构或联合中的一行中定义了一些(非零大小的)位域,它们将被打包,并且有一个特殊的零大小的,用于打破它,这样你就可以声明一堆位域并将它们分组而不必制作其他结构。

也许它说他们会对齐,但我永远不会认为那样。我知道一个事实,相同的编译器将对端子进行不同的处理并将它们打包在相反的两端(顶部位向下或底部位向上)。但是没有理由假设任何编译器都遵循除了打包之外的任何约定,并且我会假设它可能也需要解释,一旦你弄清楚它们从何处开始,它们就按照定义的顺序打包。所以我不认为6位的声明是以任何一种方式对齐的,假设一个字节是单位的大小,在一个字节内最多可以有六个不同的对齐,如果单位的大小是32或64位那么我不是打算计算组合,它不止一个,这就是最重要的。我知道gcc的一个事实是,当32到64位的x86转换发生时,这会导致代码对这些位的位置做出假设的问题。

我个人甚至不会假设这些位在将它们打包在一起时处于声明的顺序...流行的编译器倾向于这样做,但是规范并没有说明它们被打包......这是什么意思。如果我有一个1然后一个8然后一个1然后一个8然后一个6位我希望编译器在一个字节边界上对8位的那些进行操作然后将这两个位移到6附近,如果我曾经使用过一个位域的话我不... ...

这里的主要争论是,对我来说,规范非常明确,如果订单和大小相同,联合中多个声明中的初始项目只使用相同的内存,它们是兼容的类型。一位无符号它与32位无符号int不同,因此它们不兼容IMO类型。规范进一步说明,对于位域,类型必须是相同的类型和大小,因此对于在并集中共享相同内存的位域,您需要两个具有相同初始位域项的结构才能使用相同的类型和尺寸,并且只有那些项目按照规范分享内存,其余部分的情况与规格不同。因此,从我对规范的阅读中得出的例子并没有说明8位字符(使用一个非规范的声明)和8位声明的位字段绝不会相互排列并共享记忆。仅仅因为编译器选择在某个版本中执行此操作并不意味着您可以认为,特别是并集不会使该代码可移植或更易于移植,实际上可能更糟,因为现在您不仅在编译器之间存在位域问题或者版本,但现在您在编译器或版本之间存在工会问题。

作为一般规则,永远不要在编译域中使用结构(有或没有位域)(这包括联合),所以永远不要将文件读入结构,你已越过编译域,从不在硬件寄存器中指向结构,你已经越过了一个编译域,从不将结构指向内存,不要将结构指向包含以太网数据包的char数组,并使用struct和/或bitfields来分开ip头。是的,这些规则被广泛使用和滥用,并且是定时炸弹。时间炸弹很少出现的主要原因是代码一直使用相同的编译器,或者一些目前具有相同实现的不同流行编译器。但结构指向一般都会失败很常见,比特场失败只是其中的一个副作用,也许是因为你书中的文字很糟糕,工会开始出现很多让时间炸弹现在变成核武器而不是传统炸弹。 / p>

因此,如果您想使用结构或联合或位域,并且代码实际上无需维护即可运行。然后保持在相同的编译域(一个程序使用相同的编译器和设置同时编译),传递结构定义为跨函数的结构,不指向内存或其他数组等,因为联合从不访问单独定义的项目,如果一个变量只使用该变量,直到完全完成它,假设它现在是垃圾,如果你在该联合中使用结构或其他变量。对于位域,每个变量都是一个独立的项目,独立于它旁边的其他变量,你只是试图通过使用它们来节省内存,但实际上你首先使用它们浪费了大量的代码开销,性能和代码空间。保持这一点,你的代码更有可能在没有维护的情况下工作,而不是编译器。现在,如果您希望将其用作作业安全性并且您的代码无法构建或运行编译器的每个次要或主要版本,那么请执行上述操作,在编译域中指向结构,在硬件寄存器中指向位域等。其他比你的老板注意到你编写了一些可怕的代码,当其他一些员工不这样做时,你将不得不在该产品的生命周期内定期维护代码。

所有编译器都使用你的位域生成掩码和移位,如果你编写这些掩码并自行移位,MASSIVELY更具可移植性,你可能仍会遇到端点问题(实际上有时可以在便携式无字节代码中轻松解决) )但你不会完全指出使用遮蔽和移动的错误的东西。它只是工作,它不会产生更多的代码,不会产生更慢的代码,如果你真的需要为所有东西制作宏,使用宏来隔离一个&#34;单元内的字段&#34;比使用位域更便携。

忘记你曾经读过有关位域或听说过它们,永远不要再使用它们了。对他们的需求几十年前就消失了。工会有点属于同一类别,它们确实可以节省内存,但你必须小心谨慎地分享内存。

如果他们不理解这个简单的主题那些作者不理解的其他内容,我也会抛弃那本书。与大多数人一样,这可能会使流行的编译器解释与现实混淆。

答案 2 :(得分:0)

比特字段和字节序的概念有点“混淆” - 假设您有一个32位的MCU - 这意味着设备的内部存储器必须乘以32位的大小。 现在你可能知道,每个MCU存储内存的方式是LSB或MSB,分别是Big Endian和Little Endian, 请看这里插图:Endians figure 可以看出,相同的数据:0x12345678(32位值)在内部以不同方式存储。 当您使用32位指针(平凡)情况读取和写入内存时 - MCU将为您转换它(它们在字节序之间没有任何区别) - 当您处理逐个字节时出现问题操作或从其他MCU /存储器外设导出(或导入)时也采用8位/ 1字节操作。

比特字段将与字节,字和长字类型(如图所示)对齐,因此在移植到其他目标时可能会错过解释。

因此,回答你的问题:

  1. 如果只有一个字节被分成比特,它将被很好地移植。 如果你定义一个多字节的联合,它会让你遇到麻烦。

  2. 在回答此问题时回答

  3. 见答案编号。 1
  4. 请参阅我附图中的插图
  5. 一般而言

答案 3 :(得分:-2)

1,2:不完全:它总是取决于平台(endianess)和你正在使用的类型。

3:是的,他们将永远落在记忆的同一个地方。

4:你的意思是哪种对齐 - 内存对齐还是字段对齐?