如果new []表达式用于创建具有析构函数的对象数组, 数组中的对象可能无法正确分配
#include <stdint.h> #include <stdio.h> #pragma pack(8) struct A{ int64_t i; char dummy; ~A(){} }; int main(){ A* pa= new A[2]; printf("sizeof(A)= %d, pointer= %p", sizeof(A), pa); }
(我使用VC ++ 2010 express构建32位目标)
输出(在我的电脑上)是:
sizeof(A)= 16 pointer= 00344f4c
(sizeof(A)= 16表示编译器解释了A的对齐要求,结构用7个字节填充 [编辑:__ _alignof(A)也返回8] )
我理解为什么会发生这种情况:new []需要存储数组长度,并且它为此目的使用前4个字节的已分配内存,然后在没有适当填充的情况下分配数组本身。
从实际角度来看,这种行为肯定很差, 但它是否符合标准?
答案 0 :(得分:7)
您应该使用__declspec
来实现此目的。您的代码也在我的计算机上生成了未对齐的对象(使用VS2010),但当我更改为__declspec(align(8))
时,指针已正确对齐。
我相信pragma pack
只会更改结构的 size ,并且不会对其位置做出任何保证。
答案 1 :(得分:2)
标准保证内存正确对齐。
我怀疑Visual Studio有一些基本的错误。
目前我不遵循为什么你认为它没有对齐?
您是否尝试建议它应与16字节边界对齐? 标准不要求这样做。它可能只需要对齐4个字节的边界。
2基本对齐由小于或等于所有上下文中实现所支持的最大对齐的对齐来表示,它等于alignof(std :: max_align_t)(18.2)。
因此,以下代码应打印出实现所需的最大对齐:
#include <iostream>
#include <cstddef>
#include <cstdalign>
int main()
{
std::cout << alignof(std::max_align_t) << "\n";
}
答案 2 :(得分:1)
从字面上回答你的问题,我认为标准根本没有说明#pragma pack。所以我会说你的代码根据标准有未定义的行为。
答案 3 :(得分:1)
有趣。我的第一个问题是:没有#pragma包会发生什么?这个结构的原生对象是什么?操作员new无法知道您可能使用pragma设置了哪些选项,因此其存在或缺席在此处无效。并且正式地说,没有违反标准,因为英特尔不会要求任何对齐(但保持正确的对齐确实有助于提高性能)。总而言之,它似乎不是一个理想的“功能”,但它是一个QoI问题,而不是标准的一致性。
答案 4 :(得分:0)
printf("sizeof(A)= %d, pointer= %08x", sizeof(A), pa);
为了表示谨慎,请在打印内存地址时使用%p
:
printf("sizeof(A)= %d, pointer= %p", sizeof(A), pa);
%x
期望unsigned int
的大小可能与指针的大小不同。
答案 5 :(得分:0)
IIRC默认打包是在字节对齐上,使用/Zpn设置为另一个打包,然后你得到你的填充。 (或#pragma pack(n))
编辑:想想我认为默认包装甚至在VS版本之间变化
答案 6 :(得分:0)
如果struct A
只包含plain old data,您可以使用aligned_malloc
(请参阅Microsoft's documentation)。如果需要构建,可以将其与placement new
运算符组合使用。像这样的东西(我没有Microsoft VC ++,所以它可能无法编译):
void* buf = _aligned_malloc (2 * sizeof (struct A), 8) ;
A* pa = new (buf) A[2];
删除这个很痛苦:你必须为每个数组元素显式调用pa [i] .~A(),然后_aligned_free (buf)
。
答案 7 :(得分:0)
operator new()
哪个关键字new
在引擎盖下调用,不知道类型的对齐方式。唯一的对齐要求是operator new()
必须为具有最大对齐要求的内置类型分配适当的内存,在32位平台上通常为8(对齐要求为double)。
话虽如此,您发布的示例分配了在8字节边界上对齐的内存。