Нестабильное поведение динамически размещаемого LPTSTR (TCHAR *) и оператора delete []? - PullRequest
0 голосов
/ 12 июля 2011

Я пытался создать класс, который работал бы с операторами String. Однако, без какой-либо убедительной причины, он иногда зависал во время операции удаления []. Я использовал библиотеку strsafe для выполнения всех внутренних строковых операций.

// QString

LPTSTR m_string;

void QString::operator +=(const QString &_in) //Concat m_string with _in.m_string
{
    size_t size = strlength(m_string) + strlength(_in.m_string) +1; //new size
    LPTSTR buffer = new TCHAR[size]; //alloc buffer
    ::StringCchCopy(buffer,strlength(m_string)+1,m_string); //copy current m_string to buffer

    ::StringCchCat(buffer, size, _in.m_string); //concat buffer with the input

    Replace(buffer); //replace this object with m_string

    delete[] buffer; //dealloc
}


void QString::Replace(LPCTSTR src) //replace m_string with src
{
    size_t size = strlength(src)+1; //new size
    Alloc(size);
    ::StringCchCopy(m_string,size,src); //copy src to m_string
}


void QString::Alloc(size_t size) //Dynamic allocation
{
    if(m_string != NULL) Free();
    m_string = new TCHAR[size+1];
}


void QString::Free()  //Free m_string
{
        delete[] m_string; //Sometime crashes here
        m_string = NULL;
}


QString ToStr(int _in) //Convert Integer to qstring
{
    int size = 1;
    int f = _in;

    while(f > 0)
    {
        f /=10;
        size++;
    }

    TCHAR* buf = new TCHAR[size];

    for(int i = 0; i < size; i++) buf[i] = (TCHAR)TEXT("");

    QString result(L"undef");

    if(::_itow_s(_in,buf,size,10) == 0) //No error code = ok 
    {
        result = buf;
    }
    delete[] buf;
    return result;
}

// Пример 1: не падает

int ::WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    QString a(L"");
    a += L"TEST";
    a += ToStr(1000);

::MessageBox(0,a.GetStr(),L"NOTHING",MB_OK);

        return 0;
}

// Пример 2. Печать странных символов плюс некоторые текущие символы (проблемы с Юникодом?)

 int ::WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    QString a(L"");
    a += L"TESTTESTESTEST";
    a += ToStr(1000);
    ::MessageBox(0,a.GetStr(),L"NOTHING",MB_OK);

    return 0;
}

// Пример 3. Сбои при загрузке

int ::WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
    QString a(L"");
    a += L"TESTTESTESTEST";
    a += ToStr(1000);
    a += L"TESTESTEST";
    a += ToStr(100);
    ::MessageBox(0,a.GetStr(),L"NOTHING",MB_OK);

    return 0;
}

Сбой при операторе удаления [] в Free (). С ошибкой либо

HEAP [StringTest.exe]: неверный адрес, указанный для RtlFreeHeap (003C0000, 003C46A8)

или

HEAP [StringTest.exe]: блок кучи в 003C4750 изменен в 003C475C после запрошенного размера 4

Ответы [ 4 ]

1 голос
/ 12 июля 2011

Ваш конструктор не инициализирует m_string в NULL (согласно вашему собственному комментарию выше). Это вызывает случайные сбои Free ().

Как отмечает Селби, в этом коде есть и другие ошибки и недостатки. Например, Replace всегда перераспределяет, даже когда вызывается из + =, который уже выделен. И вы добавляете 1 как в Replace, так и в Alloc, предлагая вам не ясно, какие функции принимают значения, которые включают терминатор, а какие нет.

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

Martyn

1 голос
/ 12 июля 2011

Другой ответ:

Мои экстрасенсорные способности говорят мне, что если QString не имеет конструктора копирования, то, вероятно, происходят плохие вещи, когда ToStr () возвращает «копию» QString в стеке.

0 голосов
/ 12 июля 2011

все ваши алгоритмы Allocation и free основаны на том факте, что m_string была инициализирована значением NULL.Вы сделали это в своем конструкторе (я не вижу его здесь)?

Кстати, у вас может быть конфликт с библиотечной функцией CRT с именем Alloc ().http://msdn.microsoft.com/en-us/library/dd492420%28VS.100%29.aspx

Ваш алгоритм вычисления длины целочисленной строки неверен.Вы начинаете с размера = 1, вы должны начать с размера = 0.Кроме того, вам нужно обработать особый случай, когда 0 == f: 0 определенно число и занимает 1 символ.у вас была ошибка off-by-1 (ошибка fencepost), где размер целого числа всегда был слишком большим 1.

QString ToStr(int _in) //Convert Integer to qstring
{
    int size = 0;
    int f = _in;

    if (0==f) {
        size=1;
    } else {
        while(f > 0) {
            f /=10;
            size++;
        }
    }

в Replace (), вы выделяете 2 дополнительных символа.

size_t size = strlength(src)+1;

должно быть

size_t size = strlength(src);

, и если вам нужен дополнительный символ для :: StringCchCopy (m_string, size, src);положить туда размер +1.или измените ваш Alloc так, чтобы он выделял именно то количество символов, которое вы запрашиваете (как следует из названия).

0 голосов
/ 12 июля 2011

Я вижу много ошибок.

Для начала, если вы передадите 0 в функцию ToStr, вам будет выделен только один символ памяти для хранения строки «0», которая составляет два байта («0» + нулевой символ). Кроме того, если вы передадите отрицательное целое число в эту функцию, произойдут другие плохие вещи. Вам лучше просто выделять больше, чем нужно, не пытаясь вычислить «точный» размер.

int size = sizeof(_in) * 4 + 1;

Ни один из ваших strcpy (даже с использованием функций StringCch) не дает мне чувствовать себя хорошо.

Я бы посоветовал выделить в 2 раза больше необходимого вам "размера", а затем тщательно изучить адрес памяти буфера и m_string с помощью отладки, если strcpy выходит за пределы байтов "размера-1" (не включая нулевой завершающий символ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...