C builder освобождение памяти

Обновлено: 06.07.2024

Операторы 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:

Вы уверены, что пользователю действительно нужны все 2.5 млн строк? :eek:

Предполагал такой вопрос:
возможность загружать данные частями, не рассматривается, потому что суть проги - искать "кривые" записи в таблице, а в DataGridView удобно для пользователя просматривать их все, и пользователь должен свободно перемещаться по набору и принимать решения об удалении и некоторых диапазонов строк, т.е. програ перемещает пользователя на диапазон в котором найдена ошибка, и он принимает решение об удалении этого диапазона или его правке и т.д.

Можно конечно делать загрузку по частям но:
1) это неудобно,
2) все равно нужно просмотреть весь диапазон
3) исходная проблема освобождения ресурса все равно останется

Почему нельзя этого сделать на сервере?

DataGridView удобно для пользователя просматривать их все

Пользователю уже затруднительно прокручивать более 100 записей в DataGridView.

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

Почему бы не показывать именно этот диапазон?

Можно конечно делать загрузку по частям но:
1) это неудобно,

Неудобно штаны через голову надевать. Согласитесь, что вам просто лень.

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

3) исходная проблема освобождения ресурса все равно останется

Если я явно создаю объекты при помощи new или выделяю память при помощи alloc и объекты/память существуют всё время жизни програаммы - если ли смысл освобождать память ?

З.Ы. тока не надо говорить - освобождай на всякий случай.



Естественно, что при завершении программы в данном случае вся выделенная память будет освобождена. Учти только - в профессиональном подходе мелочей не бывает, правила хорошего тона обязывают делать всё как положено.
Хотя если ты пишешь только для себя, то кому какое дело. SmK
Можешь не удалять, но имей в виду, что деструкторы вызваны не будут, учитывай это.



Вопрос вдогонку, возможно дилетанский, но все-таки: нужно создать массив (видимо, с пом. операции new), число элементов которого заранее неизвестно и который одновременно был бы глобальным. Число элементов массива определяется в одной из функций, но если создать массив
в этой функции, то он не будет видимым всеми остальными?? Потом, разве память, занятая с пом. операции 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. Программист, разработчик видеоигр, независимый исследователь. Старый автор журнала «Хакер».

Читайте также: