C++ 将基元类型指针强制转换为结构指针-对齐和填充?
20分钟后,当我回答一个问题时,我想到了一个有趣的场景,我不确定其行为: 让我有一个大小为n的整数数组,由intPtr指向C++ 将基元类型指针强制转换为结构指针-对齐和填充?,c++,casting,c++11,C++,Casting,C++11,20分钟后,当我回答一个问题时,我想到了一个有趣的场景,我不确定其行为: 让我有一个大小为n的整数数组,由intPtr指向 int* intPtr; 让我也有一个这样的结构: typedef struct { int val1; int val2; //and less or more integer declarations goes on like this(not any other type) }intStruct; static_assert (sizeof(intStruct)
int* intPtr;
让我也有一个这样的结构:
typedef struct {
int val1;
int val2;
//and less or more integer declarations goes on like this(not any other type)
}intStruct;
static_assert (sizeof(intStruct) ==
sizeof(int) + sizeof(int));
我的问题是,如果我执行castintStruct*structPtr=(intStruct*)intPtr代码>
如果遍历结构中的元素,是否确保正确获取每个元素?在任何体系结构/编译器中是否存在未对齐的可能性(可能是因为填充)?严格来说,这样的指针强制转换是不允许的,并且会导致未定义的行为
但是,强制转换的主要问题是编译器可以在结构中的任何位置添加任意数量的填充字节,第一个元素之前除外。因此,它是否有效取决于特定系统的对齐要求,以及是否启用了结构填充
int
的大小不一定与可寻址数据块的最佳大小相同,即使对于大多数32位系统也是如此。有些32位不关心未对齐,有些允许未对齐,但生成的代码效率较低,有些必须对齐数据。从理论上讲,64位字节可能还希望在int(其中为32位)之后添加填充,以获得64位块,但实际上它们支持32位指令集
如果使用此强制转换编写代码,则应添加如下内容:
typedef struct {
int val1;
int val2;
//and less or more integer declarations goes on like this(not any other type)
}intStruct;
static_assert (sizeof(intStruct) ==
sizeof(int) + sizeof(int));
我在最近一次搜索时没有发现任何保证它是有效的,并且我发现了STD:复杂的C++的明确保证,如果它更普遍,那么它可以被更容易地制定,所以我怀疑我在搜索中错过了什么。(但无证据很难证明无证据,标准有时在其制定过程中模糊不清)。
鉴于元素类型为标准布局,保证其合法性。注:以下所有引用均指标准
8.3.4阵列[dcl.阵列]
1-[…]数组类型的对象包含一组连续分配的非空N
类型T
[…]的子对象
关于具有N
类型T
成员的struct
9.2班级成员[班级成员]
14-具有相同访问控制的(非联合)类的非静态数据成员分配为
以后的成员在类对象中具有更高的地址。[…]实现对齐要求可能会
使两个相邻的成员不能紧随其后分配[…]
20-指向标准布局结构对象的指针(使用重新解释转换进行适当转换)指向其
初始成员[…],反之亦然。[注:
因此,在标准布局结构对象中可能存在未命名的填充,但在其开头不存在,
如有必要,以实现适当对齐。-结束注释]
因此问题是struct
中任何需要填充的对齐是否会导致其成员之间不连续分配。答案是:
1.8 C++对象模型[介绍对象]
4-[…]普通可复制或标准布局类型的对象应占用连续的存储字节
换句话说,一个标准布局struct
a
包含至少两个成员x
,y
的相同(标准布局)类型且不尊重标识&a.y==&a.x+1
的标准布局违反了1.8:4
请注意,对齐定义为(3.11对齐[basic.align])连续地址之间可分配给给定对象的字节数;因此,类型T
的对齐不能大于T
数组中相邻对象之间的距离,以及(因为5.3.3 Sizeof[expr.Sizeof]指定一个由n个元素组成的数组的大小是一个元素大小的n倍)alignof(T)
不能大于Sizeof(T)
。因此,对齐不需要在同一类型结构的相邻元素之间添加任何附加填充,因此9.2:14不支持
关于程序员的观点,我将用26.4复数[Complex.numbers]来解释语言要求std::complex
的实例化就其成员的位置而言应作为标准布局类型,而无需符合标准布局类型的所有要求。那里的行为几乎肯定依赖于编译器、体系结构和ABI。但是,如果在使用gcc时,可以使用\uuuuu attribute\uuuuuu((packed))
强制编译器一个接一个地打包结构成员,而无需任何填充。这样,内存布局应该与平面数组的布局相匹配。即使是POD结构(我相信是最严格的结构类),标准也相当具体成员之间可以有填充。(“因此,POD struct对象中可能会有未命名的填充,但不是在其开头,这是实现适当对齐所必需的。”——非规范性注释,但仍然清楚地表明了意图)
例如,对比标准布局结构的要求(C++11,§1.8/4):
普通可复制或标准布局类型(3.9)的对象应占用连续的存储字节。”
…对于阵列(§8.3.4/1):
数组类型的对象包含一个连续分配的非空集合,其中包含N个T类型的子对象
在数组中,元素本身需要连续分配,而在结构中,只需要存储是连续的
第三种可能性
struct foo {
char a, b, c, d;
};