Как работает malloc. Распределение памяти: malloc() и са11ос(). Смотреть что такое "Malloc" в других словарях

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

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

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

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

·В языках 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(), то вам бы пришлось находить в коде определение указателя, смотреть его тип данных и уже потом вы бы смогли правильно выделить память.

Описание функций

#include void * malloc (size_t size) ; void * calloc (size_t num, size_t size) ;

Назначение

malloc принимает в качестве аргумента размер выделяемой области в байтах ; возвращает нетипизированный указатель (void*) на область памяти заявленного размера или NULL в случае, если выделить память невозможно. Содержимое выделяемой области памяти не определено.

calloc принимает в качестве аргумента количество элементов и размер каждого элемента в байтах; возвращает нетипизированный указатель (void*) на область памяти заявленного размера или NULL в случае, если выделить память невозможно. Значения элементов устанавливаются в ноль. malloc работает быстрее, чем calloc , в связи с отсутствием функции обнуления выделяемой памяти.

Параметры функций

malloc

  • size - размер распределяемой области памяти

calloc

  • num - количество распределяемых элементов
  • size - размер каждого элемента

Возвращаемое значение

Функции возвращают нетипизированный (void*) указатель на область памяти в случае успеха, либо NULL в противном случае.

Характерные ошибки при использовании

  • Память остаётся «занятой», даже если ни один указатель в программе на неё не ссылается (для освобождения памяти используется функция free()). Накопление «потерянных» участков памяти приводит к постепенной деградации системы. Ошибки, связанные с неосвобождением занятых участков памяти, называются утечками памяти (англ. memory leaks ).
  • Если объём обрабатываемых данных больше, чем объём выделенной памяти, возможно повреждение других областей динамической памяти. Такие ошибки называются ошибками переполнения буфера (англ. buffer overflow ).
  • Если указатель на выделенную область памяти после освобождения продолжает использоваться, то при обращении к «уже не существующему» блоку динамической памяти может произойти исключение (англ. exception ), сбой программы, повреждение других данных или не произойти ничего (в зависимости от типа операционной системы и используемого аппаратного обеспечения).
  • Если для одной области памяти free() вызывается более чем один раз, то это может повредить данные самой библиотеки , содержащей malloc/free, и привести к непредсказуемому поведению в произвольные моменты времени.
  • Неудачная организация программы, в которой выделяется и освобождается множество небольших объёмов памяти - возможна фрагментация свободной памяти («пунктир»), при которой свободной памяти в сумме остаётся много, но выделить большой кусок невозможно.

Точное поведение функций описано в стандарте ANSI C , на него же ссылается определение функции в стандарте POSIX .

Примеры использования

malloc

Float * dynamic_array = malloc(number_of_elements * sizeof (float ) ) ; if (! dynamic_array) { }

calloc

Float * dynamic_array = calloc(number_of_elements, sizeof (float ) ) ; if (! dynamic_array) { /* обработка ошибки выделения памяти */ } /* … работа с элементами массива … */ free(dynamic_array) ; dynamic_array = NULL;

См. также

  • stdlib
  • alloca
  • soap malloc
  • soap destroy

Источники

  • malloc (англ.) . - Описание функции malloc в стандарте POSIX.
  • calloc (англ.) . - Описание функции calloc в стандарте POSIX.

Wikimedia Foundation . 2010 .

