C выделение памяти. Распределение памяти
Динамическое выделение памяти необходимо для эффективного использования памяти компьютера. Например, мы написали какую-то программку, которая обрабатывает массив. При написании данной программы необходимо было объявить массив, то есть задать ему фиксированный размер (к примеру, от 0 до 100 элементов). Тогда данная программа будет не универсальной, ведь может обрабатывать массив размером не более 100 элементов. А если нам понадобятся всего 20 элементов, но в памяти выделится место под 100 элементов, ведь объявление массива было статическим, а такое использование памяти крайне не эффективно.
В С++ операции new и delete предназначены для динамического распределения памяти компьютера. Операция new выделяет память из области свободной памяти, а операция delete высвобождает выделенную память. Выделяемая память, после её использования должна высвобождаться, поэтому операции new и delete используются парами. Даже если не высвобождать память явно, то она освободится ресурсами ОС по завершению работы программы. Рекомендую все-таки не забывать про операцию delete .
// пример использования операции new int *ptrvalue = new int; //где ptrvalue – указатель на выделенный участок памяти типа int //new – операция выделения свободной памяти под создаваемый объект.
Операция new создает объект заданного типа, выделяет ему память и возвращает указатель правильного типа на данный участок памяти. Если память невозможно выделить, например, в случае отсутствия свободных участков, то возвращается нулевой указатель, то есть указатель вернет значение 0. Выделение памяти возможно под любой тип данных: int , float ,double , char и т. д.
// пример использования операции delete: delete ptrvalue; // где ptrvalue – указатель на выделенный участок памяти типа int // delete – операция высвобождения памяти
Разработаем программу, в которой будет создаваться динамическая переменная.
// new_delete.cpp: определяет точку входа для консольного приложения.
#include "stdafx.h"
#include
// код Code::Blocks
// код Dev-C++
// new_delete.cpp: определяет точку входа для консольного приложения.
#include
В строке 10 показан способ объявления и инициализации девяткой динамического объекта, все, что нужно так это указать значение в круглых скобочках после типа данных. Результат работы программы показан на рисунке 1.
Ptrvalue = 9 Для продолжения нажмите любую клавишу. . .
Рисунок 1 — Динамическая переменная
Создание динамических массивов
Как было сказано раньше, массивы также могут быть динамическими. Чаще всего операции new и delete применяются для создания динамических массивов, а не для создания динамических переменных. Рассмотрим фрагмент кода создания одномерного динамического массива.
// объявление одномерного динамического массива на 10 элементов: float *ptrarray = new float ; // где ptrarray – указатель на выделенный участок памяти под массив вещественных чисел типа float // в квадратных скобочках указываем размер массива
После того как динамический массив стал ненужным, нужно освободить участок памяти, который под него выделялся.
// высвобождение памяти отводимой под одномерный динамический массив: delete ptrarray;
После оператора delete ставятся квадратные скобочки, которые говорят о том, что высвобождается участок памяти, отводимый под одномерный массив. Разработаем программу, в которой создадим одномерный динамический массив, заполненный случайными числами.
// new_delete_array.cpp: определяет точку входа для консольного приложения.
#include "stdafx.h"
#include
// код Code::Blocks
// код Dev-C++
// new_delete_array.cpp: определяет точку входа для консольного приложения.
#include
Созданный одномерный динамический массив заполняется случайными вещественными числами, полученными c помощью функций генерации случайных чисел, причём числа генерируются в интервале от 1 до 10, интервал задается так — rand() % 10 + 1 .
Чтобы получить случайные вещественные числа, выполняется операция деления, с использованием явного приведения к вещественному типу знаменателя — float((rand() % 10 + 1)) . Чтобы показать только два знака после запятой используем функцию setprecision(2) ,
прототип данной функции находится в заголовочном файле
Array = 0.8 0.25 0.86 0.5 2.2 10 1.2 0.33 0.89 3.5 Для продолжения нажмите любую клавишу. . .
Рисунок 2 — Динамический массив в С++
По завершению работы с массивом, он удаляется, таким образом, высвобождается память, отводимая под его хранение.
Как создавать и работать с одномерными динамическими массивами мы научились. Теперь рассмотрим фрагмент кода, в котором показано, как объявляется двумерный динамический массив.
// объявление двумерного динамического массива на 10 элементов: float **ptrarray = new float* ; // две строки в массиве for (int count = 0; count < 2; count++) ptrarray = new float ; // и пять столбцов // где ptrarray – массив указателей на выделенный участок памяти под массив вещественных чисел типа float
Сначала объявляется указатель второго порядка float **ptrarray , который ссылается на массив указателей float* ,где размер массива равен двум. После чего в цикле for каждой строке массива объявленного в строке 2 выделяется память под пять элементов. В результате получается двумерный динамический массив ptrarray .Рассмотрим пример высвобождения памяти отводимой под двумерный динамический массив.
// высвобождение памяти отводимой под двумерный динамический массив: for (int count = 0; count < 2; count++) delete ptrarray; // где 2 – количество строк в массиве
Объявление и удаление двумерного динамического массива выполняется с помощью цикла, так как показано выше, необходимо понять и запомнить то, как это делается. Разработаем программу, в которой создадим двумерный динамический массив.
// new_delete_array2.cpp: определяет точку входа для консольного приложения.
#include "stdafx.h"
#include // код Code::Blocks // код Dev-C++ // new_delete_array2.cpp: определяет точку входа для консольного приложения.
#include При выводе массива была использована функция setw() , если вы не забыли, то она отводит место заданного размера под выводимые данные. В нашем случае, под каждый элемент массива по четыре позиции, это позволяет выровнять, по столбцам, числа разной длинны (см. Рисунок 3). 2.7 10 0.33 3 1.4
6 0.67 0.86 1.2 0.44
Для продолжения нажмите любую клавишу. . . Рисунок 3 — Динамический массив в С++ Второй способ, которым С++ может хранить информацию, заключается в использовании системы динамического распределения. При этом способе память
распределяется для информации из свободной области памяти по
мере необходимости. Область свободной памяти находится между кодом программы с ее постоянной областью памяти и стеком (
рис.
24.1). Динамическое размещение
удобно, когда неизвестно, сколько элементов данных будет обрабатываться.
По
мере использования программой стековая область увеличивается вниз, то есть программа
сама определяет объем стековой памяти. Например, программа
с большим числом рекурсивных функций
займет больше стековой памяти, чем программа
, не имеющая рекурсивных функций
, так как локальные переменные и возвращаемые адреса хранятся в стеках. Память
под саму программу и глобальные переменные
выделяется на все время выполнения
программы и является постоянной для конкретной среды. Память
, выделяемая в процессе выполнения программы, называется динамической. После выделения динамической
памяти она сохраняется до ее явного освобождения, что может быть выполнено только с помощью специальной операции
или библиотечной функции. Если динамическая
память
не освобождена до окончания программы, то она освобождается автоматически при завершении программы. Тем не менее, явное освобождение ставшей ненужной памяти является признаком хорошего стиля программирования. В процессе выполнения программы участок динамической памяти доступен везде, где доступен указатель
, адресующий этот участок. Таким образом, возможны следующие три варианта работы с динамической памятью
, выделяемой в некотором блоке (например, в теле неглавной функции). Все переменные, объявленные в программе размещаются в одной непрерывной области памяти, которую называют сегментом данных
. Такие переменные не меняют своего размера в ходе выполнения программы и называются статическими
. Размера сегмента данных может быть недостаточно для размещения больших объемов информации. Выходом из этой ситуации является использование динамической памяти. Динамическая память
– это память
, выделяемая программе для ее работы за вычетом
сегмента данных, стека, в котором размещаются локальные переменные подпрограмм и собственно тела программы. Для работы с динамической памятью используют указатели. С их помощью осуществляется доступ
к участкам динамической памяти, которые называются динамическими переменными
. Для хранения динамических переменных
выделяется специальная область памяти, называемая " кучей
". Динамические переменные
создаются с помощью специальных функций и операций. Они существуют либо до конца работы программы, либо до тех пор, пока не будет освобождена выделенная под них память
с помощью специальных функций или операций. То есть время жизни динамических переменных
– от точки создания до конца программы или до явного освобождения памяти
. В С++ используется два способа работы с динамической памятью: В языке программирования С++ для динамического распределения памяти
существуют операции
new
и delete
. Эти операции
используются для выделения и освобождения блоков памяти
. Область памяти, в которой размещаются эти блоки, называется свободной памятью
. Операция new
позволяет выделить и сделать доступным свободный участок в основной памяти, размеры которого соответствуют типу данных, определяемому именем типа. Синтаксис
: new ИмяТипа; new ИмяТипа [Инициализатор]; В выделенный участок заносится значение
, определяемое инициализатором
, который не является обязательным элементом. В случае успешного выполнения new
возвращает адрес
начала выделенного участка памяти. Если участок нужных размеров не может быть выделен (нет памяти), то операция new
возвращает нулевое значение
адреса (NULL
). Синтаксис
применения операции
: Указатель = new ИмяТипа [Инициализатор]; Операция new float
выделяет участок памяти размером 4 байта. Операция new int(15)
выделяет участок памяти 4 байта и инициализирует этот участок целым значением 15. Синтаксис
использования операций new
и delete
предполагает применение указателей. Предварительно каждый указатель
должен быть объявлен: тип *ИмяУказателя; Например: float *pi; //Объявление переменной pi
pi=new float; //Выделение памяти для переменной pi
* pi = 2.25; //Присваивание значения В качестве типа можно использовать, например, стандартные типы int, long, float, double, char
. Оператор new
чаще всего используется для размещения в памяти данных определенных пользователем типов, например, структур: struct Node {
char *Name;
int Value;
Node *Next
};
Node *PNode; //объявляется указатель
PNode = new Node; //выделяется память
PNode->Name = "Ata"; //присваиваются значения
PNode->Value = 1;
PNode->Next = NULL; В качестве имени типа в операции
new
может быть использован массив
: new ТипМассива При выделении динамической памяти для массива его размеры должны быть полностью определены. Например: ptr = new int ;//10 элементов типа int или 40 байт
ptr = new int ;//неверно, т.к. не определен размер Такая операция позволяет выделить в динамической памяти участок для размещения массива соответствующего типа, но не позволяет его инициализировать. В результате выполнения операция new
возвратит указатель
, значением которого служит адрес
первого элемента массива. Например: int *n = new int; Операция new
выполняет выделение достаточного для размещения величины типа int
участка динамической памяти и записывает адрес
начала этого участка в переменную n
. Память
под саму переменную n
(размера, достаточного для размещения указателя) выделяется на этапе компиляции. Существует два типа статических переменных: extern int maxind; int maxind = 1000; static int gcd(int x, int y); // Прототип ф-ции. . .
static int gcd(int x, int y) { // Реализация. . .
} Локальные, или стековые, переменные - это переменные, описанные внутри функции
. Память для таких переменных выделяется в аппаратном стеке, см. раздел 2.3.2. Память выделяется в момент входа в функцию или блок и освобождается в момент выхода из функции или блока. При этом захват и освобождение памяти происходят практически мгновенно, т.к. компьютер только изменяет регистр, содержащий адрес вершины стека. Локальные переменные можно использовать при рекурсии, поскольку при повторном входе в функцию в стеке создается новый набор локальных переменных, а предыдущий набор не разрушается. По этой же причине локальные переменные безопасны при использовании нитей в параллельном программировании (см. раздел 2.6.2). Программисты называют такое свойство функции реентерабельностью
, от англ. re-enter able
- возможность повторного входа. Это очень важное качество с точки зрения надежности и безопасности программы! Программа, работающая со статическими переменными, этим свойством не обладает, поэтому для защиты статических переменных приходится использовать механизмы синхронизации
(см. 2.6.2), а логика программы резко усложняется. Всегда следует избегать использования глобальных и статических переменных, если можно обойтись локальными. Недостатки локальных переменных являются продолжением их достоинств. Локальные переменные создаются при входе в функцию и исчезают после выхода из нее, поэтому их нельзя использовать в качестве данных, разделяемых между несколькими функциями. К тому же, размер аппаратного стека не бесконечен, стек может в один прекрасный момент переполниться (например, при глубокой рекурсии), что приведет к катастрофическому завершению программы. Поэтому локальные переменные не должны иметь большого размера. В частности, нельзя использовать большие массивы в качестве локальных переменных. Помимо статической и стековой памяти, существует еще практически неограниченный ресурс памяти, которая называется динамическая
, или куча
(heap
). Программа может захватывать участки динамической памяти нужного размера. После использования ранее захваченный участок динамической памяти следует освободить. Под динамическую память отводится пространство виртуальной памяти процесса между статической памятью и стеком. (Механизм виртуальной памяти был рассмотрен в разделе 2.6.) Обычно стек располагается в старших адресах виртуальной памяти и растет в сторону уменьшения адресов (см. раздел 2.3). Программа и константные данные размещаются в младших адресах, выше располагаются статические переменные. Пространство выше статических переменных и ниже стека занимает динамическая память: код программы и данные, защищенные от изменения статические переменные программы max.
адрес
(2 32 -4) Структура динамической памяти автоматически поддерживается исполняющей системой языка Си или C++
. Динамическая память состоит из захваченных и свободных сегментов, каждому из которых предшествует описатель сегмента. При выполнении запроса на захват памяти исполняющая система производит поиск свободного сегмента достаточного размера и захватывает в нем отрезок требуемой длины. При освобождении сегмента памяти он помечается как свободный, при необходимости несколько подряд идущих свободных сегментов объединяются. В языке Си для захвата и освобождения динамической памяти применяются стандартные функции malloc
и free
, описания их прототипов содержатся в стандартном заголовочном файле " stdlib.h
". (Имя malloc
является сокращением от memory allocate
- "захват памяти".) Прототипы этих функций выглядят следующим образом: void *malloc(size_t n); // Захватить участок памяти
// размером в n байт
void free(void *p); // Освободить участок
// памяти с адресом p Здесь n
- это размер захватываемого участка в байтах, size_t
- имя одного из целочисленных типов, определяющих максимальный размер захватываемого участка. Тип size_t
задается в стандартном заголовочном файле " stdlib.h
" с помощью оператора typedef
(см. c. 117). Это обеспечивает независимость текста Си-программы от используемой архитектуры. В 32-разрядной архитектуре тип size_t
определяется как беззнаковое целое число: typedef unsigned int size_t; Функция malloc
возвращает адрес захваченного участка памяти или ноль в случае неудачи (когда нет свободного участка достаточно большого размера). Функция free
освобождает участок памяти с заданным адресом. Для задания адреса используется указатель общего типа void*
. После вызова функции malloc
его необходимо привести к указателю на конкретный тип, используя операцию приведения типа, см. раздел 3.4.11. Например, в следующем примере захватывается участок динамической памяти размером в 4000
байтов, его адрес присваивается указателю на массив из 1000
целых чисел: int *a; // Указатель на массив целых чисел. . .
a = (int *) malloc(1000 * sizeof(int)); Выражение в аргументе функции malloc
равно 4000
, поскольку размер целого числа sizeof(int)
равен четырем байтам. Для преобразования указателя используется операция приведения типа (int *)
от указателя обобщенного типа к указателю на целое число. Рассмотрим пример, использующий захват динамической памяти. Требуется ввести целое цисло n
и напечатать n
первых простых чисел. (Простое число - это число, у которого нет нетривиальных делителей.) Используем следующий алгоритм: последовательно проверяем все нечетные числа, начиная с тройки (двойку рассматриваем отдельно). Делим очередное число на все простые числа, найденные на предыдущих шагах алгоритма и не превосходящие квадратного корня из проверяемого числа. Если оно не делится ни на одно из этих простых чисел, то само является простым; оно печатается и добавляется в массив найденных простых. Поскольку требуемое количество простых чисел n
до начала работы программы неизвестно, невозможно создать массив для их хранения в статической памяти. Выход состоит в том, чтобы захватывать пространство под массив в динамической памяти уже после ввода числа n
. Вот полный текст программы: #include Пример работы данной программы: Введите число простых: 50
2 3 5 7 11
13 17 19 23 29
31 37 41 43 47
53 59 61 67 71
73 79 83 89 97
101 103 107 109 113
127 131 137 139 149
151 157 163 167 173
179 181 191 193 197
199 211 223 227 229 В языке C++
для захвата и освобождения динамической памяти используются операторы new
и delete
. Они являются частью языка C++
, в отличие от функций malloc
и free
, входящих в библиотеку стандартных функций Си. Пусть T
- некоторый тип языка Си или C++
, p
- указатель на объект типа T
. Тогда для захвата памяти размером в один элемент типа T
используется оператор new
: T *p;
p = new T; Например, для захвата восьми байтов под вещественное число типа double
используется фрагмент double *p;
p = new double; При использовании new
, в отличие от malloc
, не нужно приводить указатель от типа void*
к нужному типу: оператор new
возвращает указатель на тип, записанный после слова new
. Сравните два эквивалентных фрагмента на Си и C++
. Последнее обновление: 28.05.2017 При создании массива с фиксированными размерами под него выделяется определенная память. Например, пусть у нас будет массив с пятью элементами: Double numbers = {1.0, 2.0, 3.0, 4.0, 5.0};
Для такого массива выделяется память 5 * 8 (размер типа double) = 40 байт. Таким образом, мы точно знаем, сколько в массиве элементов и сколько
он занимает памяти. Однако это не всегда удобно. Иногда бывает необходимо, чтобы количество элементов и соответственно размер выделяемой памяти
для массива определялись динамически в зависимости от некоторых условий. Например, пользователь сам может вводить размер массива. И в этом случае для
создания массива мы можем использовать динамическое выделение памяти. Для управления динамическим выделением памяти используется ряд функций, которые определены в заголовочном файле stdlib.h
: malloc()
. Имеет прототип Void *malloc(unsigned s);
Выделяет память длиной в s байт и возвращает указатель на начало выделенной памяти. В случае неудачного выполнения возвращает NULL
calloc()
. Имеет прототип Void *calloc(unsigned n, unsigned m);
Выделяет память для n элементов по m байт каждый и возвращает указатель на начало выделенной памяти. В случае неудачного выполнения возвращает NULL
realloc()
. Имеет прототип Void *realloc(void *bl, unsigned ns);
Изменяет размер ранее выделенного блока памяти, на начало которого указывает указатель bl, до размера в ns байт.
Если указатель bl имеет значение NULL
, то есть память не выделялась, то действие функции аналогично действию malloc
free()
. Имеет прототип Void *free(void *bl);
Освобождает ранее выделенный блок памяти, на начало которого указывает указатель bl. Если мы не используем эту функцию, то динамическая память все равно освободится автоматически при завершении работы программы. Однако все же
хорошей практикой является вызов функции free()
, который позволяет как можно раньше освободить память. Рассмотрим применение функций на простой задаче. Длина массива неизвестна и вводится во время выполнения программы пользователем, и также
значения всех элементов вводятся пользователем:
#include Консольный вывод программы: Size of array=5
block=23
block=-4
block=0
block=17
block=81
23 -4 0 17 81 Здесь для управления памятью для массива определен указатель block типа int
. Количество элементов массива заранее неизвестно,
оно представлено переменной n. Вначале пользователь вводит количество элементов, которое попадает в переменную n. После этого необходимо выделить память для данного количества
элементов. Для выделения памяти здесь мы могли бы воспользоваться любой из трех вышеописанных функций: malloc, calloc, realloc.
Но конкретно в данной ситуации воспользуемся функцией malloc
: Block = malloc(n * sizeof(int));
Прежде всего надо отметить, что все три выше упомянутые функции для универсальности возвращаемого значения в качестве результата возвращают указатель типа
void *
. Но в нашем случае создается массив типа int, для управления которым используется указатель типа int *
,
поэтому выполняется неявное приведение результата функции malloc к типу int *
. В саму функцию malloc передается количество байтов для выделяемого блока. Это количество подсчитать довольно просто: достаточно умножить количество элементов на размер одного элемента
n * sizeof(int) . После выполнения всех действий память освобождается с помощью функции free()
: Free(block);
Важно, что после выполнения этой функции мы уже не сможем использовать массив, например, вывести его значения на консоль: Free(block);
for(int i=0;i И если мы попытаемся это сделать, то получим неопределенные значения. Вместо функции malloc аналогичным образом мы могли бы использовать функцию calloc()
, которая принимает количество элементов и размер одного элемента: Block = calloc(n, sizeof(int));
Либо также можно было бы использовать функцию realloc()
: Int *block = NULL;
block = realloc (block, n * sizeof(int));
При использовании realloc желательно (в некоторых средах, например, в Visual Studio, обязательно) инициализировать указатель хотя бы значением NULL. Но в целом все три вызова в данном случае имели бы аналогичное действие: Block = malloc(n * sizeof(int));
block = calloc(n, sizeof(int));
block = realloc (block, n * sizeof(int));
Теперь рассмотрим более сложную задачу - динамическое выделение памяти для двухмерного массива:
#include Переменная table представляет указатель на массив указателей типа int*
. Каждый указатель table[i] в этом массиве представляет указатель на подмассив элементов типа int
, то есть отдельные строки таблицы.
А переменная table фактически представляет указатель на массив указателей на строки таблицы. Для хранения количества элементов в каждом подмассиве определяется указатель rows типа int
. Фактически он хранит количество столбцов для каждой строки таблицы. Сначала вводится количество строк в переменную rowscount . Количество строк - это количество указателей в массиве, на который указывает указатель table .
И кроме того, количество строк - это количество элементов в динамическом массиве, на который указывает указатель rows . Поэтому вначале необходимо для всех этих массивов
выделить память: Table = calloc(rowscount, sizeof(int*));
rows = malloc(sizeof(int)*rowscount);
Далее в цикле осуществляется ввод количества столбцов для каждый строки. Введенное значение попадает в массив rows. И в соответствии с введенным значением для каждой строки выделяется необходимый размер памяти: Scanf("%d", &rows[i]);
table[i] = calloc(rows[i], sizeof(int));
Затем производится ввод элементов для каждой строки. В конце работы программы при выводе происходит освобождение памяти. В программе память выделяется для строк таблицы, поэтому эту память надо освободить: Free(table[i]);
И кроме того, освобождается память, выделенная для указателей table и rows: Free(table);
free(rows);
Консольный вывод программы: Rows count=2
Columns count for 1=3
table=1
table=2
table=3
Columns count for 2=2
table=4
table=5
1 2 3
4 5 Итак. третий тип, самый интересный в этой теме для нас – динамический тип памяти. Как мы работали с массивами раньше? int a Как мы работаем сейчас?
Выделяем столько, сколько нужно: #include
<
stdio.h>
#include
<
stdlib.h>
int
main
() {
size_t
size;
//
Создаём указатель на int
//
– по сути, пустой массив.
int
*list;
scanf
("
%lu
"
, &size);
//
Выделяем память для size элементов размером int
//
и наш "пустой массив" теперь ссылается на эту память.
list = (int
*)malloc
(size * sizeof
(int
));
for
(int
i = 0
; i < size; ++i) {
scanf
("
%d
"
< size; ++i) {
printf
("
%d
"
, *(list + i));
}
//
Не забываем за собой прибраться!
free
(list);
} //
*
Void * malloc(size_t size);
Но в общем и целом это функция,
выделяет size байт неинициализированной памяти (не нули, а мусор). Если выделение прошло успешно, то возвращается указатель на самый первый байт выделенной памяти. Если неуспешно – NULL. Также errno будет равен ENOMEM (эту замечательную переменную
мы рассмотрим позднее).
То есть правильнее было написать: #include
<
stdio.h>
#include
<
stdlib.h>
int
main
() {
size_t
size;
int
*list;
scanf
("
%lu
"
, &size);
list = (int
*)malloc
(size * sizeof
(int
));
if
(list == NULL
) {
goto
error;
}
for
(int
i = 0
; i < size; ++i) {
scanf
("
%d
"
, list + i);
}
for
(int
i = 0
; i < size; ++i) {
printf
("
%d
"
, *(list + i));
}
free
(list);
return
0
;
error:
return
1
;
} //
*
Очищать NULL указатель не нужно #include
<
stdlib.h>
int
main
() {
free
(NULL
);
} – в том же clang всё пройдёт нормально (сделает ничто), но в более экзотических
случаях вполне может крэшнуть программу. Рядом с malloc и free в мане можно увидеть ещё: void * calloc (size_t count, size_t size); Равно как и malloc выделит память под count объектов размером по size байт.
Выделяемая память инициализируется нулями. void * realloc (void *ptr, size_t size); Перевыделяет (если может) память, на которую указывает ptr , в размере size байт.
Если не хватает места для увеличения выделенной памяти, на которое указывает ptr , realloc создает новое выделение (аллокацию),
копирует старые данные, на которые указывает ptr ,
освобождает старое выделение и возвращает указатель на выделенную память. Если ptr равен NULL , realloc идентичен вызову malloc . Если size равен нулю, а ptr не NULL , выделяется кусок памяти
минимального размера, а исходная освобождается. void * reallocf (void *ptr, size_t size); Придумка из FreeBSD API. Как и realloc , но если не сможет перевыделить, очищает
принятый указатель. void * valloc (size_t size); Как и malloc , но выделенная память выравнивается по границе страницы.
Рис.
24.1.
Работа с динамической памятью с помощью операций new и delete
Глобальные переменные называются так потому, что они доступны в любой точке программы во всех ее файлах. Поэтому имена глобальных переменных должны быть достаточно длинными, чтобы избежать случайного совпадения имен двух разных переменных. Например, имена x
или n
для глобальной переменной не подходят;Стековая, или локальная, память
Динамическая память, или куча
адрес
содержимое памяти
...
динамическая память
стек
Пример: печать n первых простых чисел
Операторы new и delete языка C++