热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

删除[]如何知道它是一个数组?-Howdoesdelete[]knowit'sanarray?

Alright,Ithinkweallagreethatwhathappenswiththefollowingcodeisundefined,dependingonw

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.)

记住这些东西是谁的工作?操作系统是否在后台保存某种类型的记录?(我的意思是,我意识到,我在这篇文章的开头说,发生的事情是没有定义的,但事实是,“杀人狂欢”的场景并没有发生,因此,在现实世界中,有人会记得。)

15 个解决方案

#1


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++超级常见问题解答。

#2


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.

对我们大多数人(包括我自己)来说,对一些额外字节的记忆感到大惊小怪,现在看来似乎有些古怪。但是,在某些情况下,保存几个字节(从可能是非常高的内存块)可以很重要。

#3


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.

我确信还有其他方法可以跟踪分配的大小,但这是一个选项。

#4


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

#5


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


6  

It doesn't know it's an array, that's why you have to supply delete[] instead of regular old delete.

它不知道它是一个数组,这就是为什么您必须提供delete[]而不是普通的旧删除。

#7


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[]的行为是没有定义的——它可能不会在每个编译器/系统上以相同的方式工作。

#8


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而不是数组),或者你采取一个不相关的值大小和造成重大的伤害。

#9


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)

#10


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

#11


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操作符,这对于这两种情况都足够了。

#12


0  

You cannot use delete for an array, and you cannot use delete [] for a non-array.

对于一个数组,不能使用delete,也不能对非数组使用delete[]。

#13


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

#14


-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可以找到解

#15


-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字节地址中


推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C++中省略号类型和参数个数不确定函数参数的使用方法,并提供了一个范例。通过宏定义的方式,可以方便地处理不定参数的情况。文章中给出了具体的代码实现,并对代码进行了解释和说明。这对于需要处理不定参数的情况的程序员来说,是一个很有用的参考资料。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 本文讨论了如何在codeigniter中识别来自angularjs的请求,并提供了两种方法的代码示例。作者尝试了$this->input->is_ajax_request()和自定义函数is_ajax(),但都没有成功。最后,作者展示了一个ajax请求的示例代码。 ... [详细]
author-avatar
初2新生_349
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有