Как работает malloc. Работа с памятью в C. Операторы new и delete

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

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

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

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

·В языках 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() и 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++;
нельзя выполнять арифметические операции над ссылкой, например, добавить смещение;

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

#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

  • описать указатель (тип * указатель; );
  • определить размер массива;
  • выделить участок памяти для хранения массива и присвоить указателю адрес этого участка памяти.

Для выделения памяти в С++ можно воспользоваться оператором new или функциями языка С - calloc, malloc, realloc . Все функции находятся в библиотеке stdlib.h .

5.2.1 Функция malloc

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

void* malloc (size_t size);

где size - целое беззнаковое значение 1size_t - базовый беззнаковый целочисленный тип языка С/С++, который выбирается таким образом, чтобы в него можно было записать максимальный размер теоретически возможного массива любого типа. В 32-битной операционной системе size_t является беззнаковым 32-битным числом (максимальное значение 2 32 - 1), в 64-битной - 64-битным беззнаковым числом (максимальное значение 2 64 - 1). , определяющее размер выделяемого участка памяти в байтах. Если резервирование памяти прошло успешно, то функция возвращает переменную типа void* , которую можно преобразовать к любому необходимому типу указателя. Если выделить память невозможно, то функция вернёт пустой указатель NULL .

Например,

double *h; //Описываем указатель на double. int k; cin>>k; //Ввод целого числа k. //Выделение участка памяти для хранения k элементов типа double. //Адрес этого участка хранится в переменной h. h=(double *) malloc (k* sizeof (double)); //h - адрес начала участка памяти, //h + 1, h + 2, h + 3 и т. д. - адреса последующих элементов типа double

5.2.2 Функция calloc

Функция calloc предназначена для выделения и обнуления памяти.

void * calloc (size_t num, size_t size);

С помощью функции будет выделен участок памяти, в котором будет храниться num элементов по size байт каждый. Все элементы выделенного участка обнуляются. Функция возвращает указатель на выделенный участок или NULL при невозможности выделить память.

Например,

float *h; //Описываем указатель на float. int k; cin>>k; //Ввод целого числа k. //Выделение участка памяти для хранения k элементов типа float. //Адрес этого участка хранится в переменной h . h=(float *) calloc (k, sizeof (float)); //h - адрес начала участка памяти, //h + 1, h + 2, h + 3 и т. д. - адреса последующих элементов типа float.

5.2.3 Функция realloc

Функция realloc изменяет размер ранее выделенного участка памяти. Обращаются к функции так:

void *realloc (void *p, size_t size);

где p - указатель на область памяти, размер которой нужно изменить на size . Если в результате работы функции меняется адрес области памяти, то новый адрес вернётся в качестве результата. Если фактическое значение первого параметра NULL , то функция realloc работает так же, как и функция malloc , то есть выделяет участок памяти размером size байт.

5.2.4 Функция free

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

void free (void *p);

где p - указатель на участок памяти, ранее выделенный функциями malloc, calloc или realloc .

5.2.5 Операторы new и delete

В языке С++ есть операторы new для выделения и delete для освобождения участка памяти.

Для выделения памяти для хранения n элементов одного типа оператор new имеет вид [

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