结构体的一个字节对齐与架构的对齐要求冲突?

3
我之前在这里发布了一个有关指针转换期间对齐访问的问题,详情请参见此处。总结起来,最好不要使用非对齐访问以实现完全可移植性,因为某些架构可能会抛出异常,或者性能可能会比对齐访问慢得多。
然而,在传输网络数据时,我希望使用一字节对齐,例如,我不想在结构体内添加额外的填充。因此,通常做法是: 有些情况下
#pragma pack (push, 1)
struct tTelegram
{
   u8 cmd;
   u8 index;
   u16 addr1_16;
   u16 addr2_16;
   u8  length_low;
   u8 data[1];
};
#pragma pack (pop)

那么你可能已经知道我的问题:如果我在结构体上强制使用一字节对齐,这是否意味着它不能完全可移植,因为结构体成员没有对齐?如果我既想要无填充又想要可移植性怎么办?


位域不具备可移植性。length_highreservednext打包的顺序绝对取决于实现,不同的编译器有所不同。 - Dietrich Epp
哦,是的!我刚刚从旧代码中复制了它。现在忘掉它吧,我会把它删除以便专注于问题。 - Eric Z
在https://dev59.com/z2sz5IYBdhLWcg3wlY69上有一些很好的信息。 - Tony Delroy
2个回答

3

首先,不对齐的内存访问是指单个数据跨越多个内存字(word)。例如,在32位系统上,地址为0、4、8等处的32位int是对齐的,但在1、2、3、5、6、7、9等处则是不对齐的。

其次,不对齐的数据在C++中并不会"抛出异常",但它可能会在CPU级别上引发中断/陷阱/异常——例如UNIX上的SIGBUS,你通常需要设置一个信号处理程序来响应这种情况,但如果你需要以可移植的方式解析不对齐的数据,你不应该通过捕获信号来实现——你需要手动编写步骤来打包和解包跨越字边界的数据。

在你的tTelegram结构体中,数据并没有"不对齐",但在将数据作为寄存器打包/解包时进行位移和掩码处理仍然可能比使用占用独立字的数据更慢——需要更多的机器码指令。

关于可移植性——所有非玩具编译器都会有一种选项来按照你描述的方式进行打包,但确切的#pragma会有所不同,多字节值中字节的布局仍可能是大端或小端(或某种奇怪的方式),而一些CPU允许一些不对齐的数据访问(例如x86),而其他CPU则不允许(例如Ultrasparc)。


对于不允许访问未对齐数据的CPU,大多数针对该架构的编译器将通过多次访问对齐的内存地址来“欺骗”CPU,然后以某种方式将它们进行OR运算。这样是正确的吗? - Eric Z
@EricZ:考虑一个紧密打包的 struct X { char x; int y; };。假设 X 本身在字边界上对齐(编译器倾向于处理这个问题),并且 int 的大小为一个字,那么 y 数据成员必须是未对齐的。编译器可以看到这一点,并应该生成位移和 AND(读取)/ OR(设置)y 值的操作。编译器不倾向于防范由未对齐指针引起的未对齐数据访问,例如 *(int*)(1);在这种情况下,像 x86 这样的 CPU 会“捏造”它(可能比正常的 int 访问更慢),而像 Ultrasparcs 这样的 CPU 则会陷入异常。 - Tony Delroy

2
当在不同的计算机之间传输数据时,您总是希望格式化您的数据。请注意,数据格式不一定可读,但它可以非常二进制。二进制格式将包括每个数据项的精确位置、类型,对于多字节数据,字节出现的顺序,大小或确定大小的方法等。不使用定义的格式会有问题,可能比较快就会出现。
换句话说,虽然我看到过像您描述的方法被使用,但我认为它们并不正常,并且当涉及到不同实体(公司肯定,可能也包括不同部门和/或小组)之间的定义格式时,它们肯定不正常。在我工作的地方,接收和发送数据时,确切的格式肯定已经定义好了。如果定义的格式可以与struct中的数据布局匹配,那么它肯定也用于解码数据,但它不具备可移植性,旨在具备可移植性的代码不尝试使用这样的设施。相反,它使用某种方式读取/写入相关记录,并适当地解码/编码不同的记录。通常,解码/编码代码是从描述精确数据布局的元格式生成的。

布局已经由某个工业协议很好地定义了,所以我对此无能为力。你的观点是使用单字节对齐不可移植? - Eric Z
确实,字节对齐不具备可移植性。并非所有编译器都支持强制字节对齐的选项。而且,在一个字中字节的顺序也并非总是相同的(大端序 vs. 小端序)。 - Dietmar Kühl
+1 @EricZ 其他方面,它并不具有可移植性。打包和对齐完全取决于平台,即使您的平台“允许”您在其中指定条件编译,根据定义,它也不具备可移植性。唯一保证可移植性的方法是通过协议定义,双方都严格遵循,甚至到单个八位字节。当它在大端、小端、严格对齐环境(SPARC)等上运行,并且仍然正常工作时,您可能会*发现些什么。 - WhozCraig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接