C11标准第6.2.5.20节将数组定义为:
数组类型描述了一个连续分配的非空集 具有特定成员对象类型(称为元素类型)的对象。
而struct定义为:
结构类型描述了按顺序分配的非空集 成员对象(在某些情况下是不完整的数组), 每个都有一个可选的指定名称,并且可能不同 类型。
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 总是会为数组和结构产生相同的内存布局。
答案 0 :(得分:2)
区别在于,一个数组,两个元素必须是连续的,没有交织的填充,而在结构中它们是顺序的,但是以实现定义的方式存在填充 。 / p>
现在提出您的问题:
当布局不同时,是否有现实世界的例子?
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];
};
即使标准不能保证,普通编译器也不会在相同类型(简单类型或派生类型)的元素之间添加填充。与第一个问题相同。