假设我有某种类型
T
必须是 N
字节对齐。现在我声明一个类型为 T
: 的数组
T array[size];
阵列是否具有与类型
T
相同的对齐要求,还是有任何其他对齐要求?
是的,对齐要求必须相同。显然,
T
数组必须至少与单个 T
一样严格对齐,否则其第一个成员将无法正确对齐。事实上,数组的对齐方式不能比其元素类型更严格,这一事实源自标准第 8.3.4 节,其中指出数组是“连续”分配的元素子对象。考虑这个数组数组:
T a[2][size];
无论
size
的值是什么,两个数组
a[0]
和 a[1]
之间不能有“额外”填充,否则这违反了 连续分配要求。 同样,我们知道
(char*)&a[1] == (char*)&a[0] + sizeof(a[0])
和
sizeof(a[0]) == sizeof(T[size]) == size * sizeof(T)
。由于这适用于任何 size
,因此必须可以将 T
数组放置在与单个 T
对象适当对齐的任何地址(给定足够的地址空间)。显然,数组的开头必须至少与其第一个元素要求的严格对齐,因此其对齐要求不能不严格。
数组的起始地址加上每个元素的大小必须使第二个元素充分对齐。这对元素类型的大小施加了限制,我相信这意味着可以在结构的末尾引入填充,只是为了保持数组对齐,即使您从未在数组中使用该结构。但这并不意味着需要更严格的调整。
通过归纳,如果前两个元素都可以,那么后续元素也可以,因此为数组提供与其元素相同的对齐要求应该没问题。
不过,如果能引用规范就更好了。
我相信,由于数组的每个元素都具有相同的大小,因此仅对齐第一个元素就会自动对齐其余元素,因此元素之间永远不会有任何填充。
对于简单数组来说这可能是正确的,但对于复杂场景则不然。
数组的步长可以大于元素大小,即每个单独元素之间可能有焊盘。
以下是一个很好的例子
struct ThreeBytesWide {
char a[3];
};
struct ThreeBytesWide myArray[100];
ThreeBytesWide 数组的每个元素都可以与四字节边界对齐
编辑:正如评论中所述,提到在各个元素之间有焊盘是指元素本身为 3 个字节并与四字节边界对齐。
#include <iostream>
__declspec(align(32))
struct Str1
{
int a;
char c;
};
template<typename T>
struct size
{
T arr[10];
};
int main()
{
size<Str1> b1;
std::cout << sizeof(Str1) << std::endl; // prints 32
std::cout << sizeof(b1) << std::endl; // prints 320
std::cin.ignore();
return 0;
}
参考资料:
这里(用于对齐) §5.3.6 [expr.alignof] 1/
alignof
表达式产生其操作数类型的对齐要求。操作数应为表示完整对象类型或其数组的 type-id [...] 3/ [...] 当既是数组类型的对齐方式,又是元素类型的对齐方式,那么它们必须是相同的。
alignof
应用于数组类型时,结果应为元素类型的对齐方式.具体来说,如果alignof(array_type)
接受的答案中的参数与@Pubby所述的原因存在差距:对于固定长度的数组类型,为简单起见,说
T[N][M]
,其中N
是大于1的2的幂,编译器可以一致地为
alignof(T)*N
和
T[N]
选择
T[N][M]
对齐方式。为了填补这一空白,您必须争辩说,对于固定长度的数组类型,对齐不依赖于该固定长度。
有理由相信,如果违反该假设,那么 C++(或 C)实现将很难工作。例如,将指向固定长度数组的指针转换为指向较短数组的指针是有效的,这往往意味着它们应该具有相同的对齐方式。然而,这个论点也不起作用:因为通常在另一个方向上转换是无效的,即从较短的数组类型到较长的数组类型,这本身并不能排除后者可能具有更严格对齐的可能性.无论如何,依赖关于
alignof