【问题标题】:Are packed identical structs guaranteed to have the same memory layout?打包的相同结构是否保证具有相同的内存布局?
【发布时间】:2022-07-27 23:27:47
【问题描述】:

假设我有两个结构:objectwidget

struct object {
    int field;
    void *pointer;
};
struct widget {
    int field;
    void *pointer;
};

还有一个功能:

void consume(struct object *obj)
{
    printf("(%i, %p)\n", obj->field, obj->pointer);
}

我知道,如果我尝试去做:

struct widget wgt = {3, NULL};
consume(&wgt);

我会违反strict aliasing rule,因此会有未定义的行为。

据我了解,未定义的行为是由于编译器可能以不同方式对齐结构字段:也就是说,填充字段以与地址边界对齐(但永远不会更改字段顺序,因为顺序保证是受到标准的尊重)。

但是如果这两个结构是packed 呢?它们会有相同的内存布局吗?或者,换句话说,上面的consume() 是否仍然有未定义的行为(尽管有持续的编译器警告)?

注意:我使用struct __attribute__((__packed__)) object { ... }; 进行打包(GCC)。

【问题讨论】:

  • packed 根本不是 C 标准的一部分,因此您需要查看 GCC 文档(如果有)。
  • " .. 未定义的行为是由于编译器可能以不同方式对齐结构字段这一事实造成的”--> 我会说 UB 来自对一种数据类型对象的更改假设其他数据类型不变。 - 不是布局问题。 Zakk 代码试图避免什么问题?
  • 鉴于结构声明和单独编译的工作方式,相同的结构必须相同,但这与语言保证不同。 (您要在此处添加语言-律师标签吗?)
  • @chux-ReinstateMonica 如果我没听错,您的意思是添加/删除/修改某些object 的字段吗?
  • TL;DR:即使没有打包也可以,因为结构是相同的。它可能在技术上违反了标准中的某些条款,但它是“安全”的。但是,我从名称 objectwidget 推断您正在尝试做一个“通用”?这是我的一个可能有帮助的答案:Writing a 'generic' struct-print method in C

标签: c struct casting packing


【解决方案1】:

“据我了解,未定义的行为是由于编译器可能以不同方式对齐结构字段的事实造成的……

不,它没有(完全)。即使两个结构具有相同的成员定义,它们也是不同的类型。考虑两种类型:

struct ComplexNumber  { double real, imag; };
struct GeometricPoint { double x, y;       };

可能会传递给一些例程:

double foo(ComplexNumber *c, GeometricPoint *p)
…

在函数内部,代码可能会分配一些值*p 并使用*c 的值,反之亦然。因为它们是不同且不兼容的类型,所以允许编译器假定它们不是同一内存的别名。这意味着,在优化时,它可以假设为*p 分配一个值不会改变*c 的值,编译器可能已经在以前使用的寄存器中保存了该值。因此,如果分配给*p改变*c,则不需要重新加载寄存器。

因此,别名规则授予编译器对这种行为和类似行为的许可,这意味着,如果您违反规则,则即使结构具有相同的布局,也不会定义行为。

注意:我使用了 struct attribute((packed)) object { ... };用于包装 (GCC)。

打包结构是 GCC 扩展。由于其扩展规范,您可以预期相同定义的打包结构将具有相同的内存布局。但是,C 标准的别名规则仍然适用。 GCC 有一个开关可以关闭别名规则的要求,-fno-strict-aliasing

如果您知道两个对象具有相同的布局并希望在不违反别名规则的情况下将一个对象用作另一个对象,您可以这样做:

  • 将一个字节复制到另一个字节中,与memcpy(p, c, sizeof *p); 一样。
  • 定义一个包含这两种类型的联合,用一种类型对其进行初始化,然后访问另一种类型的成员。 (这是由 C 标准定义的,但不是由 C++ 标准定义的。)

【讨论】:

    猜你喜欢
    • 2013-11-17
    • 2019-02-24
    • 2021-10-21
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多