Alright, I think we all agree that what happens with the following code is undefined, depending on what is passed,
好吧,我想我们都同意下面的代码没有定义,这取决于传递的是什么,
void deleteForMe(int* pointer)
{
delete[] pointer;
}
The pointer could be all sorts of different things, and so performing an unconditional delete[]
on it is undefined. However, let's assume that we are indeed passing an array pointer,
指针可以是各种不同的东西,因此在它上执行无条件的delete[]是没有定义的。但是,假设我们确实传递了一个数组指针,
int main()
{
int* arr = new int[5];
deleteForMe(arr);
return 0;
}
My question is, in this case where the pointer is an array, who is it that knows this? I mean, from the language/compiler's point of view, it has no idea whether or not arr
is an array pointer versus a pointer to a single int. Heck, it doesn't even know whether arr
was dynamically created. Yet, if I do the following instead,
我的问题是,在这种情况下指针是一个数组,谁知道这个?我的意思是,从语言/编译器的角度来看,它不知道arr是数组指针还是指向单个int的指针。但是,如果我这样做,
int main()
{
int* num = new int(1);
deleteForMe(num);
return 0;
}
The OS is smart enough to only delete one int and not go on some type of 'killing spree' by deleting the rest of the memory beyond that point (contrast that with strlen
and a non-\0
-terminated string -- it will keep going until it hits 0).
操作系统足够聪明,只删除一个int类型,而不会删除超过那个点的其余内存(与strlen和一个非\0终止的字符串相比,它将一直运行到0)。
So whose job is it to remember these things? Does the OS keep some type of record in the background? (I mean, I realise that I started this post by saying that what happens is undefined, but the fact is, the 'killing spree' scenario doesn't happen, so therefore in the practical world someone is remembering.)
记住这些东西是谁的工作?操作系统是否在后台保存某种类型的记录?(我的意思是,我意识到,我在这篇文章的开头说,发生的事情是没有定义的,但事实是,“杀人狂欢”的场景并没有发生,因此,在现实世界中,有人会记得。)
97
The compiler doesn't know it's an array, it's trusting the programmer. Deleting a pointer to a single int
with delete []
would result in undefined behavior. Your second main()
example is unsafe, even if it doesn't immediately crash.
编译器不知道它是一个数组,它信任程序员。使用delete[]删除指向单个int的指针将导致未定义的行为。第二个main()示例不安全,即使它不会立即崩溃。
The compiler does have to keep track of how many objects need to be deleted somehow. It may do this by over-allocating enough to store the array size. For more details, see the C++ Super FAQ.
编译器必须跟踪需要删除多少对象。它可以通过过度分配来存储数组大小。有关详细信息,请参阅c++超级常见问题解答。
98
One question that the answers given so far don't seem to address: if the runtime libraries (not the OS, really) can keep track of the number of things in the array, then why do we need the delete[]
syntax at all? Why can't a single delete
form be used to handle all deletes?
到目前为止,给出的答案似乎并没有解决:如果运行库(不是OS,真的)可以跟踪数组中的内容,那么为什么我们需要删除[]语法呢?为什么不能使用一个删除表单来处理所有的删除?
The answer to this goes back to C++'s roots as a C-compatible language (which it no longer really strives to be.) Stroustrup's philosophy was that the programmer should not have to pay for any features that they aren't using. If they're not using arrays, then they should not have to carry the cost of object arrays for every allocated chunk of memory.
这个问题的答案可以追溯到c++的词根,它是一种C语言兼容的语言(它不再真正地努力做到这一点)。Stroustrup的理念是程序员不应该为他们不使用的任何功能付费。如果它们不使用数组,那么它们不应该为每个分配的内存块承担对象数组的成本。
That is, if your code simply does
也就是说,如果你的代码简单的话。
Foo* foo = new Foo;
then the memory space that's allocated for foo
shouldn't include any extra overhead that would be needed to support arrays of Foo
.
那么分配给foo的内存空间不应该包含任何额外的开销来支持foo数组。
Since only array allocations are set up to carry the extra array size information, you then need to tell the runtime libraries to look for that information when you delete the objects. That's why we need to use
由于只设置了数组分配以携带额外的数组大小信息,因此您需要告诉运行时库在删除对象时查找该信息。这就是为什么我们需要使用
delete[] bar;
instead of just
而不是仅仅
delete bar;
if bar is a pointer to an array.
如果bar是指向数组的指针。
For most of us (myself included), that fussiness about a few extra bytes of memory seems quaint these days. But there are still some situations where saving a few bytes (from what could be a very high number of memory blocks) can be important.
对我们大多数人(包括我自己)来说,对一些额外字节的记忆感到大惊小怪,现在看来似乎有些古怪。但是,在某些情况下,保存几个字节(从可能是非常高的内存块)可以很重要。
25
Yes, the OS keeps some things in the 'background.' For example, if you run
是的,操作系统在“背景”中保留了一些东西。例如,如果你跑步
int* num = new int[5];
the OS can allocate 4 extra bytes, store the size of the allocation in the first 4 bytes of the allocated memory and return an offset pointer (ie, it allocates memory spaces 1000 to 1024 but the pointer returned points to 1004, with locations 1000-1003 storing the size of the allocation). Then, when delete is called, it can look at 4 bytes before the pointer passed to it to find the size of the allocation.
操作系统可以分配额外4字节,商店的大小分配的前4个字节分配的内存,并返回一个偏移量指针(即,它分配内存空间1000年至1024年,但返回的指针指向1004年,地点1000 - 1003存储分配的大小)。然后,当调用delete时,它可以在传递给它的指针之前查看4个字节,以找到分配的大小。
I am sure that there are other ways of tracking the size of an allocation, but that's one option.
我确信还有其他方法可以跟踪分配的大小,但这是一个选项。
12
This is very similar to this question and it has many of the details your are looking for.
这和这个问题很相似,它有很多你正在寻找的细节。
But suffice to say, it is not the job of the OS to track any of this. It's actually the runtime libraries or the underlying memory manager that will track the size of the array. This is usually done by allocating extra memory up front and storing the size of the array in that location (most use a head node).
但我想说的是,操作系统的工作不是跟踪这些信息。实际上,跟踪数组大小的是运行时库或底层内存管理器。这通常是通过预先分配额外的内存并将数组的大小存储在该位置(大多数使用head节点)来实现的。
This is viewable on some implementations by executing the following code
通过执行以下代码,可以在某些实现上看到这一点
int* pArray = new int[5];
int size = *(pArray-1);
8
delete
or delete[]
would probably both free the memory allocated (memory pointed), but the big difference is that delete
on an array won't call the destructor of each element of the array.
delete或delete[]可能会释放分配的内存(内存指向),但最大的区别是,对数组的delete不会调用数组中每个元素的析构函数。
Anyway, mixing new/new[]
and delete/delete[]
is probably UB.
无论如何,混合new/new[]和delete/delete[]可能是UB。
6
It doesn't know it's an array, that's why you have to supply delete[]
instead of regular old delete
.
它不知道它是一个数组,这就是为什么您必须提供delete[]而不是普通的旧删除。
5
I had a similar question to this. In C, you allocate memory with malloc() (or another similar function), and delete it with free(). There is only one malloc(), which simply allocates a certain number of bytes. There is only one free(), which simply takes a pointer as it's parameter.
我有一个类似的问题。在C中,您使用malloc()(或其他类似的函数)分配内存,并使用free()删除内存。只有一个malloc(),它只分配一定数量的字节。只有一个free(),它将指针作为参数。
So why is it that in C you can just hand over the pointer to free, but in C++ you must tell it whether it's an array or a single variable?
为什么在C语言中你可以把指针交给free,但是在c++中你必须告诉它它是数组还是单个变量?
The answer, I've learned, has to do with class destructors.
我学过,答案与类析构函数有关。
If you allocate an instance of a class MyClass...
如果您分配一个MyClass类的实例…
classes = new MyClass[3];
And delete it with delete, you may only get the destructor for the first instance of MyClass called. If you use delete[], you can be assured that the destructor will be called for all instances in the array.
用delete删除它,你可能只会得到调用MyClass的第一个实例的析构函数。如果您使用delete[],您可以放心,将对数组中的所有实例调用析构函数。
THIS is the important difference. If you're simply working with standard types (e.g. int) you won't really see this issue. Plus, you should remember that behavior for using delete on new[] and delete[] on new is undefined--it may not work the same way on every compiler/system.
这是重要的区别。如果您只是使用标准类型(例如int),您不会真正看到这个问题。另外,您应该记住,在new[]上使用delete和在new上使用delete[]的行为是没有定义的——它可能不会在每个编译器/系统上以相同的方式工作。
4
It's up to the runtime which is responsible for the memory allocation, in the same way that you can delete an array created with malloc in standard C using free. I think each compiler implements it differently. One common way is to allocate an extra cell for the array size.
这取决于负责内存分配的运行时,就像您可以使用免费的方式删除在标准C中使用malloc创建的数组一样。我认为每个编译器实现它都是不同的。一种常见的方法是为数组大小分配额外的单元格。
However, the runtime is not smart enough to detect whether or not it is an array or a pointer, you have to inform it, and if you are mistaken, you either don't delete correctly (E.g., ptr instead of array), or you end up taking an unrelated value for the size and cause significant damage.
然而,运行时不是足够聪明来检测是否它是一个数组或指针,你必须告诉它,如果你是错误的,你要么不删除正确(例如,ptr而不是数组),或者你采取一个不相关的值大小和造成重大的伤害。
4
ONE OF THE approaches for compilers is to allocate a little more memory and store count of elements in the head element.
编译器的一种方法是在head元素中分配更多的内存和存储元素的计数。
Example how it could be done: Here
如何做到这一点:这里
int* i = new int[4];
compiler will allocate sizeof(int)*5 bytes.
编译器将分配sizeof(int)*5字节。
int *temp = malloc(sizeof(int)*5)
Will store 4
in first sizeof(int)
bytes
将存储4在第一个sizeof(int)字节
*temp = 4;
and set i
和我
i = temp + 1;
So i
points to array of 4 elements, not 5.
我指向4个元素的数组,而不是5。
And
和
delete[] i;
will be processed following way
会按以下方式处理吗
int *temp = i - 1;
int numbers_of_element = *temp; // = 4
... call destructor for numbers_of_element elements if needed
... that are stored in temp + 1, temp + 2, ... temp + 4
free (temp)
2
Agree that the compiler doesn't know if it is an array or not. It is up to the programmer.
同意编译器不知道它是否是一个数组。这取决于程序员。
The compiler sometimes keep track of how many objects need to be deleted by over-allocating enough to store the array size, but not always necessary.
编译器有时通过分配足够多的对象来存储数组大小来跟踪需要删除多少对象,但并不总是必需的。
For a complete specification when extra storage is allocated, please refer to C++ ABI (how compilers are implemented): Itanium C++ ABI: Array Operator new COOKIEs
对于分配额外存储时的完整规范,请参考c++ ABI(如何实现编译器):Itanium C++ + ABI:数组操作符新COOKIE
1
Semantically, both versions of delete operator in C++ can "eat" any pointer; however, if a pointer to a single object is given to delete[]
, then UB will result, meaning anything may happen, including a system crash or nothing at all.
语义上,两个版本的c++删除操作符都可以“吃”任何指针;但是,如果一个指向单个对象的指针被指定为delete[],则会产生UB,这意味着任何事情都可能发生,包括系统崩溃或什么都不会发生。
C++ requires the programmer to choose the proper version of the delete operator depending on the subject of deallocation: array or single object.
c++要求程序员根据deallocation:数组还是单个对象选择正确的删除操作符版本。
If the compiler could automatically determine whether a pointer passed to the delete operator was a pointer array, then there would be only one delete operator in C++, which would suffice for both cases.
如果编译器能够自动确定传递给delete操作符的指针是否是指针数组,那么在c++中只有一个delete操作符,这对于这两种情况都足够了。
0
You cannot use delete for an array, and you cannot use delete [] for a non-array.
对于一个数组,不能使用delete,也不能对非数组使用delete[]。
-1
Hey ho well it depends of what you allocating with new[] expression when you allocate array of build in types or class / structure and you don't provide your constructor and destructor the operator will treat it as a size "sizeof(object)*numObjects" rather than object array therefore in this case number of allocated objects will not be stored anywhere, however if you allocate object array and you provide constructor and destructor in your object than behavior change, new expression will allocate 4 bytes more and store number of objects in first 4 bytes so the destructor for each one of them can be called and therefore new[] expression will return pointer shifted by 4 bytes forward, than when the memory is returned the delete[] expression will call a function template first, iterate through array of objects and call destructor for each one of them. I've created this simple code witch overloads new[] and delete[] expressions and provides a template function to deallocate memory and call destructor for each object if needed:
嘿ho它取决于你分配新的[]表达当你分配的数组构建类型或类/结构和你不提供构造函数和析构函数操作符将把它当作大小“sizeof(对象)* numObjects”而不是对象数组,所以在这种情况下,数量的分配对象将不会被存储在任何地方,但是如果你分配对象数组提供对象的构造函数和析构函数比行为改变,新表达式将分配4个字节并存储在前4个字节的对象数量每个其中之一的析构函数可以调用,因此新[]表达式将返回指针移4字节,比内存返回删除时[]表达式将首先调用一个函数模板,遍历对象和数组调用析构函数为每个其中之一。我创建了这个简单的代码,它重载了新的[]和删除[]表达式,并提供了一个模板函数来释放内存,并在需要时为每个对象调用析构函数:
// overloaded new expression
void* operator new[]( size_t size )
{
// allocate 4 bytes more see comment below
int* ptr = (int*)malloc( size + 4 );
// set value stored at address to 0
// and shift pointer by 4 bytes to avoid situation that
// might arise where two memory blocks
// are adjacent and non-zero
*ptr = 0;
++ptr;
return ptr;
}
//////////////////////////////////////////
// overloaded delete expression
void static operator delete[]( void* ptr )
{
// decrement value of pointer to get the
// "Real Pointer Value"
int* realPtr = (int*)ptr;
--realPtr;
free( realPtr );
}
//////////////////////////////////////////
// Template used to call destructor if needed
// and call appropriate delete
template
void Deallocate( T* ptr )
{
int* instanceCount = (int*)ptr;
--instanceCount;
if(*instanceCount > 0) // if larger than 0 array is being deleted
{
// call destructor for each object
for(int i = 0; i <*instanceCount; i++)
{
ptr[i].~T();
}
// call delete passing instance count witch points
// to begin of array memory
::operator delete[]( instanceCount );
}
else
{
// single instance deleted call destructor
// and delete passing ptr
ptr->~T();
::operator delete[]( ptr );
}
}
// Replace calls to new and delete
#define MyNew ::new
#define MyDelete(ptr) Deallocate(ptr)
// structure with constructor/ destructor
struct StructureOne
{
StructureOne():
someInt(0)
{}
~StructureOne()
{
someInt = 0;
}
int someInt;
};
//////////////////////////////
// structure without constructor/ destructor
struct StructureTwo
{
int someInt;
};
//////////////////////////////
void main(void)
{
const unsigned int numElements = 30;
StructureOne* structOne= nullptr;
StructureTwo* structTwo = nullptr;
int* basicType = nullptr;
size_t ArraySize = 0;
/**********************************************************************/
// basic type array
// place break point here and in new expression
// check size and compare it with size passed
// in to new expression size will be the same
ArraySize = sizeof( int ) * numElements;
// this will be treated as size rather than object array as there is no
// constructor and destructor. value assigned to basicType pointer
// will be the same as value of "++ptr" in new expression
basicType = MyNew int[numElements];
// Place break point in template function to see the behavior
// destructors will not be called and it will be treated as
// single instance of size equal to "sizeof( int ) * numElements"
MyDelete( basicType );
/**********************************************************************/
// structure without constructor and destructor array
// behavior will be the same as with basic type
// place break point here and in new expression
// check size and compare it with size passed
// in to new expression size will be the same
ArraySize = sizeof( StructureTwo ) * numElements;
// this will be treated as size rather than object array as there is no
// constructor and destructor value assigned to structTwo pointer
// will be the same as value of "++ptr" in new expression
structTwo = MyNew StructureTwo[numElements];
// Place break point in template function to see the behavior
// destructors will not be called and it will be treated as
// single instance of size equal to "sizeof( StructureTwo ) * numElements"
MyDelete( structTwo );
/**********************************************************************/
// structure with constructor and destructor array
// place break point check size and compare it with size passed in
// new expression size in expression will be larger by 4 bytes
ArraySize = sizeof( StructureOne ) * numElements;
// value assigned to "structOne pointer" will be different
// of "++ptr" in new expression "shifted by another 4 bytes"
structOne= MyNew StructureOne[numElements];
// Place break point in template function to see the behavior
// destructors will be called for each array object
MyDelete( structOne );
}
///////////////////////////////////////////
-2
just define a destructor inside a class and execute your code with both syntax
只需在类中定义一个析构函数,并使用两种语法执行代码
delete pointer
delete [] pointer
according to the output u can find the solutions
根据输出u可以找到解
-3
The answer:
答案是:
int* pArray = new int[5];
int* pArray =新int[5];
int size = *(pArray-1);
int大小= *(pArray-1);
Posted above is not correct and produces invalid value. The "-1"counts elements On 64 bit Windows OS the correct buffer size resides in Ptr - 4 bytes address
上面张贴的是不正确的并且产生无效的值。64位Windows操作系统上的“-1”计数元素正确的缓冲区大小位于Ptr - 4字节地址中