C++ 这是内存泄漏吗?如果不是,标准是否保证?

C++ 这是内存泄漏吗?如果不是,标准是否保证?,c++,C++,与a相关,我编写了以下代码: 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()

与a相关,我编写了以下代码:

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;
}
如果安全的话,这不意味着我们可以找到数组的大小吗?虽然不是以标准方式,但编译器是否需要存储有关数组大小的信息


我没有使用或计划使用此代码。我知道这是未定义的行为。我知道没有任何保证。这只是一个理论问题 这是安全的,并且不是内存泄漏。标准要求delete[]通过任何数组分配来释放内存

如果安全的话,这不意味着我们可以找到数组的大小吗

这些标准没有对存储分配大小的位置和方式提出具体要求。如上所示,这是可以发现的,但不同的编译器/平台也可以使用完全不同的方法。因此,依靠这种技术来发现尺寸是不安全的

int main()
{
   char* x = new char[33];
   int* sz = (int*)x;
   sz--;
   sz--;
   sz--;
   sz--;
   int szn = *sz; //szn is 33 :)
}
这是一个未定义的行为,因为您访问了未分配的内存位置

编译器是否需要存储有关数组大小的信息

没有

如果安全的话,这不意味着我们可以找到数组的大小吗

在第二个代码snipet中,您没有做任何特殊的操作,因此它是安全的。但是没有办法得到数组的大小

以下是安全的吗

是的,当然安全了。然而,第一个片段已经被删除了

如果安全的话,这不意味着我们可以找到数组的大小吗?虽然不是以标准方式,但编译器是否需要存储有关数组大小的信息

是的,通常在第一个元素之前存储额外的数据。这用于调用正确数量的析构函数。访问这个是很困难的

是否需要存储有关阵列大小的信息


不需要。它只需要按预期删除[]项工作。new int[10]可能只是一个普通的malloc调用,它不一定会存储请求的大小10。

我不确定,当数组分配了基本类型而不需要调用析构函数时,delete必须知道数组的大小。在visual studio编译器中,仅为用户定义的对象存储该值。在这种情况下,delete[]必须知道数组的大小,因为它必须调用其析构函数

在内存中分配的大小在VisualStudio中是未定义的,它与gcc位于同一位置


我知道在c中,堆上任何malloc的大小都位于指针之前。免费的代码依赖于此。这在K&R中有记录

但你不应该总是依赖于这一点,或者总是站在同一个位置上


如果您想知道数组长度,那么我建议您创建一个类或结构来记录实际数组的容量,并将其传递给您的程序,您以前只需传递一个char*

销毁数组有两种方法,具体取决于数组的创建方式。在这两种情况下,编译器都需要为数组的每个元素调用析构函数,因此数组中的元素数必须是已知的

如果数组是堆栈上的自动变量,则在编译时已知元素的数量。编译器可以硬编码它为销毁数组而发出的代码中的元素数

如果数组是在堆上动态分配的,则必须有另一种机制来了解元素计数。该机制未在标准中规定,也未以任何其他方式公开。我认为将计数设置为与数组前端的偏移量是一种常见的实现,但这肯定不是唯一的方法,而实际偏移量只是一个私有实现细节

因为编译器必须知道数组中有多少个元素,所以您可能会认为标准有可能规定一种方法使程序可以使用该计数。不幸的是,这是不可能的,因为只有在销毁时才知道计数。假设标准中包含一个函数count_,可以访问隐藏信息:

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的指针丢失了所有上下文,编译器无法一致地知道数组中有多少个元素,甚至它是否是数组。

第一个代码段与第二个代码段的关系如何?为什么你认为第二个代码段不安全?@JamesMcNellis第一个代码段显示特定的编译器将数组大小的信息存储在某个地方。这让我想起了第二个代码段。@JamesMcNellis,第一个代码段说明了在某些实现中,至少分配的内存大小是如何存储在内存块之前的。这就是他关于运行时总是知道大小的观点
在x64上,当您删除它时,它的内存块。ubuntu 11.10在打印szn时输出0。有道理,因为它是UB@VJovic我想,这适用于MSVS2008的Win7x64,我怀疑它适用于许多其他平台。没有真正的帮助。我知道这不是标准行为,正如我已经指出的。如果编译器不需要存储信息,那么它如何在第二段代码中释放整个数组呢?不需要在该位置存储信息,尽管有些编译器是这样做的。它是未定义的,因为您不能依赖位于该位置的信息,而且您可能正在访问完全不相关的数据。@Luchian:同样的方法,它可以释放分配有新字符的单个字符。需要存储大小是因为它需要调用正确数量的析构函数。但是,调用char的析构函数是一个noop,因此,按照“好像”规则,编译器不能调用任何东西,因此不需要存储任何东西。@LuchianGrigore你说的是编译器,但编译器有很多!它们的工作方式都不同!即使是同一编译器的不同版本也可能以不同的方式处理此问题。不要试图让这件事成功。请。@LuchianGrigore所以,你的问题是针对操作系统的。编译器不存储有关数组大小的信息