好吧,我想我们都同意,会发生什么情况下列代码是不确定,取决于什么是过去了,

void deleteForMe(int* pointer)
{
     delete[] pointer;
}

指针可能是各种不同的东西,所以执行无条件的 delete[] 它是不确定的。然而,让我们假设,我们确实通过一系列指针,

int main()
{
     int* arr = new int[5];
     deleteForMe(arr);
     return 0;
}

我的问题是,在这种情况下的指针 阵列是谁知道这个吗?我的意思是,从语言和编译器的观点看,它不知道是否 arr 是一系列针对一个指向一个int.哎呀,它甚至不知道是否 arr 是动态的建立。然而,如果我这样做以下相反,

int main()
{
     int* num = new int(1);
     deleteForMe(num);
     return 0;
}

该系统足够聪明,只有删除一个int和不去上一些类型的'的疯狂杀戮,删除其余的存储器超越这一点(相对于有 strlen 并非\0-终止串--它将继续下去,直到达到0).

因此,他们的工作是要记住这些事情?不OS保持某种类型的记录的背景吗?(我的意思是,我知道,我开始这个职位说过的话,会发生什么情况是不确定的,但事实是,'的疯狂杀戮的情况不会发生,因此在现实世界 有人 是回忆。)

有帮助吗?

解决方案

在编译器不知道它是一个数组,它的信任程序员。删除指向与int一个delete []会导致不确定的行为。第二个例子main()是不安全的,即使它不立即崩溃。

在编译器必须跟踪多少对象需要以某种方式删除。它可以通过过度分配足够的存储数组大小做到这一点。有关详细信息,请参见 C ++超级FAQ

其他提示

一个问题,到目前为止给出的答案似乎还没有解决:如果运行时库(而不是OS,真的)可以跟踪的东西数量的阵列中,那么为什么我们需要在delete[]语法所有?为什么不能在单个delete形式使用,以处理所有的删除?

这个问题的答案要追溯到C ++的根作为C兼容的语言(它不再是真正致力于成为。)Stroustrup的理念是,程序员不应该支付他们没有使用任何功能。如果他们不使用阵列,那么就应该不必携带对象阵列的成本的存储器分配每块。

也就是说,如果您的代码简单地确实

Foo* foo = new Foo;

那么所分配的用于foo存储器空间不应包括将需要支持Foo的阵列的任何额外的开销。

由于只有数组分配是建立携带额外的数组大小信息,您就需要告诉运行时库查找信息,当你删除的对象。这就是为什么我们需要使用

delete[] bar;

,而不是仅仅

delete bar;

如果杆是一个指向的数组。

对于我们大多数人(包括我自己),大概的记忆了一些额外的字节是烦躁显得古朴这些天。但仍有一些保存几个字节(来自这可能是一个非常高的数字存储块)可能是重要的一些情况。

是,则OS保持一些事情在“背景”。例如,如果运行

int* num = new int[5];

OS可以分配4个额外的字节,分配的大小存储在第一个4个字节被分配的存储器的,并返回一个偏移指针(即,它分配存储器空间1000至1024年,但指针返回指向1004,与位置一〇〇〇年至1003年存储所述分配的大小)。然后,删除被调用时,它可以看一下4个字节之前传递给它的指针以找到分配的大小。

我相信,有跟踪的分配的大小的其他方式,但是这是一个选项。

deletedelete[]很可能都释放分配的存储器(存储器指出),但是大的区别是在阵列上这delete不会调用数组的每个元素的析构函数。

反正混合new/new[]delete/delete[]可能UB。

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

我不得不这个类似的问题。在C语言中,可以分配使用malloc(内存)(或其他类似功能),并提供免费删除()。仅存在一个的malloc(),它简单地分配一定数目的字节。仅存在一个自由(),它简单地需要一个指针,因为它是参数。

那么,为什么在C你可以交出指针免费的,但在C ++中你必须告诉它是否是一个数组或一个变量?

在回答中,我了解到,有一流的析构函数做的。

如果你分配一个类的实例MyClass的...

classes = new MyClass[3];

和用delete删除它,你可能只得到MyClass的所谓的第一个实例的析构函数。如果使用删除[]时,可以放心,析构函数将被称为对阵列中的所有实例。

这是重要的区别。如果你只是用标准的类型(例如INT)的工作,你会不会真的看到这个问题。另外,你应该记住,行为使用删除新的[]和delete []新的不确定 - 它可能无法正常工作每个编译/系统以同样的方式

这是给它负责的内存分配,在可以删除使用免费标准C使用malloc创建的阵列相同的方式运行时。我想,每个不同的编译器实现它。一个常用的方法是分配一个额外单元的阵列的大小。

然而,运行时间是不足够聪明来检测它是否是一个数组或一个指针,你必须告知它,如果你错,则要么不正确地删除(例如,PTR代替阵列),或者最终以一个不相关的值的大小并造成显著损害。

ONE用于编译器的方法中的是分配在头部元件元件的多一点存储器和存储计数。

实施例如何可以实现: 这里

int* i = new int[4];

编译器将分配的sizeof(int)的* 5个字节。

int *temp = malloc(sizeof(int)*5)

4存储在第一字节sizeof(int)

*temp = 4;

和设置i

i = temp + 1;

因此i指向4个元素,而不是5。

的阵列

delete[] i;

将以下方式进行处理

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)

