Файл: Тема 11. 1 Динамическая память CC Выделение памяти malloc().pptx

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 23.11.2023

Просмотров: 27

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Тема 11.1 Динамическая память C/C++

Выделение памяти – malloc()


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

Пример кода

#include //файл с описанием функции

...

int *x; //указатель int n = 100; //кол-во элементов в массиве

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

x = (int*)malloc(n*sizeof(int));

for(int i = 0; i < 100; i++) //обнуление

x[i] = 0; //памяти

Выделение памяти – calloc()


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

Пример кода

#include //файл с описанием функции

...

int *x; //указатель int n = 100; //кол-во элементов массива

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

x = (int*)calloc(n, sizeof(int));

//for(int i = 0; i < 100; i++) память обнуляется

// x[i] = 0; calloc()

Освобождение памяти – free()


Для освобождения ранее выделенной памяти в С используется функция free(), которая принимает указатель на область, подлежащую освобождению, или NULL. Функция не проверяет указатель на правильность и может ошибочно «освободить» невыделенную область.

Пример кода

int *x; int n = 100;

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

//с заполнением её нулями

if(x = (int*)calloc(n, sizeof(int))) { x[0] = 10; ... free(x); //удаление динамической памяти

}

Перераспределение памяти – realloc()


Для изменения размера ранее выделенной памяти в С используется библиотечная функция realloc(), которая пытается увеличить блок памяти. Если это не удалось, то функция попытается выделить новый блок памяти необходимого размера, в который будут перенесены старые данные. Если это тоже не удалось, то функция вернет NULL.

Пример кода

int *x;

x = (int*)malloc(100*sizeof(int));

... x = (int*)realloc(x, 500*sizeof(int)); ... free(x);

Динамическая память в С++


В С++ для динамического выделения памяти используются операторы new и new[ ]. А для освобождения памяти – операторы
delete и delete[ ]. Варианты с квадратными скобками применяются для выделения/освобождения памяти массива. Если память выделить невозможно, функция new выкидывает исключение или возвращает NULL (зависит от настроек менеджера памяти).

Пример кода

double *a = new double; int *x = new int[100]; Product *p = new Product; ... delete a; //удаление переменной delete[] x; //удаление массива delete p; //удаление переменной

Нельзя смешивать концепции malloc/free и new/delete.

Основные ошибки при работе с динамической памятью

  • Использование неинициализированного указателя.
  • "Висячие" указатели.
  • "Утечка" памяти.
  • Повторное освобождение памяти.
  • Ошибочное применение delete вместо delete[ ].

Неинициализированный указатель


Пример кода

int *x; //имеет смысл обнулить указатель x = NULL;

//"друзья" закомментировали следующую сточку

//x = (int*)malloc(100*sizeof(int));

x[0] = 10;

Если локальный указатель не инициализирован, то он содержит непредсказуемое значение. Это значение трактуется как адрес целой переменной, к которой осуществляется доступ. По чистой случайности может оказаться, что указатель x содержит истинный адрес переменной программы, тогда значение переменной будет изменено, выполнение программы продолжится дальше, а факт изменения переменной непредсказуемым образом повлияет на дальнейшее выполнение программы.

"Висячие" указатели


Пример кода

int *x = NULL;

x = (int*)malloc(100*sizeof(int));

...

free(x); //после удаления памяти имеет смысл //обнулить указатель x = NULL;

x[0] = 10;

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

"Утечка" памяти


Пример кода

int func(int N) {

int *x = (int*)malloc(N*sizeof(int));

x[0] = 10;

...

return x[0];

} // выход из функции без удаления памяти

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


Повторное освобождение памяти


Пример кода

int *x = NULL;

x = (int*)malloc(100*sizeof(int));

...

free(x);

...

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

Данная ошибка возникает, когда происходит освобождение одного и того же выделенного блока памяти несколько раз. Как правило, такая ошибка приводит к нарушению работы менеджера памяти и непредсказуемым последствиям.

double* cpp_realloc(const double* ptr,

unsigned new_size, unsigned old_size) {

if (ptr == NULL && new_size != 0)

return new double[new_size];

if (new_size == 0) {

if (ptr != NULL) delete [] ptr;

return NULL; }

double* tmp = new double[new_size];

for(unsigned i = 0; i < old_size; i++)

tmp[i] = ptr[i];

delete [] ptr;

return tmp;}

Пример realloc для С++

Пример функции

Двумерные динамические массивы

Функции malloc(), calloc() и оператор new[ ] позволяют выделять только одномерные массивы, поэтому для работы с двумерными массивами необходимо либо дополнительно создавать массив указателей, либо пересчитывать двумерные индексы в индексы для одномерного массива.

Пример кода

//захват памяти для массива NxM

int **a = new int*[N];

for (int i = 0; i < N; ++i) a[i] = new int[M];

//обращения к массиву стандартное

a[2][2] = 5;

//освобождение памяти

for (int i = 0; i < N; ++i) delete [] a[i];

delete [] a;

Двумерные массив через одномерный

Пример кода

int N = 3, M = 4;

int *a = new int[N*M];

//массив хранится по строчкам

for(int i = 0; i < N; i++)

for(int j = 0; j < M; j++)

a[i*M+j] = i+1;

//вывод массива на экран

for(int i = 0; i < N; i++) {

for(int j = 0; j < M; j++)

cout << a[i*M+j] << "\t";

cout << endl;

}