运算符 new[] 的返回地址与数组的实际地址之间的差异
difference between return address of operator new[] and the actual address got for the array
我最近在玩新的运算符重载。当我重载 new[] 运算符(用于分配数组的新运算符)时,我注意到了一个奇怪的行为。
这是我的代码:
#include <iostream>
using namespace std;
class Pool
{
public:
void* alloc(size_t size) {
return malloc(size);
}
};
class MyClass
{
public:
MyClass() {
cout<<"ctor called"<<endl;
}
~MyClass() {
cout<<"dtor called"<<endl;
}
void* operator new(size_t size) {
cout<<"new called, size: "<<size<<endl;
return (void*)malloc(size);
}
void* operator new[](size_t size) {
cout<<"new[] called, size: "<<size<<endl;
void* result = (void*)malloc(size);
cout<<"in new[]: "<<result<<endl;
return result;
}
void* operator new(size_t size, void* ptr) {
cout<<"new(ptr) called, size: "<<size<<endl;
return (void*)ptr;
}
void* operator new(size_t size, Pool& pool) {
cout<<"new(Pool) called, size: "<<size<<endl;
return (void*)pool.alloc(size);
}
void operator delete(void* ptr) {
cout<<"delete called, ptr: "<<ptr<<endl;
free(ptr);
}
void operator delete(void* ptr, size_t size) {
cout<<"delete called, ptr: "<<ptr<<", size: "<<size<<endl;
free(ptr);
}
void operator delete[](void* ptr) {
cout<<"delete[] called, ptr: "<<ptr<<endl;
free(ptr);
}
void operator delete[](void* ptr, size_t size) {
cout<<"delete[] called, ptr: "<<ptr<<", size: "<<size<<endl;
free(ptr);
}
uint32_t data;
};
int main() {
Pool pool;
cout<<"Pool"<<endl;
new Pool;
cout<<"MyClass"<<endl;
MyClass *ptr1, *ptr2, *ptr3;
ptr1 = new MyClass;
ptr2 = new MyClass[10]();
cout<<(void*)ptr2<<endl;
ptr3 = new(pool) MyClass;
delete ptr1;
delete[] ptr2;
delete ptr3;
return 0;
}
结果(在OS X上使用gcc 64位)如下所示:
Pool
MyClass
new called, size: 4
ctor called
new[] called, size: 48
in new[]: 0x7fa7f0403840
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
0x7fa7f0403848
new(Pool) called, size: 4
ctor called
dtor called
delete called, ptr: 0x7fa7f0403830
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
delete[] called, ptr: 0x7fa7f0403840
dtor called
delete called, ptr: 0x7fa7f0403870
我注意到三件事:首先,我要求在 new[] 中分配 10 个 4 字节的对象,但函数收到的实际请求是 48 字节。 2nd,显然前 8 个字节用于其他目的:ptr2
接收的实际地址是 new[] 运算符返回的地址之后的 8 个字节。 第三、 地址也会在重载的 delete[] 函数中自动转换(向前转换 8 个字节)。
我还注意到,仅当我显式实现析构函数时,才会发生此行为。如果我只使用默认析构函数,则 8 个字节就消失了。
谁能告诉我这背后发生了什么?8 个字节的用途是什么?
谢谢。
允许数组新表达式调用数组运算符-new,其空间多于数组所需的空间。所需要的只是 array-new 表达式的值是指向数组中第一个元素的指针。
实际上,需要额外的空间来存储有关销毁数组时需要销毁多少元素的信息(有时称为"数组cookie")。
有趣的是,从数组运算符新函数请求的实际额外内存量是完全不可知的,并且可能会随着每次调用而更改。这基本上使数组放置新表达式有缺陷且无法使用。
仅供参考,相关条款为 C++11 5.3.4/10:
new-expression 将请求的空间量作为类型
std::size_t
的第一个参数传递给分配函数。该参数应不小于所创建对象的大小;仅当对象是数组时,它才可能大于所创建对象的大小。
最有趣的例子如下:
new T[5]
导致operator new[](sizeof(T) * 5 + x)
调用,并且
new(2,f) T[5]
会导致operator new[](sizeof(T) * 5 + y, 2, f)
调用。在这里,
x
和y
是非负的未指定值,表示数组分配开销;结果是 new-expression 将从operator new[]
返回的值中偏移此量。此开销可以应用于所有数组新表达式,包括引用库函数operator new[](std::size_t, void*)
和其他放置分配函数的表达式。开销量可能因 new 调用而异。
您可能会很高兴地了解到,Itanium ABI 对数组 cookie 有非常合理的规则;例如,对于易于破坏的对象数组,不需要任何规则。
- 24 位地址和 24 位算术与 24 位地址与 16 位地址算术之间的区别?
- 按引用传递和按地址传递之间的差异
- 参考和要求可变地址之间的区别
- 可能的数据竞争在内存中写入相同地址之间
- 结构成员的地址作为编译时间常数之间的差异
- 为什么分配的变量地址之间相差 16 个字节?
- 数组与其地址之间的关系是什么?
- Windows 7和Windows 8.1之间的堆栈地址差异
- 指针地址之间的差异
- OpenCL:指令和地址之间的状态空间不匹配
- 相邻数组元素地址之间的差异
- 运算符 new[] 的返回地址与数组的实际地址之间的差异
- 这些本地函数的返回地址之间有什么区别
- 内存地址的数字表示和对齐之间的关系
- 的地址和指针地址之间的区别是什么
- 在该地址c++分配之前和之后相等指针之间的差异
- 通过引用传递的int型与传递的int型的地址之间的区别
- 在函数之间传递地址值时出现C++分段错误
- 我如何查看地址和值 c++ 类之间的区别
- 成员函数指针在不同地址空间之间的互换性