【问题标题】:Contiguously allocated vs sequentially allocated连续分配与顺序分配
【发布时间】:2019-02-27 14:52:47
【问题描述】:

C11 标准第 6.2.5.20 节将数组定义为:

数组类型描述了一个连续分配的非空集合 具有特定成员对象类型的对象,称为元素类型。

虽然结构定义为:

结构类型描述了一个顺序分配的非空集合 成员对象(并且,在某些情况下,一个不完整的数组), 每个都有一个可选的指定名称并且可能不同 输入。

6.7.2.1 部分说可以在字段之间插入填充:

结构或联合对象的每个非位域成员都对齐 一种适合其类型的实现定义的方式。

在结构对象中,非位域成员和单元 哪些位域的地址按顺序增加 他们被宣布。指向结构对象的指针,适当地 转换后,指向其初始成员(或者如果该成员是 位域,然后到它所在的单元),反之亦然。 结构对象内可能有未命名的填充,但在其 开始。

但这是否意味着以下对象可能具有不同的内存布局?

struct A {
    char x0;
    short x1;
};

struct B {
    struct A x0;
    struct A x1;
    struct A x2;
};

assert(sizeof(struct B) == sizeof(struct A[3]));

我创建了这个测试脚本来检查 GCC 的内存布局:

import itertools
import subprocess

src = """
#include "assert.h"

struct A {
{fields}
};

struct B {
    struct A x0;
    struct A x1;
    struct A x2;
};

int main(int argc, char** argv) {
    assert(sizeof(struct B) == sizeof(struct A[3]));
    return 0;
}
"""

def main():
    all_types = ["char", "short", "int", "long long"]

    for types in itertools.product(all_types, repeat=3):
        rendered = src.replace("{fields}", "".join([
            "        {} x{};\n".format(t, i)
            for i, t in enumerate(types)]))
        with open("main.c", "w") as f:
            f.write(rendered)
        subprocess.call(["gcc", "main.c"])
        subprocess.call(["./a.out"])

if __name__ == "__main__":
    main()

但 GCC 总是 为数组和结构生成相同的内存布局。

  • 是否存在布局不同时的实际示例?
  • 将此类结构实例转换为数组是否安全?
  • 联合会更安全吗?

【问题讨论】:

  • 禁用填充然后检查。 #pragma pack(push) #pragma pack(1) /* struct definition here */ #pragma pack(pop).
  • @Lundin 试过了,但我得到了相同的结果——一切正常。

标签: c language-lawyer


【解决方案1】:

不同之处在于,一个数组,两个元素必须是连续的,没有交错填充,而在结构中它们是连续的,但填充可以以实现定义的方式存在。

现在回答你的问题:

在布局不同时,是否有任何现实世界的示例?

AFAIK,不适用于通用编译器。此外,大多数都有选项,程序员可以通过这些选项要求在结构中不添加填充。

将这样的结构实例转换为数组是否安全?

不可以,因为结构体没有声明等效数组,单个变量只能别名为大小为 1 的数组。a 也是单个变量,*(&a + 1) 是正式的未定义行为。

联合会更安全吗?

是的,根据that other SO post,它可以通过联合来完成。这是合法的 C:

union B {
    struct {
        struct A x0;
        struct A x1;
        struct A x2;
    };
    struct A x[3];
};

即使标准不保证这一点,普通编译器也不会在相同类型的元素之间添加填充,无论是简单类型还是派生(结构)。与第一个问题的原因相同。

【讨论】:

  • 但是联合方法仍然假设匿名结构没有被填充,对吧?如果归档类型是复合的,例如其他结构?
  • @ivaigult:是的,我假设这里不涉及填充,这不能保证每个标准,但大多数(如果不是全部)编译器都尊重这一点。事实上,他们只是没有理由在相同类型的元素之间添加填充。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-18
  • 2015-10-24
  • 2014-02-08
  • 2011-11-04
  • 2016-01-31
相关资源
最近更新 更多