Поврежден стек вокруг переменной, не уверен, в чем проблема

#c #variables #stack

#c #переменные #стек

Вопрос:

Проблема решена, спасибо всем за помощь

У меня здесь небольшая проблема, это не что-то, что разрушает мою программу, но меня просто беспокоит, что я не могу это исправить. У меня есть функция, считывающая некоторые данные из файла, в конце выполнения поврежден стек вокруг переменной longGarbage. Я немного осмотрелся и обнаружил, что возможной причиной является запись в недопустимую память. Я устранил некоторые утечки памяти, которые у меня были, и проблема все еще сохраняется. Что меня смущает, так это то, что это происходит, когда функция завершает выполнение, поэтому кажется, что это происходит, когда переменная выходит за пределы области видимости. Вот код…

 CHCF::CHCF(std::string fileName)
: PAKID("HVST84838672")
{
FILE * archive = fopen(fileName.c_str(), "rb");
std::string strGarbage = "";
unsigned int intGarbage = 0;
unsigned long longGarbage = 0;
unsigned char * data = 0;
char charGarbage = '0';

if (!archive)
{
    fclose (archive);
    return;
}

for (int i = 0; i < 12; i  )
{
    fread(amp;charGarbage, 1, 1, archive);
    strGarbage  = charGarbage;
}


if (strGarbage != PAKID)
{
    fclose(archive);
    throw "Incorrect archive format";
}
strGarbage = "";

fread(amp;_gameID, sizeof(_gameID),1,archive);
fread(amp;_fileCount, sizeof(_fileCount),1,archive);

for (int i = 0; i < _fileCount; i  )
{
    fread(amp;longGarbage, 8,1,archive); //file offset

    fread(amp;intGarbage, 4, 1, archive);//fileName

    for (int i = 0; i < intGarbage; i  )
    {
        fread(amp;charGarbage, 1, 1, archive);
        strGarbage  = charGarbage;
    }

    fread(amp;longGarbage, 8, 1, archive); //fileSize

    fread(amp;intGarbage, 4, 1, archive); //fileType

    data = new unsigned char[longGarbage];

    for (long i = 0; i < longGarbage; i  )
    {
        fread(amp;charGarbage, 1, 1, archive);
        data[i] = charGarbage;
    }

    switch ((FILETYPES)intGarbage)
    {
    case MAP:
        _maps.append(strGarbage, new CFileData(strGarbage, FILETYPES::MAP, data, longGarbage));
        break;

    default:
        break;
    }

    delete [] data;
    data = 0;
    strGarbage.clear();
    longGarbage = 0;

}
fclose(archive);
} //error happens here
  

Вот конструктор CFileData:

 CFileData::CFileData(std::string fileName, FILETYPES type, unsigned char *data, long fileSize)
{
_fileName = fileName;
_type = type;
_data = new unsigned char[fileSize];

for (int i = 0; i < fileSize; i  )
    _data[i] = data[i];
}
  

Комментарии:

1. <s>Что fread() </s>? Упс, я ошибаюсь read .

2. @muntoo: Скорее всего, стандартная библиотечная функция C fread .

3. sizeof(unsigned long) вероятно, это не 8 байт. Какой компилятор / платформу / архитектуру вы используете?

4. Кроме того, не добавляйте строки или другие литералы. Пожалуйста, используйте класс исключений.

5. VC 2008. Однако программа, записывающая файл, является VC # 2010. Класс BinaryWriter в .NET, похоже, автоматически записывает значение длиной до 8 байт.

Ответ №1:

  1. Могу ли я предложить std::vector вместо вызова new и delete вручную? Ваш код небезопасен для исключений — происходит утечка, если генерируется исключение.

  2. fread(amp;longGarbage, 8, 1, archive); //fileSize Вы уверены, что sizeof(long) равно 8? Я подозреваю, что это 4. Я полагаю, что в блоках Linux иногда это 8, но большинство других sizeof(long) равно 4, а sizeof(long long) равно 8.

  3. Как насчет каких-либо конструкторов для членов этого класса? Они также могут повредить стек.

Комментарии:

