【问题标题】:How to properly access packed struct members如何正确访问打包的结构成员
【发布时间】:2022-02-04 12:33:07
【问题描述】:

访问打包结构成员的正确方法是什么?

struct __attribute__ ((packed)) MyData {
    char ch;
    int i;
}
void g(int *x); // do something with x

void foo(MyData m) {
    g(&m.i);
}

void bar(MyData m) {
    int x = m.i;
    g(&x);
}

我的 IDE 为 foo 提供了警告/建议,我可能正在访问未对齐的 int 指针,这里确实是这种情况。我的问题是

  • 在 foo 和 bar 之间,一种方法比另一种更好吗?
  • 访问未对齐的指针数据是否不正确,但可以使用它来初始化正确对齐的类型? (如酒吧)。
  • 我们是否应该将打包结构的各个成员复制到正确对齐的数据结构中,然后使用它?这意味着几乎每个打包数据结构都有一个非打包数据结构,而打包结构仍然局限于序列化层。

【问题讨论】:

  • 编译器可能会使用各种花哨的技巧来实现int x = m.i; 行。也许它只访问i 成员作为char 的数组,并从中构建int
  • 是的,编译器当然可以在这里使用 memcpy,这是正确的做法。但我的问题不同。
  • 我真的不明白你的最后一个要点。为什么要创建一个全新的(非打包的)结构,只是为了访问打包版本的一个成员?
  • 使用一个成员只是一个例子。如果一个结构有 10 个成员,那么结构的任何成员都可能出现访问不对齐的问题。

标签: c++ serialization alignment packed


【解决方案1】:

目前,调用g 可能会假定x 是对齐的。这在 x86 架构上可能没问题,但 foo 在 ARM 上可能会崩溃。

bar 中调用它并不比在g 中使用int x 好多少。但是,这是正确的,因为编译器知道m.i 未对齐,因此可以生成代码来复制未对齐的int。这确实意味着指针不能修改原始对象(除非您重新分配它)。

你也可以使用未对齐整数的类型:

typedef int __attribute__((aligned(1))) packed_int;
void g(packed_int * x); // do something with x

这可以直接调用为g(&m.i)。请注意,它无法执行对齐的访问,从而导致某些平台上的速度变慢。

【讨论】:

    【解决方案2】:

    访问未对齐的指针数据是否不正确,但可以使用它来初始化正确对齐的类型? (如酒吧)。

    就 C++ 语言而言,没有打包类之类的东西,也没有不正确对齐的对象之类的东西。因此,不正确对齐的指针必然是无效的。

    为打包类提供语言扩展的编译器是否也扩展了语言以允许通过未对齐的指针进行访问,这取决于编译器供应商记录。该警告暗示可能不支持后一种扩展。

    在 foo 和 bar 之间,一种方法比另一种更好吗?

    bar,根据警告。

    我们是否应该将打包结构的各个成员复制到正确对齐的数据结构中,然后使用它?这意味着几乎每个打包数据结构都有一个非打包数据结构,而打包结构仍然局限于序列化层。

    这可能是将非标准打包类限制在序列化层中的便捷解决方案。

    请注意,这不是打包结构的唯一问题。另一个问题是由于不同的字节顺序和类型大小,序列化数据在系统之间的可移植性。

    序列化数据的一种可移植方式是根本不使用打包结构,而是使用显式偏移量单独移动字节。

    【讨论】:

    • 那么用两种数据结构(一种打包和一种非打包)来表示同一个实体很常见吗?我知道我们可以通过仅使用一种数据结构并序列化和反序列化单个成员来实现序列化,但与我们可以使用 POD 打包结构进行的 memcpy 调用相比,这将是低效的。
    • @DDG 访问未对齐的成员也是低效的;您刚刚将低效率转移到其他地方。如果每个对象多次访问成员,情况可能会更糟。此外,大多数序列化是为了文件写入或网络访问,与少数 CPU 指令相比,开销要大得多。
    猜你喜欢
    • 2020-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 2022-01-10
    • 1970-01-01
    相关资源
    最近更新 更多