【问题标题】:How are nested structures with different alignments laid out in memory?具有不同对齐方式的嵌套结构如何在内存中布局?
【发布时间】:2014-10-29 22:01:38
【问题描述】:

我是一名 C# 开发人员,正在为用 C++ 编写的服务器编写客户端。服务器通过 TCP/IP 将一些任意数据流式传输到客户端,我们必须在另一端重新组装它。服务器首先向我们发送数据的描述,然后是数据本身。

有问题的结构:

struct Inner_S
{
  double a;
  double b[4][4];
};

#pragma pack(1)
struct Packed_S
{
  uint8_t c;
  Inner_S d;
};

服务器告诉客户端外部结构对齐为 1,内部结构对齐为 8。协议规范说:

流式结构中的字段对齐是根据 Itanium 64 位 C++ 应用程序二进制接口规范完成的(即与典型 64 位平台上的典型 GNU 编译器相同)。

我找到了Itanium 64-bit C++ Application Binary Interface specification。我认为我正在寻找的部分是“虚拟基地以外的成员分配”,但我迷路了。

在 C# 方面,我正在读取数据流并将我自己的类与从结构中提取的值打包在一起。我需要知道在流中的确切位置来查找结构的每个元素。

我目前正在以这种方式处理结构,据我的用户说这是错误的:

(以对齐 1 开始结构)(无需填充)(读取简单值)c(以对齐 8 开始内部结构)(将填充添加到对齐 8)0000000(读取字段)aaaaaaa(开始数组)(读取简单值)bbbbbbbb.....

at least one site.支持该方法

那么,当我解析这些数据时,如何处理Inner_S 中的对齐?

caaaaaaaabbbbbbbb....(我想?) caaaaaaaa0000000bbbbbbbb....(看起来不对)

【问题讨论】:

  • 你知道,一个简单的 C++ 程序打印出sizeof 你的类型和offsetof 你的每个字段将帮助你更快地调试这种事情! :-)
  • 这是个好主意。
  • 通过实验确定这一点的一个问题是它的完成方式是编译器特定的,它不是语言特性。我想如果我在不同的平台上使用 GCC,它会以相同的方式工作,但我不确定?
  • 客户抱怨的特定问题已通过从结构的开头而不是从任何类型的全局引用进行对齐来解决,并在结构的末尾添加填充而不是让它成为容器立即恢复对齐控制。这仍然不是对应该发生的事情的简洁和完整的描述,并且导致内存布局否定了内部结构的点,其中包含 8 个包。

标签: c++ structure memory-alignment


【解决方案1】:

正如@Cameron 所建议的,我用offsetof 尝试了这个,因为这涉及到 POD 类型。

#include <iostream>
#include <cstddef>
#include <cstdint>
using namespace std;

struct Inner_S
{
  double a;
  double b[4][4];
};

#pragma pack(1)
struct Packed_S
{
  uint8_t c;
  Inner_S d;
};

int main() {
    cout << "Size: " << sizeof(Packed_S) << endl;
    cout << "c offset: " << offsetof(Packed_S, c) << endl;
    cout << "d offset: " << offsetof(Packed_S, d) << endl;
    cout << "a offset: " << offsetof(Packed_S, d.a) << endl;
    cout << "b offset: " << offsetof(Packed_S, d.b) << endl;
    return 0;
}

输出

Size: 137
c offset: 0
d offset: 1
a offset: 1
b offset: 9

因此,使用您的符号,结构被打包为caaaaaaaabbbbbbbb....。请注意,如果您取出 #pragma pack(1) 指令,编译器会在 c 之后添加 3 个字节的填充。

See here

【讨论】:

  • 你编译 32 位?我希望在 64 位编译中没有 #pragma pack(1) 的 7 字节填充。
  • @DeniseSkidmore 这给了我与 64 位编译器完全相同的结果。
  • 有趣。我们的环境之间是否存在不同的默认打包编译器设置?
  • @DeniseSkidmore:我对此表示怀疑,pack(1) 告诉编译器不要添加任何填充
  • 我使用 64 位 g++ 编译 -m64 得到 7 个字节的填充。我认为@sjdowling 使用的是 64 位编译器可执行文件,但编译为 32 位(相当于 g++ 的 -m32)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-15
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 2017-03-14
  • 2022-07-27
相关资源
最近更新 更多