2010-11-18 5 views
2

Вот мой код:Повреждение кучи при удалении строки

std::string readString() 
{ 
    int strLen = Read<int>(); 
    char* rawString = new char[strLen]; 
    Read(rawString, strLen); 
    rawString[strLen] = '\0'; 
    std::string retVal(rawString); 
    delete [] rawString; 
    return retVal; 
} 

Первая строка считывает длину строки.
Вторая строка создает новый массив символов (c-string) с длиной строки
Третья строка читает строку (ее чтение из файла)
4-я строка добавляет NULL в конец.
5-я строка создает строку std :: из c-строки.
6-я строка удаляет c-строку (HEAP CORRUPTION HAPPENS HERE)
7-я строка возвращает строку, но она никогда не достигает этой точки из-за ошибки.

На 6-й строке я получаю сообщение об ошибке кучи: ЭЛТ обнаружил, что приложение записано в память после окончания кучного буфера.

Мой вопрос может быть очевиден, но почему я получаю кучу коррупции? Когда я создаю std :: string, он должен скопировать строку, и я должен быть в безопасности, чтобы удалить c-строку.

В настоящее время я подозреваю, что std :: string пытается получить доступ к c-строке после ее удаления.

Любые идеи?

+0

У вас есть 'delete []' в вашем коде, поэтому ваш код плохой. Используйте 'std :: vector' или что-то, или просто просто прочитайте строку напрямую. – GManNickG

+0

@GMan: Даже не видел ваш комментарий, прежде чем я опубликовал: p Невероятно, как это упрощает код тоже ... –

ответ

4

Изменение:

char* rawString = new char[strLen]; 

к:

char* rawString = new char[strLen + 1]; 
+0

Спасибо. Не могу поверить, что я забыл что-то подобное. Я уже некоторое время использую C++ :). Спасибо всем, кто тоже помогал в этом! – Brad

2

int strLen = Read<int>()вероятно только возвращает длину строки без нуль-терминатором, а при попытке записать \0 байт в строку, вы столкнетесь с проблемами переполнения буфера.

Вы должны проверить, что strLen есть, и, скорее всего, вам либо придется выделить так:

char *rawString = new char[strlen+1]; 

Или использовать перегруженный конструктор std::string(const char *, size_t n) так:

std::string retVal(rawString, strlen); 
8

вы обращаетесь мимо зарезервированных байтов для вашей строки. Вы зарезервировали strLen символов, но положили \0 на символ strLen. Считая, что C массивы от 0, символ strLen находится в позиции strLen + 1, поэтому вы помещаете значение вне зарезервированного пространства для строки. Вы должны зарезервировать strLen + 1 во второй строке своего main, чтобы ваш код работал.

1

Поскольку массивы 0 индексированные в C++, когда вы создаете массив размером strLen, а затем поместить 0 в положение strLen, вы записывая нулевое значение после окончания выделенного массива.

0
rawString[strLen] = '\0'; 

Записывает NUL с конца выделенного пространства.

Если strLen равно 10, вы выделяете пространство для 10 символов, считываете 10 символов и записываете этот NUL в положение 11.Ooops

1

Многие советы до сих пор, но ни одна из них не касается проблемы безопасности исключений: как вы избавитесь от этой потенциальной утечки памяти?

Существует два способа избежать выделения с помощью new (и, таким образом, для утечки памяти). Первый чрезвычайно просто и использует расширение компилятора известный как VLA для переменной длины массива:

std::string readString() 
{ 
    int strLen = Read<int>(); 
    char rawString[strLen+1]; // VLA: the length is determined at runtime 
          // but the array is nonetheless on the stack 
    Read(rawString, strLen); 
    rawString[strLen] = '\0'; 

    std::string retVal(rawString); 
    return retVal; 
} 

другой соответствует стандарту: string имеет внутренний буфер, который вы можете получить доступ (благодаря GMan, data не является подходящим методом доступа)

std::string readString() 
{ 
    int strLen = Read<int>(); 

    std::string retVal(strLen, '\0'); // no need to allocate extra space 

    Read(&retVal[0], strLen);  // &retVal[0] gives access to the buffer 

    return retVal; 
} 

Я действительно верю, что последняя версия намного лучше. Больше не нужно копировать :)

+0

Первое нестандартное C++, на самом деле. А второй дает только доступ к const. : S Вы хотите либо прочитать в «вектор», либо зарезервировать строку и прочитать в '& retVal [0]', которая выплевывает непрерывный буфер. О, и у вас есть «чрезвычайно просто». :) – GManNickG

+0

@GMan: дерьмо, я думал, что есть две версии 'data'. Я сделал точное, что первое было нестандартным, хотя, я точно знаю, что это расширение компилятора (и приятное ...) –

Смежные вопросы