bds 2006 C hidden memory manager conflicts (class new / delete[] vs. AnsiString)

I am using BDS 2006 Turbo C++ for a long time now and some of my bigger projects (CAD/CAM,3D gfx engines and Astronomic computations) occasionally throw an exception (for example once in 3-12 months of 24/7 heavy duty usage). After extensive debugging I found this:

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

this code is usually inside template where _s can be also class therefore delete[] this code should work properly, but the delete[] does not work properly for structs (classes looks OK). No exceptions is thrown, the memory is freed, but it somehow damages the memory manager allocation tables and after this any new allocation can be wrong (new can create overlapped allocations with already allocated space or even unallocated space hence the occasional exceptions)

I have found that if I add empty destructor to _s than suddenly seems everything OK

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

Well now comes the weird part. After I update this to my projects I have found that AnsiString class has also bad reallocations. For example:

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

In this code dat contains some useful data, then later is some txt string created by adding lines so the txt must be reallocated few times and sometimes the dat data is overwritten by txt (even if they are not overlapped, I thing the temp AnsiString needed to reallocate txt is overlapped with dat)

So my questions are:

  1. Am I doing something wrong in code1, code2 ?
  2. Is there any way to avoid AnsiString (re)allocation errors ? (but still using it)

    • After extensive debugging (after posting question 2) I have found that AnsiString do not cause problems. They only occur while using them. The real problem is probably in switching between OpenGL clients. I have Open/Save dialogs with preview for vector graphics. If I disable OpenGL usage for these VCL sub-windows than AnsiString memory management errors disappears completely. I am not shore what is the problem (incompatibility between MFC/VCL windows or more likely I made some mistake in switching contexts, will further investigate). Concern OpenGL windows are:
    • main VCL Form + OpenGL inside Canvas client area
    • child of main MFC Open/Save dialog + docked preview VCL Form + OpenGL inside Canvas client area

P.S.

  1. these errors depend on number of new/delete/delete[] usages not on the allocated sizes
  2. both code1 and code2 errors are repetitive (for example have a parser to load complex ini file and the error occurs on the same line if the ini is not changed)
  3. I detect these errors only on big projects (plain source code > 1MB) with combined usage of AnsiString and templates with internal dynamic allocations, but is possible that they are also in simpler projects but occurs so rarely that I miss it.
  4. Infected projects specs:
    • win32 noinstall standalone (using Win7sp1 x64 but on XPsp3 x32 behaves the same)
    • does not meter if use GDI or OpenGl/GLSL
    • does not meter if use device driver DLLs or not
    • no OCX,or nonstandard VCL component
    • no DirectX
    • 1 Byte aligned compilation/link
    • do not use RTL,packages or frameworks (standalone)

Sorry for bad English/grammar ... any help / conclusion / suggestion appreciated.


Solution 1:

After extensive debugging i finely isolated the problem. Memory management of bds2006 Turbo C++ became corrupt after you try to call any delete for already deleted pointer. for example:

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

After this is memory management not reliable. ('new' can allocate already allocated space)

Of course deletion of the same pointer twice is bug on programmers side, but i have found the real cause of all my problems which generates this problem (without any obvious bug in source code) see this code:

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

In function get() is called destructor for class a twice. Once for real a and once for its copy because I forget to create constructor

test::test(test &x);

[Edit1] further upgrades of code

OK I have refined the initialization code for both class and struct even templates to fix even more bug-cases. Add this code to any struct/class/template and if needed than add functionality

T()     {}
T(T& a) { *this=a; }
~T()    {}
T* operator = (const T *a) { *this=*a; return this; }
//T* operator = (const T &a) { ...copy... return this; }
  • T is the struct/class name
  • the last operator is needed only if T uses dynamic allocations inside it if no allocations are used you can leave it as is

This also resolves other compiler issues like this:

  • Too many initializers error for a simple array in bcc32

If anyone have similar problems hope this helps.

Also look at traceback a pointer in c++ code mmap if you need to debug your memory allocations...