结构体成员内存布局

10

如果我有这样的一个结构体:

struct S {
    ANY_TYPE a;
    ANY_TYPE b;
    ANY_TYPE c;
} s;

我可以安全地假设以下假设在所有平台上始终为真吗?

((char *)&s.a) < ((char *)&s.c)
((char *)&s.a + sizeof(s.a) + sizeof(s.b)) <= ((char *)&s.c)

也适用于 C++ 吗?


在哪些ANY_TYPE不适用的示例? - Lou Franco
1
你为什么想要这样做呢?结构体的目的就是为了消除这种混乱。 - Beta
6个回答

7

至少在C语言中是这样的。编译器可以在任何结构成员后插入填充,但不能重新排列成员。

它也不能在第一个成员之前插入填充。

从C99开始,6.7.2.1

13/ 在结构对象内,非位域成员和位于位域中的单元按其声明顺序递增地具有地址。指向结构对象的指针进行适当转换后,指向其初始成员(或如果该成员是位域,则指向其中所在的单元),反之亦然。结构对象内可能存在未命名的填充,但不会出现在其开头。

15/ 结构体或联合体末尾可能有未命名的填充。


Re It must also not insert padding before the first member.: 这会导致大多数编译器不符合标准。在第一个成员之前添加vtable是实现多态性的一种非常常见的方法。 - David Hammen
8
首先,vtable是C++的一个实现细节,而不是C语言。其次,没有填充第一个成员之前的要求仅适用于POD(Plain Old Data)类型。带有vtable的类不是POD类型,因此允许在第一个数据成员之前有vtable指针等内容。 - Sjoerd

5
这适用于结构体,但只要引入访问限定符,C++中就会发生变化。编译器允许重新排序由访问限定符分隔的整个块。

这是真的吗?标准规定,成员变量按照它们在类中列出的顺序进行初始化。我认为由于这个原因它们的相对顺序不能被改变。 - Xeo
2
@Xeo 请看这里的例子:http://stackoverflow.com/questions/4883655/access-specifier-in-c/4883764#4883764 不幸的是,这个答案没有标准引用。我会尽力提供一些。关于初始化:初始化与内存排序无关。 - pmr

2
这在C++20中发生了变化(至少在当前草案中):如果您使用[[no_unique_address]]注释abc,并且它们恰好是空结构,则可能会使它们都具有相同的地址。
实际上,情况更为复杂 - 如果任意两个为空并带有该注释,则所有3个结构可以共享一个地址。

2
C++20还没有正式确定,对吗?如果是这样,它随时可能会发生变化(但很可能不会)。 - S.S. Anne

1
在C++中,您可以确信这些假设将成立。在这样的结构体中,编译器不允许更改成员的顺序。

0

是的,默认情况下,C++编译器不允许移动结构体中的元素,这使得两个语句都变得微不足道。


0
  1. 是的(只要sizeof(任何类型)不为0。一些编译器允许它,这是非标准的--请参见sizeof能返回0吗)。您可以使用<=或假设使用标准编译器。

  2. 是的

在C++中也是如此。

指针比较只有在数组和结构/类内部才有意义,而不是通常情况下。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接