c - 打包的相同结构是否保证具有相同的内存布局?

标签 c struct casting language-lawyer packing

假设我有两个结构: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)。

最佳答案

它们很可能具有相同的布局;这将成为编译器 ABI 的一部分。

相关的架构和/或操作系统可能有一个标准的 ABI,它可能包含也可能不包含 packed 的规范。但是编译器将有自己的 ABI 以可预测的方式布置它们,尽管算法可能不会在编译器源代码以外的任何地方精确地写下来。

但是,这并不意味着您的代码是安全的。严格的别名规则适用于指向不同类型的指针,无论它们是否具有相同的布局。

这是一个可以用gcc -O2编译的例子:

#include <stdio.h>

__attribute__((packed))
struct object {
    int field;
    void *pointer;
};

__attribute__((packed))
struct widget {
    int field;
    void *pointer;
};

struct widget *some_widget;

__attribute__((noipa)) // prevent inlining which hides the bug
void consume(struct object *obj) 
{
    some_widget->field = 42;
    int val = obj->field;
    printf("%i\n", val);
}

int main(void) {
    struct widget wgt = {3, NULL};
    some_widget = &wgt;
    consume((struct object *)&wgt);
}

Try on godbolt

您可能希望此代码打印 42,因为 some_widgetobj 都指向 wgt 并且因此 val = obj->field 应该读取由 some_widget->field = 42 编写的相同的 int。但实际上它打印了 3。允许编译器假定 objsome_widget 没有别名,因为它们具有不同的类型;所以写和读被认为是独立的,可以重新排序。

在标准层面上,您正在通过左值*some_widget 访问对象wgt,其有效类型为struct widget其类型为 struct object。这些类型不兼容,因为它们具有不同的标签(widget vs object),因此行为未定义。

关于c - 打包的相同结构是否保证具有相同的内存布局?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73140420/

相关文章:

c - 如何将 rgb 图像存储在数组中并在 C 中通过 x,y 对其进行索引

c++ - 将数据加载到结构 vector 中

强制转换为指向函数返回数组的指针 - 允许吗?

c++ - 直接初始化 unsigned short 的标准行为

c++ - C++-用于原始指针的reinterpret_cast的安全性

c - 释放 C 堆栈并移除悬挂指针

python - 在python中使用全局变量

c - UART通信与C中的十六进制代码

c - 具有结构和指针的算法

c++ - 为什么 AVFormatContext 指针在非全局结构对象中初始化,而在全局对象中为 NULL?