Домой / Скайп / Выделение памяти в Си (функция malloc). Работа с памятью в C Как работает malloc

Выделение памяти в Си (функция malloc). Работа с памятью в C Как работает malloc


#include void *malloc(size_t size);

Описание


Возвращает указатель на первый байт области памяти, которая была выделена из кучи


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

Если вы пишете 16-разрядные программы для семейства процессоров 8086(например, 80486 или Pentium), то ваш компилятор, вероятно, предоставляет дополнительные функции выделения памяти, которые учитывают модель сегментированной памяти, используемую этими процессорами при работе в 16-разрядном режиме. Например, это могут быть функции, выделяющие память FAR-кучи(которая находится вне стандартного сегмента данных). Эти функции могут назначать указатели на память, объем которой больше одного сегмента, и освобождать такую память.

void free(void *pointer);

используется для освобождения памяти, на которую указывает аргумент pointer. Сначала память выделяется для приложения, после завершения работы с памятью её надо вернуть, этим возвратом и занимается функция free.

_msize

Функция _msize возвращает размер область памяти, выделенной из кучи:

size_t _msize (void*);

аргумент - указатель на блок памяти. Функция _msize возврашает размер памяти в байтах. size_t - это unsigned integer.

malloc

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

void* malloc(size_t);

аргумент определяет количество байтов, которое надо выделить из памяти. Функция malloc возвращает void указатель на выделенную область памяти, его можно привести к нужному типу. Если свободной для выделения памяти меньше, чем затребовоно в size_t, то функция malloc вернет NULL.

Пример работы с функцией malloc:

/* Author: @author Subbotin B.P..h> #include #include int main(void) { puts(«Memory»); int *pointer; puts(«to get memory»); pointer = (int*)malloc(2 * sizeof(int)); int memorySize = _msize(pointer); printf(«memory size = %dn», memorySize); if(pointer == NULL) { puts(«Problems»); return EXIT_FAILURE; } free(pointer); puts(«to free memory»); return EXIT_SUCCESS; }

здесь выделено место в памяти для массива, состоящего из двух элементов типа int. Если выделение памяти пошло успешно, то освобождаем эту область памяти с помощью функции free.

Получаем:

calloc

Функция calloc выделяет область памяти и размещает в ней массив, инициализированный нулями:

void* calloc(size_t, size_t);

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

Пример работы с функцией calloc:

/* Author: @author Subbotin B.P..h> #include #include int main(void) { puts(«Memory»); int *pointer; puts(«to get memory»); pointer = (int*)calloc(2, sizeof(int)); int memorySize = _msize(pointer); printf(«memory size = %dn», memorySize); if(pointer == NULL) { puts(«Problems»); return EXIT_FAILURE; } free(pointer); puts(«to free memory»); return EXIT_SUCCESS; }

в примере выделяется память для массива типа int, содержащего два элемента. Эти элементы инициализированны нулями. Если выделение памяти пошло успешно, то освобождаем эту область памяти с помощью функции free.

Получаем:

realloc

Функция realloc меняет размер предварительно выделенной области памяти:

void* realloc(void*, size_t);

первый аргумент - это указатель на область памяти, размер которой нужно изменить, второй аргумент определяет новый размер области памяти. Если этот размер равен нулю, а первый аргумент указывает на имеющуюся область памяти, то функция realloc вернет NULL, а исходный блок памяти, на который указывает первый аргумент, будет освобожден. Если свободной для выделения памяти меньше, чем затребовоно, то функция realloc вернет NULL, а исходный блок памяти, на который указывает первый аргумент, сохраниться и останется без изменений. Функция realloc возвращает void указатель на выделенную область памяти, его можно привести к нужному типу.

Динамическое выделение памяти

Основные проблемы применения

Нулевой указатель

Нулевой указатель − это указатель, хранящий специальное значение, используемое для того, чтобы показать, что данная переменная-указатель не ссылается (не указывает) ни на какой объект. В различных языках программирования представлен различными константами.

·В языках C# и Java: null

·В языках Си и C++: 0 или макрос NULL. Кроме того, в стандарте C++11 для обозначения нулевого указателя предложено новое ключевое слово nullptr

·В языках Паскаль и Ruby: nil

·В языке Компонентный Паскаль:NIL

·В языке Python: None

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

Для решения части проблем есть методы предохранения и страховки:

Изучив указатели в языке Си, мы открыли для себя возможности динамического выделения памяти. Что это значит? Это значит то, что при динамическом выделении памяти, память резервируется не на этапе компиляции а на этапе выполнения программы. И это дает нам возможность выделять память более эффективно, в основном это касается массивов. С динамическим выделением память, нам нет необходимости заранее задавать размер массива, тем более, что не всегда известно, какой размер должен быть у массива. Далее рассмотрим каким же образом можно выделять память.

Функция malloc() определена в заголовочном файле stdlib.h, она используется для инициализации указателей необходимым объемом памяти. Память выделяется из сектора оперативной памяти доступного для любых программ, выполняемых на данной машине. Аргументом функции malloc() является количество байт памяти, которую необходимо выделить, возвращает функция - указатель на выделенный блок в памяти. Функция malloc() работает также как и любая другая функция, ничего нового.

Так как различные типы данных имеют разные требования к памяти, мы как-то должны научиться получить размер в байтах для данных разного типа. Например, нам нужен участок памяти под массив значений типа int - это один размер памяти, а если нам нужно выделить память под массив того же размера, но уже типа char - это другой размер. Поэтому нужно как-то вычислять размер памяти. Это может быть сделано с помощью операции sizeof(), которая принимает выражение и возвращает его размер. Например, sizeof(int) вернет количество байтов, необходимых для хранения значения типа int. Рассмотрим пример:


Яндекс.Директ


#include int *ptrVar = malloc(sizeof(int));

В этом примере, встроке 3 указателю ptrVar присваивается адрес на участок памяти, размер которого соответствует типу данных int. Автоматически, этот участок памяти становится недоступным для других программ. А это значит, что после того, как выделенная память станет ненужной, её нужно явно высвободить. Если же память не будет явно высвобождена, то по завершению работы программы, память так и не освободится для операционной системы, это называется утечкой памяти. Также можно определять размер выделяемой памяти, которую нужно выделить передавая пустой указатель, вот пример:



Как видите, в такой записи есть одна очень сильная сторона, мы не должны вызывать функцию malloc() с использованиемsizeof(float). Вместо этого мы передали в malloc() указатель на тип float, в таком случае, размер выделяемой памяти автоматически определится сам!

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


float *ptrVar; /* . . . сто строк кода */ . . . ptrVar = malloc(sizeof(*ptrVar));

