В языке программирования С можно реализовать три основных способа выделения памяти:
- Статическое выделение памяти— это когда память для какой-либо программы распределяется единожды при ее запуске и это выделенное количество памяти не меняется, пока программа не закончит работу.
- Автоматическое выделение памяти — это когда память распределяется для отдельных параметров программы в момент их «входа» в работу, после окончания работы этих отдельных параметров память высвобождается; такая память выделяется столько раз, сколько необходимо.
- Динамическое выделение памяти — это когда заранее не известен объем памяти, который может понадобиться для выполнения программы, поэтому мы «просим» операционную систему зарезервировать какую-то часть памяти для нашего программного продукта, чтобы потом использовать ее нужное количество.
Динамическое выделение памяти в С
Об автоматическом и статическом распределении памяти мы обязательно поговорим в наших следующих статьях, а пока давайте разберемся, что такое динамическое выделение памяти в Си и как оно функционирует.
Статическое и автоматическое выделение памяти имеет 2 общих свойства:
- количество выделяемой памяти для какого-либо компонента программы известно заранее;
- автоматический процесс выделения и освобождения памяти.
В основной массе компонентов программы этих свойств достаточно, чтобы управлять памятью. Однако проблемы начинаются, когда в программе нужно реализовать пользовательский ввод каких-либо сведений. В этом случае никогда точно не известно, какое количество памяти может понадобиться, пока пользователь не введет какое-либо значение.
Все, что остается в таких случаях, — это попытаться угадать максимально необходимый объем памяти.
На самом деле, это «так себе» решение, потому что оно влечет за собой как минимум 2 потенциальные проблемы:
- Память может «теряться» просто так. Например, мы предполагаем, что имя пользователя максимально может состоять из 20 символов, и резервируем соответствующее количество памяти. Но имена в основном состоят из 4-10 символов, а это значит, что у нас будет «перерасход» памяти практически в 2-3-4 раза, чем нужно на самом деле.
- Искусственное переполнение. Да, мы предположили и выделили целых 20 символов на имя пользователя, но что будет, если имя у нашего пользователя будет в 25 символов? Как вариант, мы сможем в этом случае выдать пользователю предупреждение, что его имя «слишком длинное» и его нужно «укоротить», либо произойдет переполнение нашего массива с именами, а значит, может произойти что-то неприятное.
Динамическое выделение памяти в С как раз решает подобные проблемы с памятью. Потому что динамическое выделение памяти в Си дает возможность работающим программам давать представление на необходимое количество памяти непосредственно в операционную систему. Такая память не ограничивается количеством стековой памяти, которая сама по себе очень часто сильно ограничена несколькими мегабайтами. Динамическая память «черпается» из памяти, которой управляет операционная, а там уже счет идет на гигабайты.
Как работает динамическое выделение памяти
Все мы знаем, что современные компьютеры обладают гигабайтами памяти, которая может быть использована по-разному, в том числе и различным программным обеспечением. Когда вы запускаете на компьютере какую-либо программу, «операционка» компьютера резервирует часть этой памяти и загружает туда запущенную программу. Как правило, резервируется достаточно большой объем памяти, которая разделяется на несколько частей:
- в одной части содержится код самой программы;
- в другой части выполняются различные функции;
- третья, самая большая часть памяти, просто ожидает запросов от программы на ее распределение.
Когда происходит динамическое выделение памяти, мы фактически «просим» операционную систему воспользоваться свободной областью памяти для нужд нашего программного продукта. Когда «операционка» соглашается с нашей «просьбой», то в ответ выдает адрес выделенной памяти для нужд нашего программного обеспечения. С этого момента наша программа сможет пользоваться выделенной памятью по своему усмотрению. Когда программа завершает свою работу, она «передает» эту память обратно под управление операционной системе.
Как реализовать динамическое выделение памяти в С
Во-первых, нужно сказать, что динамическое выделение памяти в Си может происходить для разных элементов программы:
- массива объектов;
- одиночного объекта классов;
- структуры;
- массива структур;
- перечисления;
- типов-значений;
- и др.
Динамическое выделение памяти в С может быть реализовано следующими функциями:
1. malloc(). Общая формула реализации:
#include <cstdlib> /* или #include <stdlib.h> */
void *malloc(size_t size);
2. calloc(). Общая формула реализации:
#include <cstdlib> /* или #include <stdlib.h> */
void *calloc(size_t count, size_t elem_size);
3. realloc(). Общая формула реализации:
#include <cstdlib> /* или #include <stdlib.h> */
void *realloc(void *memory, size_t newSize);
4. free(). Общая формула реализации:
#include <cstdlib> /* или #include <stdlib.h> */
void free(void *memory).
Также динамическое выделение памяти в С можно реализовать при помощи оператора «new»:
int *p = new int; // Здесь не задается начальное значение памяти
int *p = new int(10); // Здесь задается начальное значение памяти
Заключение
Подробнее о том, как происходит динамическое выделение памяти в С, применяя каждый приведенный выше метод, мы распишем в следующих статьях.
Другое