Bien, creo que todos estamos de acuerdo en que lo que sucede con el código siguiente es indefinido, dependiendo de lo que se pasa,

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

El puntero podría ser todo tipo de cosas diferentes, y así realizar un incondicional delete[] en es indefinido. Sin embargo, vamos a suponer que de hecho, estamos pasando un puntero de matriz,

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

Mi pregunta es, en este caso, donde el puntero es una matriz, que es lo que sabe? Quiero decir que, desde el lenguaje/compilador punto de vista, no tiene idea de si o no arr es una matriz puntero frente a un puntero a una sola int. Diablos, ni siquiera sé si arr fue creado dinámicamente. Sin embargo, si hago lo siguiente en su lugar,

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

El SO es lo suficientemente inteligente como para eliminar sólo uno de int y no ir en algún tipo de ‘matanza’ eliminando el resto de la memoria más allá de ese punto (en contraste con el strlen y no\0cadena terminada — se va a seguir hasta que llega a 0).

Tan cuyo trabajo es recordar estas cosas? ¿El SO de mantener algún tipo de registro en el fondo? (Quiero decir, me doy cuenta de que he comenzado este post diciendo que lo que sucede no está definido, pero el hecho es que la ‘matanza’ escenario no sucede, por lo tanto, en el mundo práctico alguien es recordar.)

InformationsquelleAutor GRB | 2009-04-01

