2009-10-12 23 views
41
IP_ADAPTER_INFO *ptr=new IP_ADAPTER_INFO[100]; 

si gratis con¿Eliminar [] es igual a eliminar?

delete ptr; 

dará lugar a una pérdida de memoria, si no, ¿por qué?

Esto se genera por código de desmontaje VS2005

; delete ptr; 
0041351D mov   eax,dword ptr [ptr] 
00413520 mov   dword ptr [ebp-0ECh],eax 
00413526 mov   ecx,dword ptr [ebp-0ECh] 
0041352C push  ecx 
0041352D call  operator delete (4111DBh) 
00413532 add   esp,4 

; delete []ptr; 
00413535 mov   eax,dword ptr [ptr] 
00413538 mov   dword ptr [ebp-0E0h],eax 
0041353E mov   ecx,dword ptr [ebp-0E0h] 
00413544 push  ecx 
00413545 call  operator delete[] (4111E5h) 
0041354A add   esp,4 
+2

¿Qué dicen los documentos? – spender

+0

He leído que se llamará a destructor para el primer elemento en el conjunto, pero se liberará la memoria completa, lo mismo que puedo ver mientras se depura – Satbir

+0

No, solo el primer elemento se libera, otros no. –

Respuesta

146

Si esto lleva a una pérdida de memoria, limpia el disco duro, queda embarazada, hace que Nasty Demons persiguiéndote por tu apartamento, o deja que todo funcione bien sin problemas aparentes, no está definido. Puede ser de esta manera con un compilador, y cambiar con otro, cambiar con una nueva versión del compilador, con cada nueva compilación, con las fases lunares, su estado de ánimo o dependiendo de la cantidad de neutrinos que pasaron por el procesador en el último día soleado tarde. O tal vez no.

Todo eso, y una infinidad de otras posibilidades se ponen en un término: comportamiento indefinido:

Sólo se mantenga alejado de ella.

+0

Diría que el "Comportamiento indefinido" es "malo" y debería evitarse, como usted dice. Pero esto también significa que en algunos casos se perderá memoria y usted siempre debería código para que resuelva los peores escenarios. Esa es mi opinión de todos modos. –

+5

@Filip: suponiendo que su propio programa invoca un comportamiento indefinido? es que alguna forma evolucionada de programación defensiva? –

+20

+1 por ser correcto. Hablando de cómo 'borrar' y 'delete []' se comporta en una implementación específica simplemente da una idea equivocada. Es un comportamiento indefinido: no lo hagas. – jalf

3

Si una apunta a un array que se asignó a través de la nueva T [n], a continuación, debe eliminarlo a través de delete [] A.

¿Por qué?

La diferencia entre eliminar y eliminar [] es sencilla: la primera destruye un objeto escalar y la segunda destruye una matriz.

Más información here y here.

ACTUALIZA 1 (malo):

Si utiliza eliminar (y no delete []), mientras que la asignación de la nueva T [n], sólo el primer elemento es liberado mientras que otros no están destructorized, lo que conducirá a pérdida de memoria. ¿Por qué? Esta es la forma en que Bjarne Stroustrup y otros diseñaron el lenguaje. Y no es variable en el compilador. Si uno compilador desasigna la manera diferente, es sólo no seguir la norma. The C++ Programming Language, capítulos 6.2.6.2 y 19.4.5.

ACTUALIZACIÓN 2 (correcto):

estoy admitiendo mi error sobre el comportamiento de la utilización de eliminar operador sobre las asignaciones con nueva T [n]. Al leer el documento mencionado no encontré la descripción exacta así que supongo que en esta situación el comportamiento será indefinido y variará desde compilador compilador. AFAIK, el compilador de MSVC, por ejemplo, producirá un código diferente al de GCC. favor ignorar ACTUALIZA 1.

+1

Nadie (ni siquiera Bjarne, y mucho menos algunos Bjorn) especificaron que solo un objeto sería liberado. Está _undefinido_. (Y, como dije en otro lugar, he trabajado con al menos un compilador que los liberó a todos.) – sbi

+0

Supongo que te refieres a Bjarne, ¿no Bjorn? – gnud

+0

Solucionado. Lo siento, Bjarne :-) –

7

Por lo general, no tendrá fugas porque en el caso de los destructores POD son triviales y no hay necesidad de invocarlos para que delete simplemente desasigna la memoria ocupada por la matriz. La desasignación de memoria requiere solo un valor de puntero para que se devuelva al montón. El conjunto acciona un bloque contiguo de memoria y, por lo tanto, la desasignación puede ser exitosa, como si se tratara de una desasignación de un solo elemento.

Pero no confíe en esto ya que es un comportamiento indefinido. Tal vez funciona bien, tal vez sucede algo horrible, funciona en este compilador, no funciona en otro y muchas personas le agradecen que haya encontrado un error.

Ver this answer para más detalles.

+0

¿Por qué s downvoted Me pregunto ... – sharptooth

+2

Porque 'new T []' puede agregar un desplazamiento 'sizeof' independientemente de la POD-ness de T, en cuyo caso' delete [] 'compensará esto. 'delete' omitiría el bloque de asignación de encabezado por unos pocos bytes, interpretando un conteo de elementos (posiblemente no inicializados) como un encabezado, y causaría una corrupción de montón impredecible. Lo cual, por supuesto, aparece solo cuando se ve el jefe. – MSalters

+2

Claro, es por eso que la palabra * por lo general * está allí. Y la corrupción del montón no es una fuga. – sharptooth

-4

Para la matriz de POD será sin pérdida (con la mayoría de los compiladores). Por ejemplo, MSVC genera idéntico código para Suprimir y delete [] para gama de POD.

Personalmente, creo que C/C++ podría ser sin operador delete []. El compilador conoce el tamaño del objeto y el tamaño de la memoria asignada se conoce en el tiempo de ejecución, por lo tanto, es muy simple saber que es una matriz de puntero o no y disponer la memoria de una manera correcta.

EDITAR:

OK, chicos. ¿Puedes probar en tu compilador y decir si tiene fugas?

Trate de pensar como desarrollador de compiladores. Tenemos nuevo, nuevo [], eliminar, borrar []. Cada nuevo tiene su propio eliminar. Parece perfecto y completo. Veamos qué está pasando cuando llamas al delete []?

1. call vector destructor for an object 
2. actual free memory 

¿Cuál es destructor para POD? ¡Nada! Por lo tanto, al llamar al eliminar para una matriz de ¡POD no tendrá fugas! Incluso si rompe el estándar. Incluso si no es recomendado

Edit2:

Este es el código desmontaje generada por VS2008:

operator delete[]: 
78583BC3 mov   edi,edi 
78583BC5 push  ebp 
78583BC6 mov   ebp,esp 
78583BC8 pop   ebp 
78583BC9 jmp   operator delete (78583BA3h) 
+6

'delete []' definitivamente no es una forma de optimización. Se trata de la corrección. – sbi

+0

Sí, pero es superfluo. La gente quiere saber si se filtrará y respondí. ¿Por qué downvote? –

+0

Por cierto, me gusta la CORRECCIÓN. Pero no lo discutimos ... –

2

eliminar: llama a la apropiada destructor sólo para el elemento señaló a (si es necesario), libera entonces el fragmento de memoria

borrar [] : llama a los destructores apropiadas para cada elemento en su conjunto (si es necesario), luego libera el trozo de memoria

+2

Todo esto es verdadero ** solo ** si 'delete' y' delete [] 'se usan correctamente. Esta pregunta es específicamente sobre el caso donde 'delete' se usa donde' delete [] 'debería haberse usado. –

13

Sólo una ilustración de algunos comportamientos "indefinido" en ciertos sistemas operativos y compiladores. Espero que pueda ser útil para las personas depurar su código.

Prueba 1

#include <iostream> 
using namespace std; 
int main() 
{ 
    int *p = new int[5]; 
    cout << "pass" << endl; 
    delete p; 
    return 0; 
} 

Prueba 2

#include <iostream> 
using namespace std; 
int main() 
{ 
    int *p = new int; 
    cout << "pass" << endl; 
    delete[] p; 
    return 0; 
} 

Prueba 3

#include <iostream> 
using namespace std; 
struct C { 
    C() { cout << "construct" << endl; } 
    ~C() { cout << "destroy" << endl; } 
}; 

int main() 
{ 
    C *p = new C[5]; 
    cout << "pass" << endl; 
    delete p; 
    return 0; 
} 

Prueba 4

#include <iostream> 
using namespace std; 
struct C { 
    C() { cout << "construct" << endl; } 
    ~C() { cout << "destroy" << endl; } 
}; 

int main() 
{ 
    C *p = new C; 
    cout << "pass" << endl; 
    delete[] p; 
    return 0; 
} 
  • Windows 7 x86, msvc 2010. Compile con las opciones predeterminadas, es decir, el manejador de excepciones está habilitado.

Prueba 1

pass 

Prueba 2

pass 

Prueba 3

construct 
construct 
construct 
construct 
construct 
pass 
destroy 
# Then, pop up crash msg 

Prueba 4

construct 
pass 
destroy 
destroy 
destroy 
destroy 
destroy 
destroy 
destroy 
... # It never stop until CTRL+C 
  • Mac OS X 10.8.5, llvm-gcc 4.2 o gcc-4.8 generan la misma salida

Prueba 1

pass 

Prueba 2

pass 

de ensayo 3

construct 
construct 
construct 
construct 
construct 
pass 
destroy 
a.out(71111) malloc: *** error for object 0x7f99c94000e8: pointer being freed was not allocated 
*** set a breakpoint in malloc_error_break to debug 
zsh: abort  ./a.out 

Prueba 4

construct 
pass 
a.out(71035) malloc: *** error for object 0x7f83c14000d8: pointer being freed was not allocated 
*** set a breakpoint in malloc_error_break to debug 
zsh: abort  ./a.out 
  • Ubuntu 12.04, AMD64, gcc 4,7

Prueba 1

pass 

Prueba 2

pass 

Prueba 3

construct 
construct 
construct 
construct 
construct 
*** glibc detected *** ./a.out: munmap_chunk(): invalid pointer: 0x0000000001f10018 *** 
======= Backtrace: ========= 
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fe81d878b96] 
./a.out[0x400a5b] 
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fe81d81b76d] 
./a.out[0x4008d9] 
======= Memory map: ======== 
.... 
zsh: abort (core dumped) ./a.out 

Prueba 4

construct 
destroy 
destroy 
destroy 
destroy 
destroy 
destroy 
destroy 
destroy 
... 
destroy 
destroy 
*** glibc detected *** ./a.out: free(): invalid pointer: 0x00000000016f6008 *** 
======= Backtrace: ========= 
/lib/x86_64-linux-gnu/libc.so.6(+0x7eb96)[0x7fa9001fab96] 
./a.out[0x400a18] 
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xed)[0x7fa90019d76d] 
./a.out[0x4008d9] 
======= Memory map: ======== 
... 
zsh: abort (core dumped) ./a.out 
+0

Su prueba aún no demuestra si habrá pérdidas de memoria. – SOFe