Смотреть что такое "Malloc" в других словарях:

    Malloc - est en informatique une fonction de la bibliothèque standard du C permettant d allouer dynamiquement de la mémoire. La libération de la mémoire ainsi réservée s effectue avec la fonction free. Cette fonction est déclarée dans le fichier d en tête … Wikipédia en Français

    malloc - est en informatique une fonction de la bibliothèque standard de C permettant d allouer dynamiquement de la mémoire. La libération de la mémoire ainsi réservée s effectue avec la fonction free. Cette fonction est déclarée dans l en tête

    Malloc - In computing, malloc is a subroutine provided in the C and C++ programming language s standard libraries for performing dynamic memory allocation. Rationale The C programming language manages memory either statically or automatically . Static… … Wikipedia

    Malloc - En informática, malloc es una subrutina para el ejercicio de asignación de memoria dinámica en los lenguajes de programación C y C++. Es una abreviatura del inglés Memory Allocation. Forma parte de la biblioteca estándar stdlib.h para ambos… … Wikipedia Español

    malloc - 1. noun A subroutine in the C programming languages standard library for performing dynamic memory allocation. It compares the behavior of nine different mallocs when used with Hummingbird and GNU Emacs dynamic memory activity traces. 2. verb … Wiktionary

    malloc - ● np. cde. LANGC CMDE Contraction de Memory Allocation. Nom d une fonction très importante de la bibliothèque C, car elle permet d attribuer une partie de la mémoire à un processus. Voir aussi calloc. (D après ) … Dictionnaire d"informatique francophone

    C dynamic memory allocation - C Standard Library Data types Character classification Strings Mathematics File input/output Date/time Localization … Wikipedia

    Pointer (computing) - This article is about the programming data type. For the input interface (for example a computer mouse), see Pointing device. Pointer a pointing to the memory address associated with variable b. Note that in this particular diagram, the computing … Wikipedia

    Значимость предмета статьи поставлена под сомнение. Пожалуйста, покажите в статье значимость её предмета, добавив в неё доказательства значимости по частным критериям значимости или, в случае если частные критерии значимости для… … Википедия

    Dangling pointer - Dangling pointers and wild pointers in computer programming are pointers that do not point to a valid object of the appropriate type. These are special cases of memory safety violations. Dangling Pointer Dangling pointers arise when an object is… … Wikipedia

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

char place = "Залив Свиной печенки";

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

Или мы можем быть более конкретны и запросить определенный объем памяти:

int plates;

Это описание выделяет 100 ячеек памяти, каждая из которых предназначена для запоминания целого значения.

Язык Си не останавливается на этом. Он позволяет вам распределять дополнительную память во время работы программы. Предположим, например, вы пишете диалоговую программу и не знаете заранее, сколько данных вам придется вводить. Можно выделить нужный вам (как вы считаете) объем памяти, а затем, если понадобится, потребовать еще. На рис. 15.5 дан пример, в котором используется функция malloc() , чтобы сделать именно это. Кроме того, обратите внимание на то, как такая программа применяет указатели.

/* добавляет память, если необходимо */

#include

#define STOP " " /* сигнал прекращения ввода */

#define BLOCK 100 /* байты памяти */

#define LIM 40 /* предельная длина вводимой строки */

#define MAX 50 /* максимальное число вводимых строк */

#define DRAMA 20000 /* большая задержка времени */

char store; /* исходный блок памяти */

char symph; /* приемник вводимых строк */

char *end; /* указывает на конец памяти */

char *starts; /* указывает на начала строк */

int index = 0; /* количество вводимых строк */

int count; /* счетчик */

char *malloc(); /* распределитель памяти */

starts = store;

end = starts + BLOCK - 1;

puts(" Назовите несколько симфонических оркестром.");

puts(" Вводите по одному: нажмите клавишу [ввод] в начале");

puts(" строки для завершения вашего списка. Хорошо, я готова.");

while(strcmp(fgets(symph, LIM, stdin), STOP) != 0 && index < MAX)

{ if(strlen(symph) > end - starts)

{ /* действия при недостатке памяти для запоминания вводимых данных*/

puts(" Подождите секунду. Я попробую найти дополнительную память.");

end = starts + BLOCK - 1;

for(count = 0; count < DRAMA; count++);

puts(" Нашла немного!"); }

strcpy (starts , symph);

starts = starts + strlen(symph) + 1;

if(++index < MAX)

printf("Этo %d. Продолжайте, если хотите.n", index); }

