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 数据隐藏在结构中 : an explanation

c - 基于栈的归并排序C

c - 何时为 C 中的结构和 "create"函数分配内存

c++ - 在 C++ 中传递结构数组 - 被调用函数中不存在结构元素,如何正确传递 vector ?

c - 错误 : KERN_INVALID_ADDRESS at address: 0x0000000000000000 0x00007fff99b359c4 in strstr ()

c - 在结构中如何存储值?

java - 如何在 jnr ffi 中使用结构体和结构体

Java 转换、覆盖和多态性

c - 旋转位图并将浮点格式的结果坐标转换回像素数组的整数 x,y 值

string - List<string> 的 linq 查询语法