Если бы вы использовали конструкцию выделения памяти с операцией sizeof(), то вам бы пришлось находить в коде определение указателя, смотреть его тип данных и уже потом вы бы смогли правильно выделить память.

Динамическое выделение памяти с помощью библиотечной функции malloc состоит из следующих шагов.

1. Включение в программу файла заголовков malloc .h директивой #include .

2. Объявление указателя нужного типа, например int *p ;

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

p =(int *) malloc (число элементов массива*sizeof (int ));

Вместо int может быть подставлен любой стандартный или введенный программистом тип.

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

if (!p ) сообщение, выход; else продолжение;

5. Освобождение памяти после окончания работы с ней. Для этого вызываем функцию f гее и используем указатель в качестве аргумента:

free (p );

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

Наиболее частой причиной «зависания» компьютера при работе с динамически выделяемой памятью является несоответствие инструкций malloc и free (в обеих инструкциях должен использоваться один и тоже указатель) или недостаточный объем свободной памяти.

В качестве примера рассмотрим ввод/вывод одномерного динамического массива произвольной длины, задаваемой с клавиатуры.

int i ,n ,*massiv ;//объявление указателя

cout <>n ;//ввод размера массива

massiv=(int*)malloc(n*sizeof(int));//выделение динам.памяти

if (!massiv )//проверка факта выделения памяти

{cout <

cout <

getch();

return 0;}

cout<

for(i=0;i>massiv[i];//ввод массива

cout<

for(i=0;i

free (massiv );//освобождение памяти

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

Для динамического выделения памяти можно также использовать функцию calloc (). В отличии от malloc функция calloc кроме выделения области памяти под массив объектов еще производит инициализацию элементов массива нулевыми значениями.

В зависимости от используемой версии C++ для работы с большими фрагментами динамической памяти возможно применение функций farmalloc (), farcalloc (), farcoreleft () и farfree ().