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

Вот мой код:

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-строку) с длиной строки
Третья строка считывает строку (считывает ее из файла)
В 4-й строке в конец добавляется NULL.
Пятая строка создает std :: string из c-строки.
6-я строка удаляет c-строку (ПРОИСХОДИТ КУЧА ИСПРАВЛЕНИЕ ЗДЕСЬ)
7-я строка возвращает строку, но никогда не достигает этой точки из-за ошибки.

В 6-й строке я получаю ошибку повреждения кучи: CRT обнаружила, что приложение записало в память после завершения буфера кучи.

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

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

Любые идеи?


person Brad    schedule 18.11.2010    source источник
comment
У вас есть delete[] в вашем коде, поэтому ваш код плохой. Используйте std::vector или что-то в этом роде, или даже просто прочитайте строку напрямую.   -  person GManNickG    schedule 19.11.2010
comment
@GMan: Даже не видел вашего комментария перед тем, как опубликовать: p Невероятно, как он упрощает код ...   -  person Matthieu M.    schedule 19.11.2010


Ответы (6)


Изменять:

char* rawString = new char[strLen];

to:

char* rawString = new char[strLen + 1];
person Paul R    schedule 18.11.2010
comment
Спасибо. Не могу поверить, что упустил из виду что-то подобное. Давно пользуюсь C ++ :). Спасибо всем, кто разместил помощь по этому поводу! - person Brad; 19.11.2010

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

person Diego Sevilla    schedule 18.11.2010

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

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

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

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

std::string retVal(rawString, strlen);
person wkl    schedule 18.11.2010

Поскольку в С ++ массивы индексируются 0, когда вы создаете массив размером strLen, а затем помещаете 0 в позицию strLen, вы записываете эту нулевую единицу после конца выделенного вами массива.

person tyree731    schedule 18.11.2010

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

Есть два способа избежать выделения с помощью 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;
}

Я считаю, что последняя версия НАМНОГО лучше. Больше нет никакого копирования :)

person Matthieu M.    schedule 19.11.2010
comment
Первый - это, собственно, нестандартный C ++. А второй дает только константный доступ. : S Вы хотите либо прочитать в vector, либо зарезервировать строку и прочитать в &retVal[0], что приводит к увеличению непрерывного буфера. Да, и у вас предельно просто. :) - person GManNickG; 19.11.2010
comment
@GMan: блин, я думал, что есть две версии data. Я уточнил, что первое было нестандартным, я уточняю, что это расширение компилятора (и хорошее ...) - person Matthieu M.; 20.11.2010

 rawString[strLen] = '\0';

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

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

person The Archetypal Paul    schedule 18.11.2010