Cast一个原始类型指针指向一个结构指针 - 对齐和填充?

Seç*_*şçı 9 c++ casting

当我回答问题时,只有20分钟的年龄,我想出了一个有趣的场景,我不确定这种行为:

让我有一个大小为n的整数数组,由intPtr指出;

int* intPtr;
Run Code Online (Sandbox Code Playgroud)

让我也有这样的结构:

typedef struct {
int val1;
int val2;
//and less or more integer declarations goes on like this(not any other type)
}intStruct;
Run Code Online (Sandbox Code Playgroud)

我的问题是如果我做演员 intStruct* structPtr = (intStruct*) intPtr;

如果我遍历结构的元素,我肯定能正确获取每个元素吗?在任何架构/编译器中是否存在未对齐(可能因填充而导致)的可能性?

Jer*_*fin 5

该标准非常具体,即使是POD结构(我认为是最具限制性的结构类)也可以在成员之间进行填充.("有可能因此可以根据需要一个POD结构对象中未命名的填充,但不是在它的开始,实现适当的定位." - 一个非规范的音符,但仍使意图很清楚).

例如,对比标准布局结构(C++ 11,§1.8/ 4)的要求:

平凡可复制或标准布局类型(3.9)的对象应占用连续的存储字节."

...与数组(§8.3.4/ 1):

数组类型的对象包含一个类型为T的连续分配的非空N个子对象集.

在数组中,元素本身需要连续分配,而在结构中,只需要存储是连续的.

第三种可能使"连续存储"要求更有意义的可能性是考虑一个不易于复制或标准布局的结构/类.在这种情况下,存储可能根本不是连续的.例如,实现可能会留出一个内存区域来保存所有私有变量,以及一个完全独立的内存区域来保存所有公共变量.为了使其更具体,请考虑两个定义:

class A { 
    int a;
public:
    int b;
} a;

class B {
    int x;
public:
    int y;
} b;
Run Code Online (Sandbox Code Playgroud)

根据这些定义,内存可能会布局如下:

a.a;
b.x;

// ... somewhere else in memory entirely:

a.b;
b.y;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,元素存储都不需要是连续的,因此允许交错完全独立的结构/类的部分.

也就是说,第一个元素必须与整个结构的地址相同(9.2/17):"指向POD结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员)是一个位域,然后是它所在的单位,反之亦然."

在你的情况下,你有一个POD结构,所以(§9.2/ 17):"指向POD结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段) ,然后到它所在的单位),反之亦然." 由于第一个成员必须对齐,而其余成员都是相同的类型,因此在其他成员之间不可能真正需要任何填充(即,除了位字段,您可以放入结构中的任何类型也可以放入一个数组,其中需要连续分配元素).如果你有一个小于单词的元素,在面向单词的机器上(例如,早期的DEC Alphas),填充可能会使访问更简单一些.例如,早期的DEC Alphas(在硬件级别)只能一次读/写一个完整的(64位)字.因此,让我们考虑类似四个char元素的结构:

struct foo { 
   char a, b, c, d;
};
Run Code Online (Sandbox Code Playgroud)

如果需要将它们放在内存中以便它们是连续的,那么访问foo::b(例如)将要求CPU加载该字,然后将其向右移8位,然后屏蔽零 - 扩展该字节以填充整个寄存器.

存储会更糟糕 - CPU必须加载整个单词的当前值,屏蔽掉相应的char大小的当前内容,将新值移动到正确的位置,或者将其移入单词,最后存储结果.

相比之下,在元素之间使用填充,每个元素都成为一个简单的加载/存储,没有移位,屏蔽等.

至少如果内存服务,DEC的正常编译器为Alpha,int是32位,并且long是64位(它早于预先long long).因此,使用四个ints 的结构,您可能希望在元素之间看到另外32位填充(以及最后一个元素之后的另外32位).

鉴于你确实有一个POD结构,你仍然有一些可能性.我可能更喜欢的是用于offsetof获取结构成员的偏移量,创建它们的数组,并通过这些偏移量访问成员.我演示了如何在一个做到这一点的情侣以前的答案.