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 - 结构指针类型转换错误分配使指针来自整数而不进行转换

c++ - 用于在图中查找约束最短路径的高效数据结构

Swift 类型转换和括号

java - 需要从 List<String[]> 转换为数组的帮助

c - 使用 KEIL 4,lpc2148 中断或 IRQ 不会执行

c - 将 “pointer to pointer” 和 “address of pointer” 传递给函数之间的区别

c - 结构类型数组

java - 返回类型值的通用方法

c - Cstring 中的 Bool Palidrome 函数?

c - 错误 : expected declaration specifiers or '…' before string constant