puts(" Хорошо, вот что я получила:");

for(count = 0; count < index; count ++)

puts(starts);

РИС. 15.5. Программа, добавляющая память по требованию.

Вот образец работы программы:

Назовите несколько симфонических оркестров оркестров.

Вводите их по одному; нажмите клавишу [ввод] в начале

строки для завершения нашего списка. Хорошо, я готова.

Сан-франциский симфонический.

Это 1. Продолжайте, если хотите.

Чикагский симфонический

Это 2. Продолжайте, если хотите.

Берлинский филармонический

Это 3. Продолжайте, если хотите.

Московский камерный

Это 4. Продолжайте, если хотите. Лондонский симфонический

Это 5. Продолжайте, если хотите. Венский филармонический

Подождите секунду. Я попробую найти дополнительную память.

Нашла немного!

Это 6. Продолжайте, если хотите.

Питтсбургский симфонический

Это 7. Продолжайте, если хотите.

Хорошо, вот что я получила:

Сан-францизкий симфонический

Чикагский симфонический

Берлинский филармонический

Московский камерный

Лондонский симфонический

Венский филармонический

Питтсбургский симфонический

Сначала давайте посмотрим, что делает функция malloc() . Она берет аргумент в виде целого без знака, которое представляет количество требуемых байтов памяти. Так, malloc(BLOCK) требует 100 байт. Функция возвращает указатель на тип char в начало нового блока памяти. Мы использовали описание

char *malloc();

чтобы предупредить компилятор, что malloc() возвращает указатель на тип char . Поэтому мы присвоили значение этого указателя элементу массива starts при помощи оператора

starts = malloc(BLOCK);

Хорошо, давайте теперь рассмотрим проект программы, заключающийся в том, чтобы запомнить все исходные строки подряд в большом массиве store . Мы хотим использовать starts для ссылки на начало первой строки, starts[l] - второй строки и т. д. На промежуточном этапе программа вводит строку в массив symph . Мы использовали fgets() вместо gets() , чтобы ограничить входную строку длиной массива symph .

РИС. 15.6. Последовательные строки symph, записанные в массив store.

Прежде чем копировать symph в store , мы должны проверить, достаточно ли для нее оставшегося места. Указатель end ссылается на конец памяти, а текущее значение starts ссылается на начало неиспользованной памяти. Таким образом, мы можем сравнить разницу между этими двумя указателями с длиной symph и определить, достаточно ли осталось памяти.

Если места недостаточно, вызываем malloc() , чтобы подготовить дополнительную память. Мы устанавливаем starts на начало нового блока памяти, a end - на конец нового блока. Заметим, что у нас нет имени этой новой памяти. Она не является, например, расширением store . У нас есть только обозначения указателей, ссылающихся на новую область памяти.

Когда программа работает, на каждую новую строку ссылается элемент массива указателей starts . Некоторые строки находятся в store , другие - в одной или нескольких новых областях памяти.

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

Таким образом используется mаllос() . Но предположим, что вы хотите работать с памятью типа int , а не char . Можете и здесь использовать mаllос() . Вот как это делается:

char *malloc(); /* по-прежнему описываем как указатель на char */

int *newmem;

newmem = (int *) malloc(l00); /* используем операцию приведения типа */

Снова требуется 100 байт. Операция приведения типа преобразует значение, возвращенное указателем на тип char , в указатель на тип int . Если, как в нашей системе, int занимает два байта памяти, это значит, что newmem + 1 будет увеличивать указатель на два байта, т. е. передвигать его к следующему целому. Это также означает, что 100 байт можно использовать для запоминания 50 целых чисел.

Другую возможность распределения памяти дает нам применение функции саllос() :

char *calloc();

long *newmem;

newmem = (long *) calloc(100, sizeof(long));

