C builder освобождение памяти
Операторы new и delete[] . Выделение памяти для структурных переменных, объектов классов, массивов. Инициализация выделенной памяти. Пример перераспределения ранее выделенной памяти
Содержание
- 1. Пример динамического выделения памяти для структурной переменной
- 2. Пример динамического выделения памяти для объекта класса
- 3. Как выделить память для массива оператором new ? Общая форма
- 4. Как освободить память выделенную для массива оператором delete[] ? Общая форма
- 5. Пример динамического выделения и освобождения памяти для массива указателей на базовый тип
- 6. Пример выделения памяти для массива структурных переменных и его использование
- 7. Пример выделения и освобождения памяти для массива объектов. Инициализация массива объектов
- 8. Как перераспределить память, если нужно динамически увеличить (уменьшить) размер массива? Перераспределение памяти для структур, инициализация структур. Пример
Поиск на других ресурсах:
1. Пример динамического выделения памяти для структурной переменной
Выделение и освобождение памяти для структурной переменной. Пусть дана структура Date , которая имеет следующее описание:
Тогда, чтобы выделить и использовать память для переменной типа struct Date нужно написать приблизительно следующий код:
2. Пример динамического выделения памяти для объекта класса
В примере динамично выделяется память для указателя на объект класса CDayWeek . Пример реализован для приложения типа Console Application .
3. Как выделить память для массива оператором new ? Общая форма
Оператор new может быть использован для выделения памяти для массива. Общая форма оператора new в случае выделения памяти для массива:
4. Как освободить память выделенную для массива оператором delete[] ? Общая форма
Для освобождения памяти, выделенной под массив, оператор delete имеет следующую форму использования:
где ptrArray – имя массива, для которого выделяется память.
5. Пример динамического выделения и освобождения памяти для массива указателей на базовый тип
В примере выделяется память для массива указателей на тип float . Затем элементы массива заполняются произвольными значениями. После этого, выделенная память освобождается оператором delete[] .
6. Пример выделения памяти для массива структурных переменных и его использование
В примере демонстрируется выделение и освобождение памяти для массива из 3-х структур типа TStudent . Также продемонстрированы способы доступа к полям заданного элемента в массиве структур.
7. Пример выделения и освобождения памяти для массива объектов. Инициализация массива объектов
В примере демонстрируется выделение памяти для массива объектов оператором new . После использования массива, происходит уничтожение выделенной памяти оператором delete .
В вышеприведенном коде, внутренняя переменная в массиве объектов инициализируется значением 1, так как такое значение задано в конструкторе без параметров CMonth()
Этот конструктор выступает инициализатором массива. Однако, в классе реализован еще один конструктор – конструктор с 1 параметром или параметризованный конструктор. Согласно синтаксису C++, массив объектов не может быть инициализирован параметризованным конструктором. Поэтому, в классе CMonth обязательно должен быть реализован конструктор без параметров.
Если конструктор без параметров CMonth() убрать из кода класса, то невозможно будет выделить память для массива объектов. Можно будет выделять память для одиночных объектов, но не для массива.
Вывод: если нужно выделить память для массива объектов некоторого класса, то этот класс обязательно должен иметь реализацию конструктора без параметров.
8. Как перераспределить память, если нужно динамически увеличить (уменьшить) размер массива? Перераспределение памяти для структур, инициализация структур. Пример
В примере демонстрируется процесс перераспределения памяти для типа структуры DayWeek . Выделение и перераспределение памяти динамически есть основным преимуществом этого способа по сравнению со статическим выделением памяти. Память в программе можно выделять когда нужно и сколько нужно.
В структуре DayWeek реализован конструктор без параметров (по умолчанию), который инициализирует массив структур значением по умолчанию ( d =1).
В функции main() сначала выделяется память для массива из 5 структур. Затем эта память перераспределяется для массива из 7 структур. Для этого используется дополнительный указатель p2 .
При перераспределении сначала память выделяется для p2 (7 элементов). Затем копируются данные из p в p2 . После этого освобождается память, которая была выделена для указателя p (5 элементов).
На следующем шаге значение p устанавливается равным значению p2 . Таким образом, оба указателя указывают на одну и ту же область памяти.
1GB утрированно. Есть ли способ вернуть программе эту память после того как работа с таблицей закончилась.
Для теста на всех используемых объектах (в том числе и для DataGridView) вызываю Dispose. А затем
но сборщик только освобождает лишь часть памяти, ничтожную по сравнению с той, что занимает таблица. в проге пользователь может открывать до неск. десятков подобных таблиц
Есть ли способ вернуть занятую таблицей память для повторного использования для загрузки других таблиц. Есть ли способ вернуть занятую таблицей память для повторного использования для загрузки других таблиц.
Вы уверены, что пользователю действительно нужны все 2.5 млн строк? :eek:
Предполагал такой вопрос:
возможность загружать данные частями, не рассматривается, потому что суть проги - искать "кривые" записи в таблице, а в DataGridView удобно для пользователя просматривать их все, и пользователь должен свободно перемещаться по набору и принимать решения об удалении и некоторых диапазонов строк, т.е. програ перемещает пользователя на диапазон в котором найдена ошибка, и он принимает решение об удалении этого диапазона или его правке и т.д.
Можно конечно делать загрузку по частям но:
1) это неудобно,
2) все равно нужно просмотреть весь диапазон
3) исходная проблема освобождения ресурса все равно останется
Почему нельзя этого сделать на сервере?
DataGridView удобно для пользователя просматривать их всеПользователю уже затруднительно прокручивать более 100 записей в DataGridView.
и пользователь должен свободно перемещаться по набору и принимать решения об удалении и некоторых диапазонов строк, т.е. програ перемещает пользователя на диапазон в котором найдена ошибка, и он принимает решение об удалении этого диапазона или его правке и т.д.Почему бы не показывать именно этот диапазон?
Можно конечно делать загрузку по частям но:1) это неудобно,
Неудобно штаны через голову надевать. Согласитесь, что вам просто лень.
По мере подкачки одних данных, уже доступные данные могут анализироваться. Таким образом получаем ускорение работы.
3) исходная проблема освобождения ресурса все равно останетсяЕсли я явно создаю объекты при помощи new или выделяю память при помощи alloc и объекты/память существуют всё время жизни програаммы - если ли смысл освобождать память ?
З.Ы. тока не надо говорить - освобождай на всякий случай.
Хотя если ты пишешь только для себя, то кому какое дело. SmK
Можешь не удалять, но имей в виду, что деструкторы вызваны не будут, учитывай это.
в этой функции, то он не будет видимым всеми остальными?? Потом, разве память, занятая с пом. операции new в какой-то функции не может быть занята кем-то другим после завершения этой функции, но когда еще она не была освобождена с помощью delete?
Как быть?
Или сделай SomeArray членом класса формы, к примеру. Тогда этот указатель будет доступен в методах этой формы.
Потом, разве память, занятая с пом. операции new в какой-то функции не может быть занята кем-то другим после завершения этой функции, но когда еще она не была освобождена с помощью delete? SmKМожешь не удалять, но имей в виду, что деструкторы вызваны не будут, учитывай это.
А разве при закрытии программы не вызываются деструкторы для всех использованных обьектов? А разве при закрытии программы не вызываются деструкторы для всех использованных обьектов? Если ты сам их невызовешь (хотя бы косвенным образом), то - нет. А разве при закрытии программы не вызываются деструкторы для всех использованных обьектов?
Вызовутся все деструкторы только тех объектов, которые созданы на стеке
З.Ы. тока не надо говорить - освобождай на всякий случай.Не на всякий случай.
Ресурсы, используемые в программе, совершенно различны по своей природе. Это могут быть и файлы, сокеты, COM-интерфейсы и т.д.
И многие ресурсы просто могут быть не освобождены сразу после "убиения" твоей программы, в отличие от ситуации, когда ты у себя в программе все ресурсы освобождаешь корректно. После аварийного завершения твоего приложения многие системные программы могут продолжать ждать от него ответной реакции(скажем, сокеты, для которых таймаут весьма большое число).
Пятнадцать лет назад эпический труд Криса Касперски «Фундаментальные основы хакерства» был настольной книгой каждого начинающего исследователя в области компьютерной безопасности. Однако время идет, и знания, опубликованные Крисом, теряют актуальность. Редакторы «Хакера» попытались обновить этот объемный труд и перенести его из времен Windows 2000 и Visual Studio 6.0 во времена Windows 10 и Visual Studio 2019.
Ссылки на другие статьи из этого цикла ищи на странице автора.
Идентификация указателя this
Указатель this — это настоящий золотой ключик или, если угодно, спасательный круг, позволяющий не утонуть в бурном океане ООП. Именно благодаря this можно определять принадлежность вызываемой функции к тому или иному классу. Поскольку все невиртуальные функции объекта вызываются непосредственно — по фактическому адресу, объект как бы расщепляется на составляющие его функции еще на стадии компиляции. Не будь указателей this , восстановить иерархию функций было бы принципиально невозможно!
Таким образом, правильная идентификация this очень важна. Единственная проблема: как отличить его от указателей на массивы и структуры? Ведь экземпляр класса идентифицируется по указателю this (если на выделенную память указывает this , это экземпляр класса), однако сам this по определению — это указатель, ссылающийся на экземпляр класса. Замкнутый круг! К счастью, есть одна лазейка. Код, манипулирующий указателем this , весьма специфичен, что и позволяет отличить this от всех остальных указателей.
Вообще‑то у каждого компилятора свой почерк, который настоятельно рекомендуется изучить, дизассемблируя собственные программы на C++, но существуют и универсальные рекомендации, применимые к большинству реализаций. Поскольку this — это неявный аргумент каждой функции — члена класса, то логично отложить разговор о его идентификации до раздела «Идентификация аргументов функций». Здесь же мы обсудим, как реализуют передачу указателя this самые популярные компиляторы.
Здесь мы, конечно, говорим об архитектуре x64. На 32-битной платформе параметры, выровненные до 32-битного размера, передаются через стек. С другой стороны, на 64-битной платформе дела обстоят интереснее: первые четыре целочисленных аргумента передаются в регистрах RCX , RDX , R8 , R9 . Если целочисленных аргументов больше, остальные размещаются в стеке. Аргументы, имеющие значения с плавающей запятой, передаются в регистрах XMM0 , XMM1 , XMM2 , XMM3 . При этом 16-битные аргументы передаются по ссылке. Замечу, все это касается соглашения о вызовах в операционных системах Microsoft (Microsoft ABI), в Unix-подобных системах дела обстоят по‑другому. Но не будем распылять на них свое внимание.
Оба протестированных мною компилятора, Visual C++ 2019 и C++Builder 10.3, независимо от соглашения вызова функции ( __cdecl , __clrcall , __stdcall , __fastcall , __thiscall ) передают указатель this в регистре RCX , что соответствует его природе: this — целочисленный аргумент.
Идентификация операторов new и delete
Операторы new и delete транслируются компилятором в вызовы библиотечных функций, которые могут быть распознаны точно так же, как и обычные библиотечные функции. Автоматически распознавать библиотечные функции умеет, в частности, IDA Pro, снимая эту заботу с плеч исследователя. Однако IDA Pro есть не у всех и далеко не всегда в нужный момент находится под рукой, да к тому же не все библиотечные функции она знает, а из тех, что знает, не всегда узнает new и delete . Словом, причин идентифицировать их вручную предостаточно.
Реализация new и delete может быть любой, но Windows-компиляторы в большинстве своем редко реализуют функции работы с кучей самостоятельно. Зачем это? Намного проще обратиться к услугам операционной системы. Однако наивно ожидать вместо new появление вызова HeapAlloc , а вместо delete — HeapFree . Нет, компилятор не так прост! Разве он может отказать себе в удовольствии «вырезания матрешек»? Оператор new транслируется в функцию new , вызывающую для выделения памяти malloc , malloc же, в свою очередь, обращается к HeapAlloc (или ее подобию — в зависимости от реализации библиотеки работы с памятью) — своеобразной «обертке» одноименной Win32 API-процедуры. Картина с освобождением памяти аналогична.
Углубляться в дебри вложенных вызовов слишком утомительно. Нельзя ли new и delete идентифицировать как‑нибудь иначе, с меньшими трудозатратами и без лишней головной боли? Разумеется, можно! Давай вспомним все, что мы знаем о new:
-
new принимает единственный аргумент — количество байтов выделяемой памяти, причем этот аргумент в подавляющем большинстве случаев вычисляется еще на стадии компиляции, то есть является константой;
если объект не содержит ни данных, ни виртуальных функций, его размер равен единице (минимальный блок памяти, выделяемый только для того, чтобы было на что указывать указателю this ); отсюда будет очень много вызовов типа
где XXX и есть адрес new ! Вообще же, типичный размер объектов составляет менее сотни байтов. ищи часто вызываемую функцию с аргументом‑константой меньше ста байтов;
функция new — одна из самых популярных библиотечных функций, ищи функцию с «толпой» перекрестных ссылок;
самое характерное: new возвращает указатель this , а this очень легко идентифицировать даже при беглом просмотре кода (обычно он возвращается в регистре RCX );
возвращенный new результат всегда проверяется на равенство нулю (операторами типа test RCX , RCX ), и, если он действительно равен нулю, конструктор (если он есть) не вызывается.
«Родимых пятен» у new более чем достаточно для быстрой и надежной идентификации, тратить время на анализ кода этой функции совершенно ни к чему! Единственное, о чем следует помнить: new используется не только для создания новых экземпляров объектов, но и для выделения памяти под массивы (структуры) и изредка — под одиночные переменные (типа int *x = new int , что вообще маразм, но некоторые так делают). К счастью, отличить два этих способа очень просто — ни у массивов, ни у структур, ни у одиночных переменных нет указателя this !
Сложнее идентифицировать delete . Каких‑либо характерных признаков эта функция не имеет. Да, она принимает единственный аргумент — указатель на освобождаемый регион памяти, причем в подавляющем большинстве случаев это указатель this . Но помимо нее, this принимают десятки, если не сотни других функций! Раньше в эпоху 32-битных камней у исследователя была удобная зацепка за то, что delete в большинстве случаев принимал указатель this через стек, а остальные функции — через регистр. В настоящее же время, как мы уже неоднократно убеждались, любые функции принимают параметры через регистры:
В данном случае IDA без замешательств распознала delete .
К тому же delete ничего не возвращает, но мало ли функций поступают точно так же? Единственная зацепка — вызов delete следует за вызовом деструктора (если он есть), но, поскольку конструктор как раз и идентифицируется как функция, предшествующая delete , образуется замкнутый круг!
Ничего не остается, кроме как анализировать содержимое функции: delete рано или поздно вызывает HeapFree (хотя тут возможны и варианты: так, Borland/Embarcadero содержит библиотеки, работающие с кучей на низком уровне и освобождающие память вызовом VirtualFree ). К счастью, IDA Pro в большинстве случаев опознает delete и самостоятельно напрягаться не приходится.
А что произойдет, если IDA не распознает delete ? Код будет выглядеть примерно так:
Неглубокий анализ показывает: в первой строчке в регистр RCX , очевидно для передачи в качестве параметра, помещается блок памяти. Похоже, это указатель на сущность. А после вызова XXX выполняется сравнение этого блока памяти с нулем и, если блок не обнулен, происходит переход по адресу. Таким несложным образом мы можем легко идентифицировать delete , даже если IDA его не определяет.
Продолжение доступно только участникам
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Крис Касперски
Известный российский хакер. Легенда ][, ex-редактор ВЗЛОМа. Также известен под псевдонимами мыщъх, nezumi (яп. 鼠, мышь), n2k, elraton, souriz, tikus, muss, farah, jardon, KPNC.
Юрий Язев
Широко известен под псевдонимом yurembo. Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер».
Читайте также: