Ошибка при освобождении памяти строк(LUA C API)

Страницы: 1
RSS
Ошибка при освобождении памяти строк(LUA C API), Unknown error. Possible unhandled exception.
 
Столкнулся с проблемой, что не освобождается памяти от строк, которые вернулись lua_tostring. Если не удалять, то появляется утечка памяти. Что не так и как правильно освободить память?



Код
const char *getStringField(lua_State* state, const char* field)
{

   lua_getfield(state, -1, field);
   // lua_pushstring(state, field);
   // lua_gettable(state, -2);

   const auto result = lua_isnil(state, -1) ? "" : lua_tostring(state, -1);
   lua_pop(state, 1);

   return result;
}

const auto firmid = getStringField(state, "firmid");

delete[] firmid; // Бабах ошибка Unknown error. Possible unhandled exception.

 
Цитата
null написал:
Что не так и как правильно освободить память?
Всё не так...
1. lua_tostring просто конвертирует элемент на стеке в строку, но эта строка остаётся в рамках памяти сборщика мусора, и после следующего вызова lua_pop попадает в мусор. Между вызовами lua_tostring и lua_pop необходимо выделить память и скопировать туда содержимое строки, если следовать логике вашего кода.
2. операция «delete[] firmid» вообще не имеет смысла по трём причинам: Во-первых потому что как уже выше сказал, lua_tostring возвращает указатель на внутреннюю память освобождаемую сборщиком мусора, во-вторых, потому что у вас в условии, при lua_isnil == true вообще вернётся указатель на константную строку, которую нельзя освобождать через delete, а в-третьих, потому что строку «char *» вообще нельзя освобождать через delete[], так как это не массив «char []», а такие строки выделенные внутри ф-ций, если их и нужно освобождать, то нужно просто через delete.
3. И что это за «const auto»? зачем вам «const»? И «auto» (новодельный С++11) вы тоже зря засунули, пишите честно типы и тогда не будет путаницы с типами и delete и пр...
 
Цитата
Suntor написал:
Цитата
null   написал:
Что не так и как правильно освободить память?
Всё не так...
1. lua_tostring просто конвертирует элемент на стеке в строку, но эта строка остаётся в рамках памяти сборщика мусора, и после следующего вызова lua_pop попадает в мусор. Между вызовами lua_tostring и lua_pop необходимо выделить память и скопировать туда содержимое строки, если следовать логике вашего кода.
2. операция «delete[] firmid» вообще не имеет смысла по трём причинам: Во-первых потому что как уже выше сказал, lua_tostring возвращает указатель на внутреннюю память освобождаемую сборщиком мусора, во-вторых, потому что у вас в условии, при lua_isnil == true вообще вернётся указатель на константную строку, которую нельзя освобождать через delete, а в-третьих, потому что строку «char *» вообще нельзя освобождать через delete[], так как это не массив «char []», а такие строки выделенные внутри ф-ций, если их и нужно освобождать, то нужно просто через delete.
3. И что это за «const auto»? зачем вам «const»? И «auto» (новодельный С++11) вы тоже зря засунули, пишите честно типы и тогда не будет путаницы с типами и delete и пр...
Так и думал, что там сборщик мусора. const позволяет компилятору оптимизировать лучше код. Чем вам C++ 11 не угодил? Синтаксический сахар, очень удобен, особенно когда тип переменной очень длинный, очень облегчает читаемость кода, компилятор сам подставит нужный тип. "а в-третьих, потому что строку «char *» вообще нельзя освобождать через  delete[], так как это не массив «char []», а такие строки выделенные  внутри ф-ций, если их и нужно освобождать, то нужно просто через delete." Это с каких пор надо освобождать через delete? delete[] если не ошибаюсь сам определяет размерность, так как по адресу указателя, или после хранится размерность, уже не помню все тонкости хранения данных в памяти. Просто delete c char * у вас оставит утечку памяти.
 
Цитата
null написал:
const позволяет компилятору оптимизировать лучше код.
Что вы там оптимизируете? можете объяснить? или вы где-то что-то прочитали и теперь лепите этот const куда ни попади...
Цитата
null написал:
Чем вам C++ 11 не угодил? Синтаксический сахар, очень удобен, особенно когда тип переменной очень длинный, очень облегчает читаемость кода, компилятор сам подставит нужный тип.
Вы сначала классическим способом овладейте написания программ на Си/Си++, а потом уже за сахар новый хватайтесь. Ошибки типизации это бич для новичка. А всякие auto только способствуют этому. И читаемость программы падает, когда вы не видите типа. Для того чтобы повысить читаемость, наоборот, эти типы даже в префиксах переменных нотируют.
Цитата
null написал:
Это с каких пор надо освобождать через delete?
С момента написания Страуструпом своей первой книги. Там, если не ошибаюсь, целая глава есть посвящённая вопросу, почему нельзя смешивать вызовы new/delete и new[]/delete[] между собой и чем это грозит. Вообще, масса тем в сети по этому поводу. Помимо того что это плохой стиль, это вообще «undefined behaviour» в общем случае.

Цитата
null написал:
так как по адресу указателя, или после хранится размерность
никогда, как раз для этого и введён delete[], чтобы этого не делать и оставить совместимость по структуре памяти с чистым Си

Цитата
null написал:
Просто delete c char * у вас оставит утечку памяти.
Я писал про то, что «char *» это просто указатель на какую-то память, которая пришла из недр lua_tostring. Вместо «char *» могло быть объявлено как «void *». Под «char *» часто (стандартная практика) имеется ввиду просто указатель на память и всё. А не то, что там кто-то выделял массив через new char[]. Вот про что речь шла. Какая-то неизвестная ф-ция, вернула вам память неизвестного происхождения, и вы её зафигачили delete[] увидев, что указатель на эту память объявлен типом «char *». Понимаете суть своей ошибки?... более того, внутри lua_tostring по определению не может быть никакого new char[], так как она написана на чистом Си, как и вся Lua! Исходники Lua посмотрите. Там эта память через malloc выделяется, а вы её в delete[] засунули. Так вообще делать нельзя, это уже системная ошибка освобождения памяти выделенной разными библиотеками. Поэтому она вам «Unknown error. Possible unhandled exception.» и выдала... осознаёте суть происходящего?
 
Цитата
Suntor написал:
Цитата
null   написал:
const позволяет компилятору оптимизировать лучше код.
Что вы там оптимизируете? можете объяснить? или вы где-то что-то прочитали и теперь лепите этот const куда ни попади...
Цитата
null   написал:
Чем вам C++ 11 не угодил? Синтаксический сахар, очень удобен, особенно когда тип переменной очень длинный, очень облегчает читаемость кода, компилятор сам подставит нужный тип.
Вы сначала классическим способом овладейте написания программ на Си/Си++, а потом уже за сахар новый хватайтесь. Ошибки типизации это бич для новичка. А всякие auto только способствуют этому. И читаемость программы падает, когда вы не видите типа. Для того чтобы повысить читаемость, наоборот, эти типы даже в префиксах переменных нотируют.
Цитата
null   написал:
Это с каких пор надо освобождать через delete?
С момента написания Страуструпом своей первой книги. Там, если не ошибаюсь, целая глава есть посвящённая вопросу, почему нельзя смешивать вызовы new/delete и new[]/delete[] между собой и чем это грозит. Вообще, масса тем в сети по этому поводу. Помимо того что это плохой стиль, это вообще «undefined behaviour» в общем случае.
Цитата
null   написал:
так как по адресу указателя, или после хранится размерность
никогда, как раз для этого и введён delete[], чтобы этого не делать и оставить совместимость по структуре памяти с чистым Си
Цитата
null   написал:
Просто delete c char * у вас оставит утечку памяти.
Я писал про то, что «char *» это просто указатель на какую-то память, которая пришла из недр lua_tostring. Вместо «char *» могло быть объявлено как «void *». Под «char *» часто (стандартная практика) имеется ввиду просто указатель на память и всё. А не то, что там кто-то выделял массив через new char[]. Вот про что речь шла. Какая-то неизвестная ф-ция, вернула вам память неизвестного происхождения, и вы её зафигачили delete[] увидев, что указатель на эту память объявлен типом «char *». Понимаете суть своей ошибки?... более того, внутри lua_tostring по определению не может быть никакого new char[], так как она написана на чистом Си, как и вся Lua! Исходники Lua посмотрите. Там эта память через malloc выделяется, а вы её в delete[] засунули. Так вообще делать нельзя, это уже системная ошибка освобождения памяти выделенной разными библиотеками. Поэтому она вам «Unknown error. Possible unhandled exception.» и выдала... осознаёте суть происходящего?
Это рекомендация стандарта С++ 2003 года, что надо помечать переменные, которые не меняются const, так же как const функции. Чем владеть я сам разберусь, мне так удобнее, читаемость не меняется. Разве где-то я смешиваю new/delete и new[]/delete[] между собой? Разве в большинстве компиляторов C++ delete реализован не через free и отличие лишь в том, что delete запускает дескруктор?
 
Скорее всего, ошибка идет из-за const auto firmid. Попробуйте поставить const char *firmid. Либо прямо указать  delete (char*)firmid.
Не совсем понял, зачем вам удалять по delete объект, который вы по new не создавали. Фактически, вы хотите удалить объект, созданный  через Lua C API? Там же могут быть какие-то еще перекрестные связи, которые вы не отслеживаете.
Кстати, зачем вам вообще удалять firmid? Вы поставили спецификацию const, она в любом случае для этой переменной, не создаваемой по new разместит при работе компилятора firmid либо в стеке, либо в статическом локальном секторе кода программы. Уберите delete[] firmid вообще.
 
Цитата
Andrei2016 написал:
Скорее всего, ошибка идет из-за const auto firmid. Попробуйте поставить const char *firmid. Либо прямо указать  delete (char*)firmid.
Не совсем понял, зачем вам удалять по delete объект, который вы по new не создавали. Фактически, вы хотите удалить объект, созданный  через Lua C API? Там же могут быть какие-то еще перекрестные связи, которые вы не отслеживаете.
Кстати, зачем вам вообще удалять firmid? Вы поставили спецификацию const, она в любом случае для этой переменной, не создаваемой по new разместит при работе компилятора firmid либо в стеке, либо в статическом локальном секторе кода программы. Уберите delete[] firmid вообще.
Спасибо. Уже разобрался с пароблемой, ничего подчищать не надо. Сборщик мусора Lua сам там подчищает. Насчет замены const auto на  const char * это ничего не даст, компилятор не настолько глупый, он понимает что тип там char * и сам заменит запись на const char *. Просто меньше буков в записи - тратится меньше времени на написание кода.
 
Цитата
null написал:
Это рекомендация стандарта С++ 2003 года, что надо помечать переменные, которые не меняются const, так же как const функции.
Создаётся впечатление, что вы просто не можете объяснить, зачем поставили это ключевое слово в свой код. В итоге, ссылаетесь на некие рекомендации. Даже я уже для себя увидел, одну из причин, зачем оно там нужно, но вот всё-таки хотелось бы автора услышать... ну да ладно.

Цитата
null написал:
Чем владеть я сам разберусь, мне так удобнее, читаемость не меняется.
Разумеется. Это лишь советы и мнения, как и на любом форуме. Тем не менее, своё мнение я высказал, и не меняю его. В вашем коде auto затрудняет читаемость и ничего не даёт взамен. Если вы считаете, что для вас наоборот, читаемость повышается, ну что ж, значит у вас такое восприятие кода...

Цитата
null написал:
Разве где-то я смешиваю new/delete и new[]/delete[] между собой?
Ну, если вы вызываете «delete[] p», и этот p объявлен типом «xxx *», то да, с синтаксической точки зрения. Потому что «delete[] p» можно делать только для p объявленным как «xxx []». Я могу даже вам привести пример коротенькой программы, где такое использование delete[] p приводит к зацикливанию и подвисанию кода. Если хотите. Тем не менее, в вашем случае, с примитивными типами (char, int и т.д.), это сработает. И вызов delete[] отработает для char * также как и для «char []», как впрочем и вызов просто delete, также освободит всю память без утечек для char []. Это только для базовых примитивных типов delete и delete[] равносильны, хотя с точки зрения синтаксиса и идеологии C++ это неправильно. Я даже затрудняюсь сказать, насколько это «specific behavior» и насколько противоречит каким версиям стандарта C++, это нужно глубоко ковырять все ревизии, но тем не менее. Это вообще проблема языка, что он не может возвращать массивы из ф-ций, для этой цели сделали даже std::array шаблон для массивов, который оборачивает их в структуру, и их можно возвращать из ф-ций. То-есть, тема более глубокая и сложная. Но суть я сказал в начале...

Цитата
null написал:
Разве в большинстве компиляторов C++ delete реализован не через free и отличие лишь в том, что delete запускает дескруктор?
Отличие там в дополнительных полях, за пределами массива, куда delete и delete[] лезут, чтобы узнать сколько и чего им удалять. Если вы им подсунете указатель из malloc'а, то это приведёт к ошибке выхода за пределы массива в общем случае. Да и вообще, сами malloc и free реализованы через системные ф-ции типа HeapAlloc, HeapFree и т.д. Как и new и delete. Это разные группы ф-ций работы с памятью, со своими структурами, и вызывать ф-ции одной группы для обработки памяти выделенной из другой просто бессмысленно.
Страницы: 1
Читают тему
Наверх