Arhn - архитектура программирования

Почему неправильное выделение памяти для буфера может вызвать сбои только при компиляции в режиме выпуска, а не в режиме отладки?

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

Однако, чтобы быть более конкретным, я столкнулся с проблемой повреждения кучи, которая возникла только тогда, когда Visual Studio 2019 был установлен в режим выпуска, выплевывает exe-версию моей программы, а затем прошел свой первый сеанс отладки в этой форме. Получается (возможно, я ошибаюсь, но это последнее, что я изменил перед тем, как проблема полностью исчезла), что следующий код:

std::unique_ptr<std::vector<Stat>> getSelStudStats(HWND listboxcharnames) {
    std::unique_ptr<std::vector<Stat>> selStats = std::make_unique<std::vector<Stat>>();
    int pos = ListBox_GetCurSel(listboxcharnames);
    int len = ListBox_GetTextLen(listboxcharnames, pos);
    const wchar_t* buffer = new const wchar_t[++len];
    ListBox_GetText(listboxcharnames, pos, buffer);

    for (int i = 0; i < getSize(); i++) {
        Character character = getCharacterPtr(i);

        std::wstring name = character.getName();
        if (name.compare(buffer) == 0) {
            *selStats = character.getAllStats();
            return selStats;
        }
    }
    return selStats;
    delete[] buffer;
}

не присваивал правильный размер переменной buffer через len. При добавлении оператора приращения префикса к len теперь учитывался нулевой символ завершения, который будет появляться вместе с текстом окна списка; Следовательно, ошибка повреждения кучи перестала возникать.

Хотя я рад, что разобрался с проблемой, я не знаю, почему VS2019 не поднял эту проблему в режиме отладки. Пытаясь отладить проблему, я узнал, что оптимизация в режиме выпуска может изменить структуру и порядок выполнения кода.

Есть ли что-то в этом блоке кода, что могло бы создать ошибку, которая у меня была, но только в режиме выпуска/исполняемой форме?

EDITED: я удалил звездочки, которые изначально окружали ++len, пытаясь выделить изменение, на которое я ссылаюсь. Приносим извинения за путаницу, которую это, по понятным причинам, вызвало.


  • Почему вы используете std::unique_ptr<std::vector<Stat>> вместо std::vector<Stat>? Нет необходимости динамически выделять vector. И почему вы используете new wchar_t[] вместо std::vector<wchar_t> или даже std::wstring? Вы вызываете delete[] вручную, но вы теряете выделенный buffer, если его значение найдено в вашем списке Character. Вы также не проверяете возвращаемые значения ListBox_GetCurSel(), ListBox_GetTextLen() или ListBox_GetText() на наличие сбоев. 30.06.2020
  • @tadman Нет проблем с использованием ++len в этом контексте, это просто увеличение len, чтобы добавить место для нулевого терминатора, и это увеличение происходит до того, как значение будет передано в new[]. Это требуется ListBox_GetText(). Я бы больше беспокоился о том, что выделяется массив из const wchar_t, а не массив из wchar_t 30.06.2020
  • @RemyLebeau Двойное удаление ссылки, затем увеличение, затем двойное удаление ссылки снова? Или это просто выделение жирным шрифтом? len здесь целое число, так что это должно быть какая-то опечатка. 30.06.2020
  • @tadman Вы не можете разыменовать целое число или применить * без другого целого числа. Итак, я предполагаю, что OP на самом деле не использует ** в своем реальном коде, но добавил, что здесь, на SO, ожидается, что ++ будет отображаться жирным шрифтом, чего не будет внутри блока кода. 30.06.2020
  • @RemyLebeau Это мое предположение, просто прошу разъяснений. 30.06.2020
  • @viracocha Я только что понял, что вы также сливаете свой buffer, даже если вы не найдете его значение в своем списке Character, потому что ваше утверждение delete[] следует за вашим последним утверждением return selStats;. Еще одна причина избавиться от new[]. А лучше вообще избавиться от unique_ptr и просто return character.getAllStats();, когда совпадение найдено, и return vector<Stat>();, когда совпадений не найдено. 30.06.2020
  • Когда в вашем коде есть ошибка, вызывающая неопределенное поведение во время выполнения, например переполнение буфера, очень часто происходит сбой в режиме выпуска, а не в режиме отладки (или наоборот). Это несоответствие само по себе ничего не значит. 30.06.2020
  • Это нехорошо **++**len. Я не думаю, что это читабельно. Разбейте это на несколько строк и убедитесь, что это задокументировано. 30.06.2020
  • Ваш delete[] buffer; никогда не работает. Вы пропускаете это во всех случаях. 30.06.2020
  • Обозначение ++ было моей неудачной попыткой активировать выделение жирным шрифтом в текстовом редакторе при создании этого поста; Я очень сожалею об этом. Мое намерение состояло в том, чтобы выделить изменение, которое я внес, чтобы исправить ошибку повреждения кучи, с которой я имел дело. Во-вторых, хочу поблагодарить всех, кто оставил критику; Я немедленно рассмотрю каждое исправление. 30.06.2020
  • Я думаю, что у вас не было места для завершающего нулевого символа в исходном коде. 30.06.2020

Ответы:


1

документы объяснить поведение:

Когда вы запрашиваете блок памяти, диспетчер кучи отладки выделяет из базовой кучи немного больший блок памяти, чем запрошенный, и возвращает указатель на вашу часть этого блока. Например, предположим, что ваше приложение содержит вызов: malloc( 10 ). В сборке Release malloc вызывает процедуру выделения базовой кучи, запрашивая выделение 10 байтов. Однако в отладочной сборке malloc вызовет _malloc_dbg, которая затем вызовет процедуру выделения базовой кучи, запрашивающую выделение 10 байт плюс примерно 36 байт дополнительной памяти.

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

29.06.2020
Новые материалы

Коллекции публикаций по глубокому обучению
Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

Представляем: Pepita
Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

Советы по коду Laravel #2
1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

3 способа решить квадратное уравнение (3-й мой любимый) -
1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

Создание VR-миров с A-Frame
Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

Демистификация рекурсии
КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..