notes

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

Мотивация

  1. Стек программы ограничен. Он не предназначен для хранения больших объёмов данных
     // не умещается на стек
     double m[100000000] = {}; // 80 Mb
     // => segmentation fault or access violation error
    
  2. Время жизни локальных переменных ограниченно временем работы функции
  3. Динамическая память выделяется в сегменте данных.
  4. Структура, отвечающая за выделение дополнительной памяти, называют кучей.

Выделение памяти в стиле С

  1. Стандартная библиотека cstdlib предоставляет четыре функции для управления памятью:

     void * malloc (size_t size);
     void free (void * ptr);
     void * calloc (size_t nmemb, size_t size);
     void * realloc (void * ptr, size_t size);
    
  2. size_t - специальный целочисленный беззнаковый тип, может вместить в себя размер любого типа в байтах. ( int‘a не всегда достаточо)
  3. Тип size_t используется для указания размеров типов данных, для индексации массивов и пр.
  4. void * - это указатель на нетипизированную память (раньше для этого использовалось char *)
  5. malloc - выделяет область памяти размера >= size. Не кратный размер памяти выделяется по следующим причинам
    • вначало участка памяти записывается служебная информация
    • участок памяти выделяется кратным 16
  6. calloc - выделяет массив из nmemb размера size. Данные инициализируются нулём.
  7. realloc - изменяет размер области памяти по указателю ptr на size (если возможно, то это делается на месте).
  8. free - освобождает облать памяти, ранее выделенную одной из функций malloc/calloc/realloc.
  9. Для указания размера типа используется оператор sizeof.
     // create an array of 1000 int
     int * m  = (int *)malloc(1000 * sizeof(int));
     m[10] = 10;
    
     // change array size to 2000
     m = (int *)realloc(m, 2000 * sizeof(int));
    
     // free memory
     free (m);
    
     // create an array of int and inicialize it with 0
     m = (int *)calloc(3000, sizeof(int));
    
     free(m);
     m = 0; // best practise
    

Выделение памяти в стиле С++

  1. Язык С++ предоставляет два набора операторов для выделения памяти:
    • new и delete - для одиночных значений,
    • new[] и delete[] - для массивов.
  2. Версия оператора delete должна соответствовать версии оператора new.
     // выделение памяти под один int со значением 5
     int * m = new int(5);
     delete m; // освобождение памяти
    
     // создание массива нулей
     m = new int[1000];
     delete [] m; // освобождение памяти
    

Типичные проблемы при работе с памятью

  1. Проблемы производительности: создание переменной на стеке намного “дешевлу” выделения для неё динамической памяти.
  2. Проблема фрагментации: выделение большого количества небольших сегментов способствует фрагментации памяти.
  3. Утечки памяти
     int * m = new int[1000];
    
     m = new int[2000]; // потеряли указатель но массив из 1000 элементов => утечка памяти
    
     // не вызван delete [] m => утечка памяти
    
  4. Неправильное освобождение памяти
     int * m1 = new int[1000];
     delete m1; // должно быть delete [] m1;
    
     int *p = new int(0);
     free(p); // совмещение функций С++ и С
    
     int *q1 = (int*) malloc (sizeof(int));
     free(q1);
     free(q1); // двойное удаление
    
     int *q2 = (int*) malloc(sizeof(int));
     free(q2);
     q2 = 0;
     free(q2); // правильно работает для q2 = 0;