Подобно malloc() функция саllос() возвращает указатель на char . Нужно использовать оператор приведения типа, если вы хотите запомнить другой тип. Эта новая функция имеет два аргумента, и оба они должны быть целыми без знака. Первый аргумент содержит количество требуемых ячеек памяти. Второй аргумент - размер каждой ячейки в байтах. В нашем случае long

Динамическое выделение памяти с помощью библиотечной функции 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 ().

Каждый раз при инициализации указателя использовался адрес той или иной переменной. Это было связано с тем, что компилятор языка С++ автоматически выделяет память для хранения переменных и с помощью указателя можно без последствий работать с этой выделенной областью. Вместе с тем существуют функции malloc() и free(), позволяющие выделять и освобождать память по мере необходимости. Данные функции находятся в библиотеке и имеют следующий синтаксис:

void* malloc(size_t); //функция выделения памяти
void free(void* memblock); //функция освобождения памяти

Здесь size_t – размер выделяемой области памяти в байтах; void* - обобщенный тип указателя, т.е. не привязанный к какому-либо конкретному типу. Рассмотрим работу данных функций на примере выделения памяти для 10 элементов типа double.

Листинг 4.3. Программирование динамического массива.

#include
#include
int main()
{
double* ptd;
ptd = (double *)malloc(10 * sizeof(double));
if(ptd != NULL)
{
for(int i = 0;i ptd[i] = i;
} else printf(“Не удалось выделить память.”);
free(ptd);
return 0;
}

При вызове функции malloc() выполняется расчет необходимой области памяти для хранения 10 элементов типа double. Для этого используется функция sizeof(), которая возвращает число байт, необходимых для хранения одного элемента типа double. Затем ее значение умножается на 10 и в результате получается объем для 10 элементов типа double. В случаях, когда по каким-либо причинам не удается выделить указанный объем памяти, функция malloc() возвращает значение NULL. Данная константа определена в нескольких библиотеках, в том числе в и. Если функция malloc() возвратила указатель на выделенную область памяти, т.е. не равный NULL, то выполняется цикл, где записываются значения для каждого элемента. При выходе из программы вызывается функция free(), которая освобождает ранее выделенную память. Формально, программа написанная на языке С++ при завершении сама автоматически освобождает всю ранее выделенную память и функция free(), в данном случае, может быть опущена. Однако при составлении более сложных программ часто приходится много раз выделять и освобождать память. В этом случае функция free() играет большую роль, т.к. не освобожденная память не может быть повторно использована, что в результате приведет к неоправданным затратам ресурсов ЭВМ.

Использование указателей досталось в "наследство" от языка С. Чтобы упростить процесс изменения параметров в С++ вводится такое понятие как ссылка. Ссылка представляет собой псевдоним (или второе имя), который программы могут использовать для обращения к переменной. Для объявления ссылки в программе используется знак & перед ее именем. Особенность использования ссылок заключается в необходимости их инициализации сразу же при объявлении, например:

int var;
int &var2 = var;

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

Листинг 4.4. Пример использования ссылок.

void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int agr1 = 10, arg2 = 5;
swap(arg1, arg2);
return 0;
}

В данном примере функция swap() использует два аргумента, представляющие собой ссылки на две переменные. Используя имена ссылок a и b, осуществляется манипулирование переменными arg1 и arg2, заданных в основной функции main() и переданных как параметры функции swap(). Преимущество функции swap() (которая использует ссылки, а не указатели на переменные) заключается гарантии того, что функция в качестве аргументов будет принимать соответствующие типы переменные, а не какую-либо другую информацию, и ссылки будут инициализированы корректно перед их использованием. Это отслеживается компилятором в момент преобразования текста программы в объектный код и выдается сообщение об ошибке, если использование ссылок неверно. В отличие от указателей со ссылками нельзя выполнять следующие операции:

Нельзя получить адрес ссылки, используя оператор адреса C++;
нельзя присвоить ссылке указатель;
нельзя сравнить значения ссылок, используя операторы сравнения C++;
нельзя выполнять арифметические операции над ссылкой, например, добавить смещение;