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)

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

4

2 回答 2

3

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

其次,未对齐的数据不会在 C++ 意义上“引发异常”,但可能会在 CPU 级别引发中断/陷阱/异常 - 例如 UNIX 上的 SIGBUS,您通常会设置一个信号处理程序来对此做出反应,但是,如果您需要以可移植的方式解析未对齐的数据,则不会通过捕获信号来执行此操作 - 您需要手动编写步骤来打包和解包跨越字边界的数据。

在您的tTelegram结构中,数据不是“未对齐”的,但是在从寄存器打包/解包数据时移位和屏蔽数据的过程仍然可能比使用占据独立字的数据更慢 - 需要更多机器代码指令。

关于可移植性-所有非玩具编译器都可以选择按照您描述的方式打包,但确切的编译指示会有所不同,多字节值中的字节布局可能仍然是大端或小端(或一些很奇怪的东西),而一些 CPU 允许一些未对齐的数据访问(例如 x86),而另一些则不允许(例如 Ultrasparc)。

于 2012-12-17T00:51:04.247 回答
2

在不同计算机之间传输数据时,您总是希望格式化数据。请注意,数据格式不一定是可读的,但它很可能是二进制的。二进制格式将包括每个数据项的确切位置、它的类型、对于多字节数据、字节出现的顺序、大小或确定大小的方法等。不使用定义的格式会咬人,可能早于之后。

换句话说,尽管我已经看到了您所描述的使用方法,但我认为它们不正常,而且在不同实体之间定义的格式(当然是公司,也可能在不同部门和/或组之间)时它们肯定不正常)。在我工作的地方接收和发送数据,确切的格式肯定是定义的。如果定义的格式可以与 a 中的数据布局匹配,struct它当然也用于解码数据,但众所周知它不可移植,并且旨在可移植的代码不会尝试使用这样的工具。相反,它使用读/写相关记录并适当地解码/编码不同的东西。通常,解码/编码代码是从某种描述确切数据布局的元格式生成的。

于 2012-12-17T00:12:03.580 回答