将基本类型指针转换为结构指针-对齐和填充? [英] Cast A primitive type pointer to A structure pointer - Alignment and Padding?

查看:72
本文介绍了将基本类型指针转换为结构指针-对齐和填充?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Just 20 minutes age when I answered a question, I come up with an interesting scenario that I'm not sure of the behavior:

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

Let me have an integer array of size n, pointed by intPtr;

int* intPtr;

让我也有一个这样的结构:

and let me also have a struct like this:

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

我的问题是我是否进行强制转换 intStruct * structPtr =(intStruct * )intPtr;

My question is if I do a cast intStruct* structPtr = (intStruct*) intPtr;

Am如果遍历该结构的元素,我确定可以正确获取每个元素吗?

Am I sure to get every element correctly if I traverse the elements of the struct? Is there any possibility of miss-alignment(possible because of padding) in any architecture/compiler?

推荐答案

在任何体系结构/编译器中都可能发生未对准(可能是由于填充)的问题?即使是POD结构(也就是我认为限制性最强的一类结构)也可以在成员之间填充。 (因此,为了实现适当的对齐,在POD-struct对象中可能会存在未命名的填充,但在其开始处可能没有,但是为了实现适当的对齐而没有填充。-非规范性注释,但意图很明确)。

The standard is fairly specific that even a POD-struct (which is, I believe the most restrictive class of structs) can have padding between members. ("There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment." -- a non-normative note, but still makes the intent quite clear).

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

For example, contrast the requirements for a standard-layout struct (C++11, §1.8/4):


普通可复制或标准布局类型(3.9)的对象应占据连续的存储字节。

An object of trivially copyable or standard-layout type (3.9) shall occupy contiguous bytes of storage."

...以及用于数组的对象(第8.3.4 / 1节):

...with those for an array (§8.3.4/1):


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

An object of array type contains a contiguously allocated non-empty set of N subobjects of type T.

在数组中,元素本身必须可以连续分配,而在结构中,仅要求 storage 是连续的。

In the array, the elements themselves are required to be allocated contiguously, whereas in the struct, only the storage is required to be contiguous.

第三个可能导致连续存储的可能性要求更加合理,考虑一个不可复制或标准布局的结构/类。在这种情况下,存储可能根本不连续。例如,一个实现可能会预留一个内存区域来保存所有私有变量,而将一个单独的内存区域分开来保存所有公共变量。为了更具体一点,请考虑以下两个定义:

The third possibility that might make the "contiguous storage" requirement make more sense would be to consider a struct/class that is not trivially copyable or standard layout. In this case, it's possible that the storage might might not be contiguous at all. For example, an implementation might set aside one area of memory for holding all the private variables, and an entirely separate area of memory to hold all the public variables. To make that a little more concrete, consider two definitions like:

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

class B {
    int x;
public:
    int y;
} b;

使用这些定义,内存的布局可能类似于:

With these definitions, the memory might be laid out something like:

a.a;
b.x;

// ... somewhere else in memory entirely:

a.b;
b.y;

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

In this case, neither the elements nor the storage needs to be contiguous, so interleaving parts of entirely separate structs/classes is allowable.

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

That said, the first element must be at the same address as the struct as a whole (9.2/17): "A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa."

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

In your case, you have a POD-struct, so (§9.2/17): "A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa." Since the first member must be aligned, and the remaining members are all of the same type, it's impossible for any padding to be truly necessary between the other members (i.e., except for bit-fields, any type you can put in a struct you can also put in an array, where contiguous allocation of the elements is required). If you have elements smaller than a word, on a word-oriented machine (e.g., early DEC Alphas), it's possible that padding could make access somewhat simpler though. For example, early DEC Alphas (at the hardware level) were only capable of reading/writing an entirely (64-bit) word at a time. As such, let's consider something like a struct of four char elements:

struct foo { 
   char a, b, c, d;
};

如果需要将它们放在内存中以便它们连续,则访问 foo :: b (例如)将要求CPU加载该字,然后将其右移8位,然后进行掩码以将该字节零扩展以填充整个寄存器。

If it was required to lay these out in memory so they were contiguous, accessing a foo::b (for example) would require that the CPU load the word, then shift it 8-bits right, then mask to zero-extend that byte to fill the entire register.

存储会更加糟糕-CPU将不得不加载整个单词的当前值,屏蔽掉相应的char大小的当前内容,然后进行移位

Storing would be even worse -- the CPU would have to load the current value of the whole word, mask out the current contents of the appropriate char-sized piece of that, shift the new value to the correct place, OR it into the word, and finally store the result.

通过元素之间的填充进行对比,每个元素都变得简单

By contrast, with padding between the elements, each of those becomes a simple load/store, with no shifting, masking, etc.

至少要在有内存的情况下使用DEC的Alpha普通编译器 int code>是32位,而 long 是64位(早于 long long )。因此,使用四个 int s的结构,您可能希望看到元素之间还有32位填充(以及最后一个元素之后还有32位)

At least if memory serves, with DEC's normal compiler for the Alpha, int was 32 bits, and long was 64 bits (it predated long long). As such, with your struct of four ints, you could have expected to see another 32 bits of padding between the elements (and another 32 bits after the last element as well).

鉴于您确实具有POD结构,尽管如此,您仍然有一些可能。我可能更喜欢的是使用 offsetof 获取结构成员的偏移量,创建它们的数组,然后通过这些偏移量访问成员。我在夫妇中展示了如何执行此操作/ 179910>先前的答案。

Given that you do have a POD-struct, you still have some possibilities though. The one I'd probably prefer would be to use offsetof to get the offsets of the members of the struct, create an array of them, and access the members via those offsets. I showed how to do this in a couple of previous answers.

这篇关于将基本类型指针转换为结构指针-对齐和填充?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