Способы выделения памяти в программах

Ниже приведено краткое сравнение различных методов выделения памяти.

Несмотря на то, что функции GlobalAlloc, LocalAlloc и HeapAlloc в конечном счете выделяют память из одной кучи, каждый из них предоставляет несколько иной набор функций. Например, HeapAlloc может быть показано, чтобы создать исключение, если не удалось выделить память, возможность недоступна в LocalAlloc. LocalAlloc поддерживает выделение дескрипторов, которые позволяют перемещению базовой памяти путем перемещений без изменения значения дескриптора, возможности, недоступные в HeapAlloc.

Начиная с 32-разрядной Windows, GlobalAlloc и LocalAlloc реализуются как функции-оболочки, которые вызывают HeapAlloc с помощью дескриптора к куче по умолчанию процесса. Таким образом, GlobalAlloc и LocalAlloc имеют большую нагрузку, чем HeapAlloc.

Так как различные распределители кучи предоставляют отличительные функциональные возможности с помощью различных механизмов, необходимо освободить память с правильной функцией. Например, память, выделенная с помощью HeapAlloc , должна быть освобождена с помощью HeapFree , а не LocalFree или GlobalFree. Память, выделенная с помощью GlobalAlloc или LocalAlloc , должна запрашиваться, проверяться и освобождаться с помощью соответствующей глобальной или локальной функции.

Выделение и освобождение динамической памяти в Си

Функция VirtualAlloc позволяет указать дополнительные параметры выделения памяти. Однако при выделении страниц используется степень детализации страницы, поэтому использование VirtualAlloc может привести к более высокому использованию памяти.

Функция malloc имеет недостаток в зависимости от времени выполнения. Новый оператор имеет недостаток в том, что компилятор зависит от языка и зависит от языка.

Функция CoTaskMemAlloc имеет преимущество эффективной работы в C, C++или Visual Basic. Это также единственный способ совместного использования памяти в приложении на основе COM, так как MIDL использует CoTaskMemAlloc и CoTaskMemFree для маршалинга памяти.

Примеры

  • Резервирование и фиксация памяти
  • Пример AWE

Источник: learn.microsoft.com

Динамическая память

При создании массива с фиксированными размерами под него выделяется определенная память. Например, пусть у нас будет массив с пятью элементами:

double numbers[5] = ;

Для такого массива выделяется память 5 * 8 (размер типа double) = 40 байт. Таким образом, мы точно знаем, сколько в массиве элементов и сколько он занимает памяти. Однако это не всегда удобно. Иногда бывает необходимо, чтобы количество элементов и соответственно размер выделяемой памяти для массива определялись динамически в зависимости от некоторых условий.

Например, пользователь сам может вводить размер массива. И в этом случае для создания массива мы можем использовать динамическое выделение памяти.

Что такое динамическая память. Утечка памяти. Стек и куча. Статическая память. Обзорный урок #45

Для управления динамическим выделением памяти используется ряд функций, которые определены в заголовочном файле stdlib.h :

    malloc() . Имеет прототип

void *malloc(unsigned s);
void *calloc(unsigned n, unsigned m);
void *realloc(void *bl, unsigned ns);
void *free(void *bl);

malloc

Функция malloc() выделяет память длиной для определенного количества байт и возвращает указатель на начало выделенной памяти.

Читайте также:
Нужна ли программа для сканера штрих кодов

Через полученный указатель мы можем помещать данные в выделенную память. Рассмотрим простой пример:

#include #include // для подключения функции malloc int main(void) < int *ptr = malloc(sizeof(int)); // выделяем память для одного int *ptr = 24; // помещаем значение в выделенную память printf(«%d n», *ptr); free(ptr); >

Здесь с помощью функции malloc выделяется память для одного объекта int . Чтобы узнать, сколько байтов надо выделить, передаем в функцию malloc размер типа int на текущей и в результате получаем указатель ptr , который указывает на выделенную память

int *ptr = malloc(sizeof(int));

То есть поскольку int на большинстве архитектур занимает 4 байта, то в большинстве случаев будет выделяться память объемом в 4 байта. Стоит отметить, что мы также могли бы получить размер через разыменование указателя:

int *ptr = malloc(sizeof *ptr);

Для универсальности возвращаемого значения в качестве результата функция malloc() (как и calloc() и realloc() ) возвращает указатель типа void * . Но в нашем случае создается массив типа int, для управления которым используется указатель типа int * , поэтому выполняется неявное приведение результата функции malloc к типу int * .

Далее через этот указатель с помощью операции разыменования помещаем в выделенный участок памяти число 24:

*ptr = 24;

В дальнейшем, используя указатель, можно получить значение из выделенного участка памяти:

printf(«%d n», *ptr);

После завершения работы освобождаем память, передавая указатель в функцию free() :

free(ptr);

Стоит отметить, что теоретически мы можем столкнуться с тем, что функции malloc() не удастся выделить требуемую память, и тогда она возвратит NULL. Чтобы избежать подобной ситуации перед использованием указателя мы можем проверять его на значение NULL:

#include #include // для подключения функции malloc int main(void) < int *ptr = malloc(sizeof(int)); // выделяем память для одного int if(ptr != NULL) < *ptr = 24; // помещаем значение в выделенную память printf(«%d n», *ptr); >free(ptr); >

Немотря на освобождение памяти с помощью функции free() указатель сохраняет свой адрес, и теоретически мы можем обращаться к памяти по данному указателю. Однако полученные значения уже будут неопределенными и недетеминированными. Поэтому некоторые советуют после освобождения памяти также устанавливать для указателя значение NULL :

free(ptr); ptr = NULL;

Выделение памяти для массива

Подобным образом можно выделять память и под набор объектов. Например, выделим память для массива из 4-х чисел int:

#include #include int main(void) < int n = 4; int *ptr = malloc(n * sizeof(int)); // выделяем память для 4-х чисел int if(ptr) < // помещаем значения в выделенную память ptr[0] = 1; ptr[1] = 2; ptr[2] = 3; ptr[3] = 5; // получаем значения for(int i = 0; i < n; i++) < printf(«%d», ptr[i]); >> free(ptr); >

Выделение памяти для структуры

Аналогичным образом можно выделять память для одной или набора структур:

#include #include struct person < char* name; int age; >; int main(void) < // выделяем память для одной структуры person struct person *ptr = malloc(sizeof(struct person)); if(ptr) < // помещаем значения в выделенную память ptr->name = «Tom»; ptr->age = 38; // получаем значения printf(«%s : %d», ptr->name, ptr->age); // Tom : 38 > free(ptr); return 0; >

Читайте также:
Программа для эмуляции подстройки кнопок геймпада или джойстика к клавиатуре компьютера

calloc

Функция calloc() имеет прототип

void *calloc(unsigned n, unsigned m);

Она выделяет память для n элементов по m байт каждый и возвращает указатель на начало выделенной памяти. В случае неудачного выполнения возвращает NULL

В отличие от функции malloc() она инициализирует все выделенные байты памяти нулями. Например, выделим память для одного объекта int :

#include #include int main(void) < // выделяем память для одного объекта int int *ptr = calloc(1, sizeof(int)); if(ptr) < // получаем значение по умолчанию — 0 printf(«Initial value: %d», *ptr); // Initial value: 0 // устанавливаем новое значение *ptr = 15; // получаем новое значение printf(«New value: %d», *ptr); // New value: 15 >free(ptr); return 0; >
Initial value: 0 New value: 15

Подобным образом можно выделить память и для других объектов. Например, выделим память для массива из 4-х объектов int :

#include #include int main(void) < // выделяем память для 4-х объектов int int n = 4; int *ptr = calloc(n, sizeof(int)); if(ptr) < // устанавливаем значения ptr[0] = 1; ptr[1] = 2; ptr[2] = 3; ptr[3] = 5; // получаем значения for(int i = 0; i < n; i++) < printf(«%d», ptr[i]); >> free(ptr); >

realloc

Функция realloc() позволяет изменить размер памяти, ранее выделенной с помощью функций malloc() b calloc() . Имеет прототип

void *realloc(void *bl, unsigned ns);

Первый параметр представляет указатель на ранее выделенный блок памяти. А второй параметр представляет новый размер блока памяти в байтах.

Если указатель bl имеет значение NULL , то есть память не выделялась, то действие функции аналогично действию malloc

Рассмотрим небольшой пример:

#include #include int main(void) < // выделяем память для 1-го объекта int int size = sizeof(int); int *ptr = malloc(size); if(ptr) < // отображаем адрес и размер памяти printf(«Addresss: %p t Size: %dn», (void*)ptr, size); >// расширяем память до размера 4-х объектов int size = 4 * sizeof(int); int *ptr_new = realloc(ptr, size); // если выделение памяти прошло успещно if(ptr_new) < printf(«Reallocationn»); // заново отображаем адрес и размер памяти printf(«Addresss: %p t Size: %dn», (void*)ptr_new, size); free(ptr_new); // освобождаем новый указатель >else < free(ptr); // освобождаем старый указатель >>

Здесь сначала выделяем память для одного объекта int с помощью функции malloc.

int size = sizeof(int); int *ptr = malloc(size);

Если память успешно выделена, то выводим на консоль адрес и размер выделенного блока памяти. Затем с помощью функции realloc расширяем память до 4 объектов int

size = 4 * sizeof(int); int *ptr_new = realloc(ptr, size);

Если увеличение памяти прошло успешно, то заново выводим данные на консоль и освобождаем память по новому указателю. Если увеличение памяти прошло не удачно, то освобождаем память но старому указателю.

Консольный вывод в моем случае

Addresss: 0000018B078A82F0 Allocated: 4 Reallocation Addresss: 0000018B078A82F0 Allocated: 16

Стоит отметить, что нам необязательно создавать новый указатель, мы можем присвоить значение старому указателю:

#include #include int main(void) < int size = sizeof(int); int *ptr = malloc(size); if(ptr) < printf(«Addresss: %p t Allocated: %dn», (void*)ptr, size); >size = 4 * sizeof(int); ptr = realloc(ptr, size); // используем старый указатель if(ptr) < printf(«Reallocationn»); printf(«Addresss: %p t Allocated: %dn», (void*)ptr, size); >free(ptr); >

Читайте также:
Как перекинуть программы с айфона на Айпад

Источник: metanit.com

Способы выделения памяти в программах: абсолютное, статическое, динамическое и автоматическое распределения. Механизм стека и кучи при реализации процессоров языка программирования.

Статическое выделение памяти – выделение памяти под данные внутри сегмента данных программы. Такие данные существуют на протяжении всей жизни программы до ее завершения.

Автоматическое распределение – выделение памяти под данные в стеке. Такие данные существуют на протяжении работы текущей подпрограммы (функции или процедуры), затем уничтожаются.

Динамическое выделение – выделение памяти под данные самой программой, когда это необходимо. Время жизни таких данных зависит от программы.

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

Куча. Для реализации динамических структур данных используют т.н. кучу (heap). Это объем памяти, в котором можно выделить участок для произвольного элемента данных. Для кучи есть 2 операции: выделения памяти ALLOCATE и освобождения FREE. Эти функции не делают никаких действий с собственно памятью.

При выделении программист получает адрес, а при освобождении доступный объем кучи становится больше. Одного адреса для этих операций недостаточно, требуется еще и размер элемента данных. Если указатель типизированный, размер будет получен автматически. В случае нетипизированного указателя размер должен быть передан в функцию.

Для реализации кучи ЯВУ снабжаются диспетчерами памяти, которые выделяют и освобождают память, имеют сведения о ее фрагментации, знают наибольший кусок свободной памяти и ее общее количество и т.п. При ненадобности память должна своевременно освобождаться. При использовании динамической памяти возможноа ситуация образования «мусора» – кусков памяти, на которые утеряны ссылки, но которые не были своевремменно освобождены, поэтому менеджер считает их занятыми. Для оптимизации известна процедура “уборка мусора” – перестройка динамических структур с освобождением памяти из-под тех данных, на которые отсутствуют ссылки.

Среды ссылок: глобальная, локальная и нелокальная их реализация в исполняющих системах яву. Понятие программного модуля: подпрограммы, функции, сопрограммы.

Модульное программирование подразумевает разбиение программы на подпрограммы по специфике обрабатываемых данных. Для этой цели в ЯВУ используются функции и процедуры. При вызове подпрограммы в стеке сохраняется текущее значение счетчика команд (ближняя модель вызова) и значение сегмента кода (дальняя модель вызова). При использовании дальней модели вызова подпрограмма необязательно должна находиться в том же сегменте, что и вызывающая программа. По окончании выполнения кода подпрограммы эти данные восстанавливаются, управление передается на следующий оператор после оператора вызова процедуры.

До вызова подпрограммы в стек помещаются параметры – аргументы подпрограммы. Если передан не сам аргумент, а его адрес, то подпрограмма может изменить аргумент, в противном случае нет, т.к. по завершению работы подпрограммы стек очищается от аргументов. Отличие функций от процедур в том, что функции могут возвращать значения в вызывающую программу и их можно присваивать, например, какой-либо переменной. В действительности функция при возврате значений просто модифицирует регистры процессора, а ЯВУ по соглашению использует данные из этих регистров.

Источник: studfile.net

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru