Conflictos del administrador de memoria oculta de bds 2006 C (clase nueva/eliminar[] frente a AnsiString)

Resuelto Spektre asked hace 11 años • 1 respuestas

Estoy usando BDS 2006 Turbo C++ desde hace mucho tiempo y algunos de mis proyectos más importantes ( CAD/CAM, motores 3D gfx y cálculos astronómicos) ocasionalmente arrojan una excepción (por ejemplo, una vez cada 3 a 12 meses de uso intensivo las 24 horas, los 7 días de la semana). ). Después de una extensa depuración encontré esto:

//code1:
struct _s { int i; }    // any struct
_s *s=new _s[1024];     // dynamic allocation
delete[] s;             // free up memory

este código generalmente está dentro de la plantilla donde _stambién puede ser una clase, por lo tanto, delete[]este código debería funcionar correctamente, pero delete[]no funciona correctamente para las estructuras (las clases se ven bien). No se lanzan excepciones, la memoria se libera, pero de alguna manera daña las tablas de asignación del administrador de memoria y después de esto, cualquier nueva asignación puede ser incorrecta (las nuevas pueden crear asignaciones superpuestas con espacio ya asignado o incluso con espacio no asignado, de ahí las excepciones ocasionales)

Descubrí que si agrego un destructor vacío, _sde repente todo parece estar bien.

struct _s { int i; ~_s(){}; }

Bueno, ahora viene la parte rara. Después de actualizar esto en mis proyectos, descubrí que AnsiStringla clase también tiene malas reasignaciones. Por ejemplo:

//code2:
int i;
_s *dat=new _s[1024];
AnsiString txt="";
// setting of dat
for (i=0;i<1024;i++) txt+="bla bla bla\r\n";
// usage of dat
delete[] dat;

Este código datcontiene algunos datos útiles, luego se txtcrea una cadena agregando líneas, por lo que se txtdeben reasignar varias veces y, a veces, los datdatos se sobrescriben txt(incluso si no están superpuestos, creo que la temperatura AnsiStringnecesaria para reasignar txtse superpone con dat)

Entonces mis preguntas son:

  1. ¿Estoy haciendo algo mal en código1, código2?
  2. ¿ Hay alguna forma de evitar AnsiStringerrores de (re)asignación? (pero sigo usándolo)

    • Después de una depuración exhaustiva (después de publicar la pregunta 2), descubrí que AnsiStringno causan problemas. Sólo ocurren mientras los usamos. El verdadero problema probablemente esté en cambiar entre clientes OpenGL . Tengo cuadros de diálogo Abrir/Guardar con vista previa para gráficos vectoriales. Si desactivo el uso de OpenGL para estas subventanas de VCLAnsiString , los errores de administración de memoria desaparecen por completo. No sé cuál es el problema (incompatibilidad entre ventanas MFC/VCL o, más probablemente, cometí algún error al cambiar de contexto, lo investigaré más a fondo). Las ventanas OpenGL que preocupan son:
    • Formulario VCL principal + OpenGL dentro del Canvasárea del cliente
    • hijo del cuadro de diálogo principal Abrir/Guardar de MFC + formulario VCL de vista previa acoplado + OpenGL dentro del Canvasárea del cliente

PD

  1. Estos errores dependen del número de new/delete/delete[]usos, no de los tamaños asignados.
  2. Tanto los errores de código1 como de código2 son repetitivos (por ejemplo, tenga un analizador para cargar un archivo ini complejo y el error ocurre en la misma línea si no se cambia el ini)
  3. Detecto estos errores solo en proyectos grandes (código fuente simple > 1 MB) con uso combinado de AnsiStringplantillas con asignaciones dinámicas internas, pero es posible que también estén en proyectos más simples, pero ocurren tan raramente que los pierdo.
  4. Especificaciones de proyectos infectados:
    • win32 noinstall independiente (usando Win7sp1 x64 pero en XPsp3 x32 se comporta igual)
    • no mide si usa GDI u OpenGl/GLSL
    • no mide si usa el controlador de dispositivo DLL o no
    • sin OCX o componente VCL no estándar
    • sin DirectX
    • Compilación/enlace alineado de 1 byte
    • no utilice RTL , paquetes o marcos (independientes)

Perdón por el mal inglés/gramática... se agradece cualquier ayuda/conclusión/sugerencia.

Spektre avatar Jul 17 '13 18:07 Spektre
Aceptado

Después de una depuración exhaustiva, aislé finamente el problema. La administración de memoria de bds2006 Turbo C++ se corrompió después de intentar ejecutar cualquier eliminación para un puntero ya eliminado. Por ejemplo:

BYTE *dat=new BYTE[10],*tmp=dat;
delete[] dat;
delete[] tmp;

Después de esto, la gestión de la memoria no es confiable. ('nuevo' puede asignar espacio ya asignado)

Por supuesto, eliminar el mismo puntero dos veces es un error por parte de los programadores, pero he encontrado la causa real de todos mis problemas que genera este problema (sin ningún error obvio en el código fuente). Vea este código:

//---------------------------------------------------------------------------
class test
    {
public:
    int siz;
    BYTE *dat;
    test()
        {
        siz=10;
        dat=new BYTE[siz];
        }
    ~test()
        {
        delete[] dat;   // <- add breakpoint here
        siz=0;
        dat=NULL;
        }
    test& operator = (const test& x)
        {
        int i;
        for (i=0;i<siz;i++) if (i<x.siz) dat[i]=x.dat[i];
        for (   ;i<siz;i++) dat[i]=0;
        return *this;
        }
    };
//---------------------------------------------------------------------------
test get()
    {
    test a;
    return a;   // here call a.~test();
    }           // here second call a.~test(); 
//---------------------------------------------------------------------------
void main()
    {
    get();
    }
//---------------------------------------------------------------------------

En función get()se llama destructor para la clase a dos veces. Una vez de verdad y otra para su copia porque me olvidé de crear el constructor.

test::test(test &x);

[Editar1] actualizaciones adicionales del código

OK, he refinado el código de inicialización para las plantillas de clase y estructura, incluso para corregir aún más casos de errores. Agregue este código a cualquier estructura/clase/plantilla y, si es necesario, agregue funcionalidad

T()     {}
T(const T& a)   { *this=a; }
~T()    {}
T* operator = (const T *a) { *this=*a; return this; }
//T* operator = (const T &a) { ...copy... return this; }
  • Tes el nombre de la estructura/clase
  • el último operador es necesario solo si Tusa asignaciones dinámicas dentro de él, si no se usan asignaciones, puede dejarlo como está

Esto también resuelve otros problemas del compilador como este:

  • Error de demasiados inicializadores para una matriz simple en bcc32

Si alguien tiene problemas similares, espero que esto ayude.

También mire el rastreo de un puntero en el código C++mmap si necesita depurar sus asignaciones de memoria...

Spektre avatar Aug 02 '2013 12:08 Spektre