提问人:Luchian Grigore 提问时间:1/19/2012 最后编辑:CommunityLuchian Grigore 更新时间:1/19/2012 访问量:166
这是内存泄漏吗,如果不是,标准是否保证?
Is this a memory leak, if not is it guaranteed by the standard?
问:
与最近的一个问题相关,我编写了以下代码:
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 char
char
以下安全吗?
是的,这当然是安全的。但是,第一个代码段具有 UB。
如果它是安全的,那不意味着我们实际上可以找到数组的大小吗?当然,不是以标准方式,但是编译器是否需要存储有关数组大小的信息?
是的,通常额外的数据存储在第一个元素之前。这用于调用正确数量的析构函数。访问这个是 UB。
是否需要存储有关数组大小的信息?
不。它只需要按预期工作。 可能只是一个普通的 malloc 调用,它不一定存储请求的大小 10。delete[]
new int[10]
我不确定当数组被分配基本类型时,删除是否必须知道数组的大小(这不需要调用析构函数)。在 Visual Studio 编译器中,仅为用户定义的对象存储值(在这种情况下,delete[] 必须知道数组的大小,因为它必须调用其析构函数)。
在内存中分配大小的位置是未定义的(在 Visual Studio 中,它位于 gcc 的同一位置)。
http://www.parashift.com/c++-faq-lite/freestore-mgmt.html#faq-16.14
我知道在 c 中,堆上任何一个的大小都位于指针之前。代码依赖于此。这在K&R中有所记录。malloc
free
但你不应该依赖它总是在那里或总是处于同一个位置。
如果你想知道数组的长度,那么我建议你创建一个类或结构来记录实际数组的容量,并将其传递给你的程序,而你以前只是传递一个.char*
有两种方法可以销毁数组,具体取决于数组的创建方式。在这两种情况下,编译器都需要为数组的每个元素调用析构函数,因此必须知道数组中的元素数。
如果数组是堆栈上的自动变量,则元素的数量在编译时是已知的。编译器可以对它发出的代码中的元素数量进行硬编码,以销毁数组。
如果数组是在堆上动态分配的,则必须有另一种机制来了解元素计数。该机制未在标准中指定,也没有以任何其他方式公开。我认为将计数放在数组前面的偏移量是一种常见的实现,但这肯定不是唯一的方法,实际的偏移量只是一个私有的实现细节。
由于编译器必须知道数组中有多少个元素,因此您会认为标准可以强制要求一种使该计数可供程序使用的方法。不幸的是,这是不可能的,因为计数只有在销毁时才知道。想象一下,该标准包括一个可以访问隐藏信息的函数: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
上一个:如何在MSVS中查看设置标志?
评论