在这个I need C++ array class template, which is fixed-size, stack-based and doesn't require default constructor回答中,我发布了一段代码,即使用char new的placement new。对我来说,这是绝对正常的。但根据评论,这段代码是错误的。
任何人都可以更详细地解释一下吗?
特别是阵列出了什么问题。我从评论中理解的是T x[size];
可能不适合char x[size*sizeof(T)];
。我不相信这是真的。
编辑:
我越来越困惑。我知道结构的对齐方式。是的,当你有一个结构时,属性从不同的偏移开始,那么你可能会想。
好的,现在我们回到阵列了。您告诉我T x[size];
与char x[size*sizeof(T)];
的大小相同,但我不能将char数组作为T数组访问,因为可能存在一些对齐。当数组具有相同的大小时,如何进行对齐?
编辑2:
好的,我终于明白了,它可能从一个错误的地址开始。
编辑3:
大家好,你可以停止发帖:-) P,这总让我大吃一惊。我从未意识到这是可能的。
答案 0 :(得分:13)
T x[size]
数组总是完全适合size * sizeof(T)
个字节,这意味着char buffer[size*sizeof(T)]
总是足以存储这样的数组。
根据我的理解,该答案中的问题是,您的char
数组无法保证正确对齐以存储T
类型的对象。只保证malloc
- ed / new
- ed缓冲区能够正确对齐以存储任何较小或相同大小的标准数据类型(或由标准数据类型组成的数据类型),但如果您只是明确地声明一个char
数组(作为本地对象或成员子对象),没有这样的保证。
对齐意味着在某些平台上,严格(或不严格)要求分配所有int
对象,例如4字节边界。例如。您可以在地址int
或0x1000
放置0x1004
个对象,但不能在地址int
上放置0x1001
个对象。或者,更准确地说,您可以,但任何尝试将此内存位置作为int
类型的对象进行访问都将导致崩溃。
当您创建任意char
数组时,编译器不知道您计划将其用于什么。它可以决定将该数组放在地址0x1001
。由于上述原因,在这样的未对齐缓冲区中创建int
数组的天真尝试将失败。
某些平台上的对齐要求是严格的,这意味着任何使用未对齐数据的尝试都将导致运行时失败。在其他一些平台上,它们不那么严格:代码可以工作,但性能会受到影响。
正确对齐的需要有时意味着当您想要在任意int
数组中创建char
数组时,您可能必须 shift 从int
数组的开头向前char
数组。例如,如果char
数组位于0x1001
,则您别无选择,只能从地址int
开始构建的0x1004
数组(这是char
元素,索引为3)。为了容纳移位的int
数组的尾部,char
数组必须比size * sizeof(T)
求值的数组大3个字节。这就是为什么原始尺寸可能不够。
通常,如果你的char
数组没有以任何方式对齐,你真的需要一个size * sizeof(T) + A - 1
字节数组来容纳{{1}类型的对齐(即可能移位)的对象数组必须在A字节边界对齐。
答案 1 :(得分:0)
T
可能与char不同。
另外,itanium abi(例如)指定非pod数组的cookie,因此它知道删除时要走多少元素(调用析构函数)。通过new分配是这样的(iirc):
size_t elementCount;
// padding to get native alignment for 1st element
T elements[elementCount];
所以16字节对齐对象的分配是:
size_t elementCount; // 4
char padding[16 - sizeof(elementCount)];
T elements[elementCount]; // naturally aligned
char可以在某些系统上与1对齐,所以...你会看到错位和大小问题适合的位置。内置类型不需要调用它们的dtors,但其他一切都可以。
答案 2 :(得分:0)
§5.3.4/ 10:
new-expression传递的数量 要求分配的空间 作为类型的第一个参数
std::size_t
。那个论点应该是 不小于对象的大小 被创造;它可能大于 正在创建的对象的大小 仅当对象是数组时。对于 char和unsigned char数组 结果之间的差异 new-expression和地址 由分配函数返回 应该是的整数倍 最严格的对齐要求 (3.9)任何尺寸为的对象类型 不大于数组的大小 被创造。
这允许使用分配了new的char数组来进行放置构造 适当大小的其他类型的对象。必须在堆上分配预分配的缓冲区。否则,您可能会遇到alignment个问题。
答案 3 :(得分:0)
在某些系统上,内存访问必须“对齐”。为简单起见,这意味着地址必须是某个整数的倍数,称为该类型的“alignemnt要求”(参见C ++标准的3.9 / 5)。
因此,例如,假设sizeof(int) == 4
:
int *intarray = new int[2]; // 8 bytes
char *charptr = (char *)intarray; // legal reinterpret_cast
charptr += 1; // still 7 bytes available
*((int*)charptr) = 1; // BAD!
charptr的地址不是4的倍数,因此如果int
在您的平台上的对齐要求为4,则程序具有未定义的行为。
类似地:
char ra[8];
int *intptr = reinterpret_cast<int*>(ra);
intptr[0] = 1; // BAD!
ra
的地址不能保证是4的倍数。
但这没关系:
char ra = new char[8];
int *intptr = reinterpret_cast<int*>(ra);
intptr[0] = 1; // NOT BAD!
因为new
保证char数组分配与任何足够小以适应分配的类型(5.3.4 / 10)对齐。
远离自动化,很容易理解为什么编译器可以自由地不对齐数据成员。考虑:
struct foo {
char first[1];
char second[8];
char third[3];
};
如果标准保证second
是4对齐的(仍假设int
是4对齐的),则此结构的大小必须至少为16(及其对齐要求)至少4)。由于标准实际上是编写的,因此允许编译器给出这个结构大小为12,没有填充和没有对齐要求。
答案 4 :(得分:0)
char x[size*sizeof(T)];
可能不考虑alignment,其中T x [size];将。使用需要16字节对齐的SSE类型时,alignment(2)也非常重要