У меня есть функция с прототипом, как показано ниже:
void function(std::string str);
Эта функция вызывается в моей основной функции в другой программе, которая загружает и использует эту DLL.
function("some string value here");
При возврате из этой функции я получаю ошибку повреждения кучи:
Windows запустила точку останова в program.exe.
Это может быть связано с повреждением кучи, что указывает на ошибку в
program.exe или любая из загруженных библиотек.Это также может быть связано с тем, что пользователь нажимает клавишу F12, пока
Программа.exe имеет фокус.Окно вывода может иметь больше диагностической информации.
Играя с моим кодом, я заметил несколько странных наблюдений:
1. Когда длина передаваемой строки меньше 11 символов, я не получаю ошибок, как только я добавляю больше символов, появляется ошибка.
2. При изменении типа параметра с std::string
в std::string&
ошибка исчезает. Идея передачи ссылки пришла от Вот.
3. Я закомментировал тело функции. Операции там не имеют ничего общего с произведенным исключением.
4. Изменение типа параметра с std::string
в char*
также решает проблему.
Что может быть причиной этой ошибки? Как мне это решить?
Скорее всего, вы видите сбои из-за того, что в Windows DLL имеют свою собственную кучу.
Когда вы скомпилировали свою функцию, компилятор сгенерировал некоторый код для std::string
Деструктор, чтобы очистить свои аргументы. Этот код освобождает выделенную память в куче DLL. Тем не менее, приложение EXE также генерирует свой собственный код для std::string
конструктор, который размещает код в куче программы. Когда вы выделяете одну кучу, а свободную — другую, возникает неопределенное поведение, и вы вылетаете.
Что касается того, почему маленькие строки не вызывают ошибку — многие std::string
Реализации встраивают небольшие строки в саму структуру, чтобы избежать накладных расходов. Когда ваша строка достаточно мала, чтобы уместиться, не требуется выделять память, и, таким образом, она, кажется, работает … до тех пор, пока вы используете одну и ту же версию STL для EXE и DLL, и порог для встраивания никогда не меняется.
Чтобы избежать этой проблемы, не передавайте объекты по значению в DLL (если они не POD объекты) и не освобождайте объект в другой DLL или EXE, чем он был создан. Избегайте передачи объектов библиотеки STL или C ++, так как их реализация может отличаться в разных версиях компилятора C ++. Передавать объекты POD или C примитивные типы, такие как const char *
вместо.
При экспорте функций DLL лучше всего, если они принимают только целочисленные типы данных, т. Е. Int или указатели (не уверенны относительно float и double).
Когда вам нужно передать строку, передайте ее как const char *
, когда вам нужна функция DLL для возврата строки, передайте в DLL char *
указатель на предварительно выделенный буфер, в который DLL записывает строку.
Никогда не используйте память, выделенную DLL за пределами собственных функций DLL, и никогда не передавайте структуры значений, которые имеют свой собственный конструктор / деструктор.
Возможно, вы связали со статической версией среды выполнения C, никогда не стоит создавать DLL, связанную со статической версией среды исполнения C. Это может вызвать много проблем, например, в вашей программе ваш EXE выделяет память из частной кучи статической среды выполнения C, с которой он связан, затем в вашей DLL вы хотите удалить эту кучу и создать новую кучу (так как вы хотите добавить некоторые данные для входной строки, и он должен увеличить свой буфер), так что это приведет к ошибке. Простейший подход к этому состоит в том, чтобы связать все части вашей программы (EXE и DLL) с DLL-версией среды выполнения C, чтобы они все использовали одну и ту же кучу из MSVCRTXX.dll.