与recent question相关,我写了以下代码:
int main()
{
char* x = new char[33];
int* sz = (int*)x;
sz--;
sz--;
sz--;
sz--;
int szn = *sz; //szn is 33 :)
}
我知道它不安全,永远不会使用它,但它会让人想起一个问题:
以下是否安全?这是内存泄漏吗?
char* allocate()
{
return new char[20];
}
int main()
{
char* x = allocate();
delete[] x;
}
如果它是安全的,这是否意味着我们实际上可以找到阵列的大小?当然,不是以标准方式,而是编译器必需来存储有关数组大小的信息?
答案 0 :(得分:5)
以下是否安全?
是的,当然这是安全的。第一个片段有UB。
如果它是安全的,这是否意味着我们实际上可以找到阵列的大小?当然,不是以标准方式,而是编译器需要存储有关数组大小的信息吗?
是的,通常在第一个元素之前存储额外数据。这用于调用正确数量的析构函数。这是UB访问它。
需要存储有关数组大小的信息吗?
没有。它只需delete[]
按预期工作。 new int[10]
可能只是一个普通的malloc调用,它不一定存储请求的大小10。
答案 1 :(得分:1)
这是安全的,并不是内存泄漏。标准要求delete[]
通过任何数组分配来处理释放内存。
如果它是安全的,这是否意味着我们实际上可以找到数组的大小?
标准没有对存储分配大小的位置和方式提出具体要求。如上所示,这可以被发现,但是不同的编译器/平台也可以使用完全不同的方法。因此,依靠这种技术来发现尺寸是不安全的。
答案 2 :(得分:1)
我知道在c中,堆上任何malloc
的大小都驻留在指针之前。 free
的代码依赖于此。这在K& R中有记载。
但你不应该依赖于总是在那里或总是处于同一位置。
如果你想知道数组长度,那么我建议你创建一个类或结构来记录实际数组旁边的capcity,并将它传递给你之前刚刚传递char*
的程序。< / p>
答案 3 :(得分:0)
int main()
{
char* x = new char[33];
int* sz = (int*)x;
sz--;
sz--;
sz--;
sz--;
int szn = *sz; //szn is 33 :)
}
这是一种未定义的行为,因为您访问了未分配的内存位置。
是否需要存储有关数组大小信息的编译器?
没有
如果它是安全的,这是否意味着我们实际上可以找到阵列的大小?
你在第二代码snipet中没有做任何特别的事情,因此它是安全的。但是没有办法获得数组的大小。
答案 4 :(得分:0)
我不确定当数组分配了基本类型(不需要调用析构函数)时,delete必须知道数组的大小。在visual studio编译器中,该值仅存储在用户定义的对象中(在这种情况下,delete []必须知道数组的大小,因为它必须调用它们的析构函数。)
在内存中分配大小的位置是未定义的(在visual studio中它与gcc位于同一位置)。
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.14
答案 5 :(得分:0)
有两种方法可以销毁数组,具体取决于它的创建方式。在这两种情况下,编译器都需要为数组的每个元素调用析构函数,因此必须知道数组中元素的数量。
如果数组是堆栈上的自动变量,则在编译时已知元素数。编译器可以硬编码它发出的用于销毁阵列的代码中的元素数量。
如果在堆上动态分配数组,则必须有另一种机制来了解元素计数。标准没有规定该机制,也不以任何其他方式公开。我认为将计数放在数组前面的偏移处是一种常见的实现,但它肯定不是唯一的方法,而实际的偏移只是一个私有的实现细节。
由于编译器必须知道数组中有多少元素,因此您认为标准可以强制要求一种方法使该计数可用。不幸的是,这是不可能的,因为计数仅在销毁时知道。想象一下,该标准包含一个可以访问隐藏信息的函数count_of
:
MyClass array1[33];
MyClass * array2 = new MyClass[33];
cout << count_of(array1) << count_of(array2); // outputs 33 33
Foo(array1);
Foo(array2);
MyClass * not_array = new MyClass;
Foo(not_array);
void Foo(MyClass * ptr)
{
for (int i = 0; i < count_of(ptr); ++i) // how can count_of work here?
...
}
由于传递给Foo
的指针已经丢失了所有上下文,因此编译器没有一致的方法知道数组中有多少元素,或者即使它根本就是数组。