Повреждение кучи при удалении строки - PullRequest
2 голосов
/ 19 ноября 2010

Вот мой код:

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 :: string из c-строки.
The6-я строка удаляет c-строку (HEAP CORRUPTION HAPPENS HERE)
7-я строка возвращает строку, но никогда не достигает этой точки из-за ошибки.

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

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

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

Есть идеи?

Ответы [ 6 ]

8 голосов
/ 19 ноября 2010

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

4 голосов
/ 19 ноября 2010

Изменение:

char* rawString = new char[strLen];

на:

char* rawString = new char[strLen + 1];
2 голосов
/ 19 ноября 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);
1 голос
/ 19 ноября 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;
}

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

1 голос
/ 19 ноября 2010

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

0 голосов
/ 19 ноября 2010
 rawString[strLen] = '\0';

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

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

...