16 Comentarios

  1. 98

    Que el compilador no sabe que es una matriz, es confiar en el programador. La eliminación de un puntero a una sola int con delete [] podría resultar en un comportamiento indefinido. Su segundo main() ejemplo es seguro, incluso si no inmediatamente accidente.

    El compilador tiene que mantener un seguimiento de cuántos objetos deben ser eliminados de alguna manera. Puede hacer esto por la sobre-asignación suficiente para almacenar el tamaño de la matriz. Para obtener más detalles, consulte la C++ Super FAQ.

  2. 102

    Una pregunta que las respuestas dadas hasta el momento no parecen dirección: en el caso de las bibliotecas de tiempo de ejecución (no el sistema operativo, en realidad) puede mantener un seguimiento de la cantidad de cosas en la matriz, entonces ¿por qué necesitamos la delete[] sintaxis en todo? ¿Por qué no puede una sola delete formulario se utiliza para controlar todos los elimina?

    La respuesta a esto se remonta a C++raíces como un C-compatible con el lenguaje (que ya no se esfuerza por ser.) Stroustrup la filosofía era que el programador no debe tener que pagar por características que no están utilizando. Si no está utilizando matrices, a continuación, que no debería tener que llevar el costo de las matrices de objetos para cada asignados fragmento de memoria.

    Es decir, si el código simplemente no

    Foo* foo = new Foo;

    a continuación, el espacio de memoria asignado para foo no deberían incluir ninguna sobrecarga adicional que sería necesario para el apoyo de las matrices de Foo.

    Ya que sólo la matriz de asignaciones se establecen para llevar a la extra tamaño de la matriz de información, entonces usted necesidad de decirle a las bibliotecas de tiempo de ejecución para buscar esa información al eliminar los objetos. Es por eso que necesitamos utilizar

    delete[] bar;

    lugar de sólo

    delete bar;

    si la barra es un puntero a un array.

    Para la mayoría de nosotros (me incluyo), que el escrúpulo acerca de un extra de unos pocos bytes de memoria parece pintoresco estos días. Pero todavía hay algunas situaciones en las que el ahorro de unos pocos bytes (de lo que podría ser un muy alto número de bloques de memoria) puede ser importante.

    • «el escrúpulo acerca de un extra de unos pocos bytes de memoria parece pintoresco en estos días». Afortunadamente, ese tipo de gente desnuda matrices también se están empezando a mirar pintoresco, por lo que la puede usar un vector o boost::array, y olvidarse de eliminar[] para siempre 🙂
  3. 27

    Sí, el sistema operativo mantiene algunas cosas en el fondo.’ Por ejemplo, si ejecuta

    int* num = new int[5];

    el sistema operativo puede asignar 4 bytes adicionales, almacenar el tamaño de la asignación en los 4 primeros bytes de la memoria asignada y devolver un puntero de desplazamiento (es decir, asigna los espacios de memoria de 1000 a 1024 pero el puntero devuelto los puntos 1004, con lugares 1000-1003 almacenar el tamaño de la asignación). Entonces, al eliminar se llama, que se puede ver en 4 bytes antes de que el puntero del pasado para encontrar el tamaño de la asignación.

    Estoy seguro de que hay otras maneras de realizar el seguimiento del tamaño de una distribución, pero esa es una opción.

    • +1 – punto válido, en general, salvo que, usualmente, el tiempo de ejecución de lenguaje es responsable de almacenar este tipo de metadatos, no el sistema operativo.
    • ¿Qué sucede con el tamaño de la matriz o el tamaño de un objeto que tiene la matriz definida? No se muestran los 4 bytes adicionales cuando usted hace una sizeof sobre ese objeto?
    • No, sizeof muestra el tamaño de la matriz. Si el tiempo de ejecución elige implementar w/ el método que he descrito, que es estrictamente un detalle de implementación y desde la perspectiva del usuario, que debe ser enmascarada. La memoria antes de que el puntero no «pertenecen» al usuario, y no se cuentan en las estadísticas.
    • Lo que es más importante, sizeof no devolverá el verdadero tamaño de un asignada dinámicamente matriz en cualquier caso. Sólo puede volver tamaños conocido en tiempo de compilación.
    • Es posible el uso de este tipo de metadatos en un bucle for con precisión un bucle en la matriz? por ejemplo, for(int i = 0; i < *(arrayPointer - 1); i++){ }
    • Aunque puede funcionar en algunos entornos, es un detalle de implementación, y no debe ser invocado. Dudo que el real de la longitud de matriz serán almacenados de todos modos, lo más probable es que el tamaño del bloque de memoria asignado para apoyar la matriz.

  4. 13

    Esto es muy similar a este pregunta y tiene muchos de los detalles de su busca.

    Pero baste decir, no es el empleo del sistema operativo para el seguimiento de cualquier tipo de esta. De hecho, es el tiempo de ejecución de las bibliotecas o de su administrador de memoria que le dará seguimiento al tamaño de la matriz. Esto se hace generalmente mediante la asignación de memoria adicional en la delantera y almacenar el tamaño de la matriz en esa ubicación (la mayoría utiliza un nodo cabeza).

    Esto es visible en algunas implementaciones mediante la ejecución del siguiente código

    int* pArray = new int[5];
    int size = *(pArray-1);
    • en este trabajo? En windows & linux no hemos recibido este trabajo.
    • trate de size_t size = *(reinterpret_cast<size_t *>(pArray) - 1) lugar
  5. 9

    delete o delete[] probablemente ambos liberar la memoria asignada (memoria de punta), pero la gran diferencia es que delete en una matriz no llamar al destructor de cada elemento de la matriz.

    De todos modos, la mezcla de new/new[] y delete/delete[] es, probablemente, la UB.

    • Claro, corto y el más útil respuesta!
  6. 6

    No sabe que es una matriz, es por eso que usted tiene que proporcionar delete[] en lugar de los viejos delete.

  7. 5

    Yo tenía una pregunta similar a esta. En C, asignar memoria con malloc() (u otra función similar), y eliminar con free(). Sólo hay una malloc(), que simplemente asigna un cierto número de bytes. Sólo hay uno gratis(), que simplemente toma un puntero como parámetro.

    Así que ¿por qué es que en C se puede simplemente entregar el puntero a la libre, pero en C++, debemos decir si es una matriz o una sola variable?

    La respuesta, he aprendido, tiene que ver con la clase de destructores.

    Si asigna una instancia de una clase Miclase…

    classes = new MyClass[3];

    Y eliminarlo con borrar, sólo se puede obtener el destructor para la primera instancia de Miclase llamado. Si usted usa delete[], usted puede estar seguro de que el destructor será llamada por todas las instancias de la matriz.

    ESTA es la diferencia más importante. Si usted simplemente está trabajando con tipos estándar (por ejemplo, int) no realmente ver este problema. Además, usted debe recordar que el comportamiento para el uso de eliminar en new[] y delete[] de nuevo es indefinido, ya que puede que no funcione de la misma manera en cada compilador/sistema.

  8. 3

    El tiempo de ejecución que es el responsable de la asignación de la memoria, de la misma manera que se puede eliminar una matriz creada con malloc en el estándar de C uso libre. Creo que cada compilador implementa de una manera diferente. Una forma común es asignar una celda adicional para el tamaño de la matriz.

    Sin embargo, el tiempo de ejecución no es lo suficientemente inteligente como para detectar si es o no es una matriz o un puntero, tiene que informar de ello, y si usted se equivoca, que no eliminar correctamente (E. g., ptr en lugar de la matriz), o puedes acabar teniendo una relación de valor para el tamaño y causar daños significativos.

  9. 3

    UNO DE LOS enfoques para compiladores es la asignación de un poco más de memoria y almacenar el número de elementos en el elemento head.

    Ejemplo de cómo se podría hacer:
    Aquí

    int* i = new int[4];

    compilador asignará sizeof(int)*5 bytes.

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

    Almacenará 4 en primer sizeof(int) bytes

    *temp = 4;

    y establecer i

    i = temp + 1;

    Así i apunta a un array de 4 elementos, no 5.

    Y

    delete[] i;

    serán procesados siguiente manera

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

    Semánticamente, ambas versiones de operador delete de C++ puede «comer» cualquier puntero; sin embargo, si un puntero a un objeto único es dado a delete[], a continuación, UB, esto significa que puede suceder cualquier cosa, incluyendo un fallo del sistema o de nada en absoluto.

    C++ requiere que el programador elegir la versión correcta del operador delete dependiendo del tema de la cancelación: array o un objeto único.

    Si el compilador puede determinar automáticamente si un puntero pasa al operador delete fue un puntero a matriz, entonces no habría un solo operador delete de C++, que sería suficiente para ambos casos.

  11. 1

    De acuerdo en que el compilador no sabe si es un array o no. Es para el programador.

    El compilador, a veces, mantener un seguimiento de cuántos objetos deben ser eliminados por la sobre-asignación suficiente para almacenar el tamaño de la matriz, pero no siempre es necesario.

    Para una especificación completa cuando extra de almacenamiento asignado, por favor refiérase a la de C++ ABI (cómo compiladores son implementados): Itanium C++ ABI: Operador de Matriz de Cookies

    • Yo sólo deseo que cada compilador observó algunos documentado ABI para C++. +1 por el enlace, que he visitado antes. Gracias.
  12. 0

    Usted puede utilizar eliminar de una matriz, y usted no puede usar delete [] para una matriz no.

    • Creo que te refieres debe no, como el promedio de su compilador no va a detectar el abuso.
  13. 0

    «un comportamiento indefinido» significa simplemente que el lenguaje de especificación no hace gaurantees de lo que va a suceder. No nessacerally significa que algo malo va a suceder.

    Tan cuyo trabajo es recordar estas cosas? ¿El SO de mantener algún tipo de registro en el fondo? (Quiero decir, me doy cuenta de que he comenzado este post diciendo que lo que sucede no está definido, pero el hecho es que la ‘matanza’ escenario no sucede, por lo tanto, en el mundo práctico alguien es recordar.)

    Normalmente, hay dos capas de aquí. El subyacente administrador de memoria y la implementación en C++.

    En general, el administrador de memoria recordará (entre otras cosas) el tamaño del bloque de memoria que se ha asignado. Esto puede ser más grande que el bloque de la implementación en C++ de pedir. Normalmente, el administrador de memoria que almacenan los metadatos antes de que el bloque asignado de la memoria.

    La implementación en C++ por lo general sólo recuerde que el tamaño de la matriz si es necesario hacerlo por sus propios fines, normalmente porque el tipo tiene un no-trivial destructor.

    Así que para tipos con un trivial destructor de la aplicación de «borrar» y «delete []» normalmente es el mismo. La implementación en C++ simplemente pasa el puntero a la base del administrador de memoria. Algo así como

    free(p)

    Por otro lado para los tipos con un no-trivial destructor «eliminar» y «delete []» son propensos a ser diferentes. «eliminar» sería algo como (donde T es el tipo que señala el puntero)

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

    Mientras que «delete []» sería algo como.

    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);
  14. -1

    Hey ho bueno, depende de lo que dotaba de nuevos[] expresión cuando se trata de asignar la matriz de construir en tipos o clase /estructura y usted no proporciona su constructor y destructor que el operador va a tratar como a un tamaño «sizeof(objeto)*numObjects» en lugar de » objeto de la matriz, por tanto, en este caso el número de objetos asignados no serán almacenados en cualquier lugar, sin embargo, si se le asigna a la matriz de objetos y proporcionar constructor y destructor en su objeto de cambio de comportamiento, nueva expresión asignará 4 bytes más y almacenar el número de objetos en los primeros 4 bytes para que el destructor para cada uno de ellos puede ser llamado y por lo tanto nuevos[] la expresión devolverá el puntero se desplaza por 4 bytes hacia adelante, que cuando se devuelve la memoria delete[] la expresión se llama a una función de la plantilla de primera, iterar a través de la matriz de objetos y llama al destructor para cada uno de ellos. He creado este sencillo código de la bruja de sobrecargas de nuevo[] y delete[] expresiones y proporciona una función de plantilla para desasignar memoria y llama al destructor para cada objeto si es necesario:

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

    basta con definir un destructor dentro de una clase y ejecutar el código con ambas sintaxis

    delete pointer
    delete [] pointer

    de acuerdo a la salida de u puede encontrar las soluciones

    • el uso de delete [] cuando un nuevo tipo de matriz. por ejemplo, int* a = new int; int* b = new int[5]; delete a; delete[] b;
  16. -3

    La respuesta:

    int* pArray = new int[5];

    int tamaño = *(pArray-1);

    Publicado anteriormente no es la correcta y produce valor no válido.
    El «-1″recuentos de elementos
    En la versión de 64 bits de Windows OS la correcta tamaño de búfer reside en Ptr – 4 bytes de la dirección

Dejar respuesta

Please enter your comment!
Please enter your name here