Конфликты скрытого менеджера памяти bds 2006 C (класс new / delete[] и AnsiString)

Я давно использую BDS 2006 Turbo C++, и некоторые из моих более крупных проектов (CAD / CAM, 3D- движки gfx и астрономические вычисления) иногда выдают исключение (например, раз в 3-12 месяцев при работе в тяжелых условиях 24/7).). После обширной отладки я нашел это:

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

этот код обычно находится внутри шаблона, где _s может быть также классом, поэтому delete[] этот код должен работать правильно, но delete[] не работает должным образом для структур (классы выглядят нормально). Никаких исключений не выдается, память освобождается, но это каким-то образом повреждает таблицы распределения диспетчера памяти, и после этого любое новое распределение может быть неправильным (new может создавать перекрывающиеся распределения с уже выделенным пространством или даже нераспределенным пространством, следовательно, случайные исключения)

Я обнаружил, что если я добавлю пустой деструктор в _s чем вдруг кажется все ок

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

Что ж, теперь начинается странная часть. После того, как я обновлю это для своих проектов, я обнаружил, что AnsiString класс также имеет плохие перераспределения. Например:

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

В этом коде dat содержит некоторые полезные данные, а затем некоторые txt строка, созданная путем добавления строк, чтобы txt должны быть перераспределены несколько раз, а иногда dat данные перезаписываются txt (даже если они не перекрываются, я думаю, что температура AnsiString необходимо перераспределить txt перекрывается с dat)

Итак, мои вопросы:

  1. Я делаю что-то не так в code1, code2?
  2. Есть ли способ избежать AnsiString (пере) ошибки распределения? (но все еще использую это)

    • После обширной отладки (после публикации вопроса 2) я обнаружил, что AnsiString не вызывает проблем. Они появляются только при их использовании. Настоящая проблема, вероятно, заключается в переключении между клиентами OpenGL. У меня есть диалоги открытия / сохранения с предварительным просмотром для векторной графики. Если я отключу использование OpenGL для этих подокнов VCL, чем AnsiString Ошибки управления памятью исчезают полностью. Я не знаю, в чем проблема (несовместимость между окнами MFC/VCL или, скорее всего, я допустил какую-то ошибку при переключении контекста, рассмотрим подробнее). Концерны OpenGL имеют следующие окна:
    • основная форма VCL + OpenGL внутри Canvas клиентская зона
    • дочерний элемент основного диалогового окна открытия / сохранения MFC + закрепленный предварительный просмотр формы VCL + OpenGL внутри Canvas клиентская зона

PS

  1. эти ошибки зависят от количества new/delete/delete[] использования не по выделенным размерам
  2. и ошибки code1, и code2 являются повторяющимися (например, имеется синтаксический анализатор для загрузки сложного INI-файла, и ошибка возникает в одной строке, если INI-файл не изменяется)
  3. Я обнаруживаю эти ошибки только в больших проектах (простой исходный код> 1 МБ) с комбинированным использованием AnsiString и шаблоны с внутренним динамическим распределением, но возможно, что они также находятся в более простых проектах, но встречаются настолько редко, что я пропускаю это.
  4. Спецификации зараженных проектов:
    • win32 noinstall standalone (при использовании Win7sp1 x64, но на XPsp3 x32 ведет себя так же)
    • не измеряет, если использовать GDI или OpenGl/GLSL
    • не измеряет, если использовать DLL драйвера устройства или нет
    • нет OCX или нестандартного компонента VCL
    • нет DirectX
    • Компиляция / ссылка на 1 байт
    • не используйте RTL, пакеты или фреймворки (автономно)

Извините за плохой английский / грамматику... любая помощь / заключение / предложение приветствуется.

1 ответ

Решение

После обширной отладки я точно изолировал проблему. Управление памятью в bds2006 Turbo C++ стало поврежденным после попытки вызвать любое удаление для уже удаленного указателя. например:

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

После этого управление памятью ненадежно. ("новый" может выделить уже выделенное пространство)

Конечно, удаление одного и того же указателя дважды является ошибкой на стороне программистов, но я нашел реальную причину всех моих проблем, которая порождает эту проблему (без какой-либо явной ошибки в исходном коде), см. Этот код:

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

В функции get() называется деструктором для класса дважды. Один раз для реального и один раз для его копии, потому что я забыл создать конструктор

test::test(test &x);

[Edit1] дальнейшие обновления кода

Хорошо, я усовершенствовал код инициализации для шаблонов классов и даже структур, чтобы исправить еще больше ошибок. Добавьте этот код в любую структуру / класс / шаблон и, если необходимо, добавьте функциональность.

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 это имя структуры / класса
  • последний оператор нужен только если T использует динамическое распределение внутри него, если оно не используется, вы можете оставить его как есть

Это также решает другие проблемы компилятора как это:

Если у кого-то есть подобные проблемы, надеюсь, это поможет.

Также посмотрите на traceback указатель в коде C++mmap если вам нужно отладить распределение памяти...

Другие вопросы по тегам