1. 1. Это может быть более подходящей альтернативой для управления утечками. 2. Да, я тестировал это несколько раз, и каждый раз поступающие данные верны. РЕДАКТИРОВАТЬ: похоже, вы правы в отношении длинного размера! Я внесу это изменение в программу и посмотрю, изменит ли это что-нибудь.

2. @MGZero: Да — RAII делает утечки делом прошлого. auto_ptr , shared_ptr и unique_ptr для одиночных указателей, vector для массивов.

3. Ха, ну, это, конечно, не устраивает использование 4 для sizeof. Мне нужно будет проверить программу, которая записывает этот файл, и посмотреть, в чем там дело.

Ответ №2:

Происходит то, что что-то записывается в память вокруг или поверх местоположения longGarbage, что вызывает повреждение.

Вы не говорите, какую среду разработки вы используете. Одним из способов диагностики этого было бы установить точку останова, которая срабатывает при изменении определенного местоположения в памяти. Выберите ячейку памяти, которая перекрывает область повреждения, и дождитесь ее неожиданного срабатывания.

Другим способом диагностики этого было бы изучить код, который изменяет память вокруг или поверх longGarbage. Конечно, это может быть почти что угодно, но вероятными кандидатами являются модификации ‘data’, модификации ‘intGarbage’ и модификации самого ‘longGarbage’.

Мы можем сузить область поиска еще больше, потому что мы можем (обычно) быть достаточно уверены, что сам оператор присваивания безопасен. Подобный код data = new... вряд ли может быть виновником, поэтому на самом деле нам нужно сосредоточиться на изменениях памяти, которые включают в себя использование адреса ‘data’, ‘intGarbage’ или ‘longGarbage’. В частности, изменения в памяти, которые изменяют больше байтов, чем должны.

Несколько других уже указали, что длина long, вероятно, не равна восьми байтам. Если вы передаете fread неправильную длину, полученные дополнительные байты должны куда-то деваться.

Комментарии:

1. Куда пойдут эти дополнительные байты? Причина, по которой я спрашиваю, заключается в том, что значение longGarbage является правильным при каждом чтении. В любом случае, я собираюсь работать с форматом файла, чтобы исправить длину.

2. Они перейдут в следующую более высокую ячейку памяти. Помните, что Windows на Intel имеет начальный код. Четыре наименее значимых байта извлеченного значения перейдут в longGarbage, следующие четыре извлеченных байта перейдут во все, что находится рядом с longGarbage — в данном случае intGarbage (в любом случае, в сборке Release отладочные сборки имеют дополнительные байты между ними, чтобы помочь обнаружить ошибку такого типа). Если исходное значение, записанное BinaryWrites, достаточно мало, чтобы поместиться в четыре байта, дополнительные четыре байта будут равны нулю.

3. Ах, теперь я понимаю. Итак, причина, по которой я получаю правильные значения, заключается в том, что записанное значение достаточно мало, чтобы поместиться в 4 байта.

4. Это правильно. Это и тот факт, что вам приходится удалять longGarbage до того, как вы удалите intGarbage. Если бы вы прочитали их наоборот, первое чтение заполнило бы intGarbage, а затем чтение longGarbage обнулило бы intGarbage.

5. Что ж, я изменил длинный массив на тип __int64, и, похоже, это устранило проблему.

Ответ №3:

Вы используете много магических чисел для размеров данных, поэтому я бы сначала проверил это. В частности, я сомневаюсь в этом sizeof(unsigned long)==8 и sizeof(unsigned in)==4 при всех возможных обстоятельствах. Обратитесь к документации вашего компилятора, но вам все равно следует быть осторожным, поскольку это, скорее всего, изменится с одного компилятора / платформы на другой.

Проверьте наличие этих битов:

 fread(amp;longGarbage, 8,1,archive); //file offset
  

Вы также можете захотеть использовать <iostream> библиотеку C вместо материалов C FILE* для чтения. Это позволило бы создать гораздо более короткую версию, потому что вам не нужно было бы закрывать файл 3 раза.

Комментарии:

1. Спасибо, изучаю это сейчас в программе, фактически записывающей файл.

Ответ №4:

Из других комментариев и предоставленной информации кажется, что проблема на стороне C , вам следует использовать либо __int64 для среды Windows, либо int64_t для кроссплатформенной.