在语义上,删除操作员的在C ++中可以“吃”任何指针两个版本;然而,如果给定一个指向单个对象delete[],那么UB将导致,这意味着任何可能发生,包括在系统崩溃或什么都没有。

C ++需要程序员取决于释放的主题选择的删除操作的正确版本:阵列或者单个对象

如果编译器可以自动地确定传递到删除操作符的指针是否是一个指针数组,然后将有在C ++中只有一个删除操作符,这将足以满足这两种情况。

您不能使用的删除为一个数组,并且不能使用删除[] 对于非阵列。

“未定义的行为”只是意味着该语言的规范是没有gaurantees来会发生什么。这并不nessacerally意味着要出事。

  

所以,他们的工作是要记住这些东西?是否OS继续在后台某种类型的记录? (我的意思是,我意识到,我被说会发生什么是不确定的,但事实是,在“杀人狂魔”的情况不会发生,所以因此现实世界有人开始记住这个职位。)

有通常这里两层。底层存储器管理器和C ++实现。

在一般的内存管理器将记住(除其他事项外),将分配的内存块的大小。这可以比该块的C ++实现要求大。典型地,该存储器管理器将存储它的已分配的存储块之前的元数据。

C ++实现将通常只记得阵列的尺寸,如果它需要为它自己的目的这样做,通常是因为类型具有非繁琐析构函数。

因此,对于类型的一个简单的析构函数的“删除”和“删除[]”的执行通常是相同的。 C ++实现只是简单地传递指针到底层内存管理器。类似

free(p)

在另一方面,对于类型与非平凡的析构函数“删除”和“删除[]”很可能是不同的。 “删除”。将财产以后等(其中T是类型的指针指向)

p->~T();
free(p);

虽然 “删除[]” 会是这样。

size_t * pcount = ((size_t *)p)-1;
size_t count = *count;
for (size_t i=0;i<count;i++) {
  p[i].~T();
}
char * pmemblock = ((char *)p) - max(sizeof(size_t),alignof(T));
free(pmemblock);

嘿嗬以及它取决于你用新的[]表达式分配什么,当你在类型或类/结构分配构建的数组,你不提供你的构造函数和析构函数的操作将把它作为一个尺寸“的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<class T>
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 );
}
///////////////////////////////////////////

只要定义一个析构函数类的内部和与两个语法执行代码

delete pointer

delete [] pointer
根据输出u可以找到解决方案

答案:

INT *粒子阵列=新INT [5];

INT尺寸= *(粒子阵列-1);

以上发布是不正确的,并产生无效值。  “-1”计数元素 在64位的Windows操作系统的正确的缓冲区大小驻留在PTR - 4个字节的地址

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top