这是一个内存泄漏,如果不是由标准保证?

时间:2012-01-18 19:39:40

标签: c++

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;
}

如果它是安全的,这是否意味着我们实际上可以找到阵列的大小?当然,不是以标准方式,而是编译器必需来存储有关数组大小的信息?

我没有使用或计划使用此代码。我知道这是未定义的行为。我知道它没有任何保证。这只是一个理论问题!

6 个答案:

答案 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的指针已经丢失了所有上下文,因此编译器没有一致的方法知道数组中有多少元素,或者即使它根本就是数组。