Динамическое выделение памяти в C: malloc(), calloc()

⚡ Умное резюме

Динамическое выделение памяти в C резервирует и освобождает память кучи во время выполнения с помощью функций malloc(), calloc(), realloc() и free() из библиотеки stdlib.h, позволяя программам гибко определять размер массивов и структур, когда требования неизвестны до начала выполнения.

  • ???? Стек против кучи: Базовые переменные используют автоматический стек, в то время как динамическая память находится в куче и управляется вручную с помощью указателей.
  • 📦 маллок(): Резервирует один неинициализированный блок запрошенного размера в байтах и ​​возвращает указатель типа void или NULL, если памяти недостаточно.
  • 🔢 calloc (): Выделяет несколько блоков для массивов, обнуляет каждый байт и возвращает указатель на первый блок.
  • 🔄 realloc(): Изменяет размер существующего блока в большую или меньшую сторону, сохраняя при этом исходное содержимое, возможно, в новом месте.
  • 🧹 бесплатно(): Возвращает память в кучу; для предотвращения утечек памяти при каждом вызове malloc() или calloc() требуется соответствующий вызов free().
  • 🤖 Помощь ИИ: Искусственный интеллект-помощники и GitHub Copilot разрабатывают черновой код распределения ресурсов и отмечают отсутствующие вызовы функции free() и утечки памяти.

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

Как работает управление памятью в C?

При объявлении переменной с использованием базового типа данных компилятор C автоматически выделяет для неё память в пуле памяти, называемом стеком.

Например, переменная типа float обычно занимает 4 байта (в зависимости от платформы) при объявлении. Мы можем проверить эту информацию, используя оператор sizeof, как показано в примере ниже.

#include <stdio.h>
int main() { float x; printf("The size of float is %d bytes", sizeof(x)); return 0;}

Выходной сигнал будет:

 The size of float is 4 bytes

Также массив заданного размера размещается в смежных блоках памяти, каждый блок имеет размер для одного элемента:

#include <stdio.h>
int main() { float arr[10];
printf("The size of the float array with 10 element is %d", sizeof(arr)); return 0;}

Результат:

 The size of the float array with 10 element is 40

Как уже было изучено, при объявлении базового типа данных или массива управление памятью происходит автоматически. Однако в языке C существует процесс выделения памяти, который позволяет реализовать программу, в которой размер массива не определен до момента запуска программы (во время выполнения). Этот процесс называется «динамическим выделением памяти».

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

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

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

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

Функция Цель
таНос () Выделяет память запрошенного размера и возвращает указатель на первый байт
выделенное пространство.
calloc () Выделяет место для элементов массива. Инициализирует элементы нулями и возвращает указатель на память.
realloc () Он используется для изменения размера ранее выделенного пространства памяти.
Бесплатно() Освобождает или очищает ранее выделенное пространство памяти.

Давайте обсудим вышеуказанные функции с их применением.

Функция malloc() в C

Функция C malloc() обозначает распределение памяти. Это функция, которая используется для динамического выделения блока памяти. Он резервирует пространство памяти указанного размера и возвращает нулевой указатель, указывающий на ячейку памяти. Возвращаемый указатель обычно имеет тип void. Это означает, что мы можем назначить функцию C malloc() любому указателю.

Синтаксис функции malloc():

ptr = (cast_type *) malloc (byte_size);

Здесь,

  • ptr — это указатель cast_type.
  • Функция C malloc() возвращает указатель на выделенную память размером byte_size.

Пример функции malloc():

Example: ptr = (int *) malloc (50)

При успешном выполнении этого оператора резервируется пространство памяти размером 50 байт. Адрес первого байта зарезервированного пространства присваивается указателю ptr типа int.

Рассмотрим другой пример:

#include <stdlib.h>
int main(){
int *ptr;
ptr = malloc(15 * sizeof(*ptr)); /* a block of 15 integers */
    if (ptr != NULL) {
      *(ptr + 5) = 480; /* assign 480 to sixth integer */
      printf("Value of the 6th integer is %d",*(ptr + 5));
    }
}

Выход:

Value of the 6th integer is 480

Функция C malloc()

  1. Обратите внимание, что вместо sizeof(int) использована функция sizeof(*ptr), чтобы сделать код более устойчивым при последующем преобразовании типа объявления *ptr в другой тип данных.
  2. Выделение может завершиться неудачей, если памяти недостаточно. В этом случае он возвращает NULL-указатель. Итак, вам следует включить код для проверки NULL-указателя.
  3. Имейте в виду, что выделенная память является непрерывной и ее можно рассматривать как массив. Мы можем использовать арифметику указателей для доступа к элементам массива вместо использования скобок [ ]. Мы советуем использовать + для ссылки на элементы массива, поскольку использование приращения ++ или += изменяет адрес, хранящийся в массиве. указатель.

Функцию Malloc() также можно использовать с символьным типом данных, а также со сложными типами данных, такими как структуры.

функция free() в C

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

Функция free() вызывается для освобождения/освобождения памяти в языке C. Освобождая память в программе, вы освобождаете больше памяти для последующего использования.

Например:

#include <stdio.h>
int main() {
int* ptr = malloc(10 * sizeof(*ptr));
if (ptr != NULL){
  *(ptr + 2) = 50;
  printf("Value of the 2nd integer is %d",*(ptr + 2));
}
free(ptr);
}

Выход:

 Value of the 2nd integer is 50

функция calloc() в C

Функция C calloc() означает непрерывное выделение. Эта функция используется для выделения нескольких блоков памяти. Это функция динамического выделения памяти, которая используется для выделения памяти для сложных структур данных, таких как массивы и структуры.

Функция Malloc() используется для выделения одного блока памяти, а функция calloc() в C используется для выделения нескольких блоков памяти. Каждый блок, выделенный функцией calloc(), имеет одинаковый размер.

Синтаксис функции calloc():

ptr = (cast_type *) calloc (n, size);

Приведенный выше оператор используется для выделения n блоков памяти одинакового размера.

После выделения пространства памяти все байты инициализируются нулями.

Возвращается указатель, который в данный момент находится на первом байте выделенного пространства памяти.

Всякий раз, когда возникает ошибка при выделении памяти, например нехватка памяти, возвращается нулевой указатель.

Пример вызова():

Программа ниже вычисляет сумму арифметической последовательности.

#include <stdio.h>
    int main() {
        int i, * ptr, sum = 0;
        ptr = calloc(10, sizeof(int));
        if (ptr == NULL) {
            printf("Error! memory not allocated.");
            exit(0);
        }
        printf("Building and calculating the sequence sum of the first 10 terms \ n ");
        for (i = 0; i < 10; ++i) { * (ptr + i) = i;
            sum += * (ptr + i);
        }
        printf("Sum = %d", sum);
        free(ptr);
        return 0;
    }

Результат:

Building and calculating the sequence sum of the first 10 terms
Sum = 45

calloc() и malloc(): ключевые различия

Ниже приводится ключевое различие между malloc() против calloc() в C:

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

Блок памяти, выделенный вызовом calloc() в C, всегда инициализируется нулем, тогда как в функции malloc() в C он всегда содержит мусорное значение.

Функция realloc() в C

С помощью функции realloc() в языке C можно увеличить объем уже выделенной памяти. Она расширяет текущий блок, оставляя при этом исходное содержимое неизменным. realloc() в языке C означает перераспределение памяти.

Realloc() также можно использовать для уменьшения размера ранее выделенной памяти.

Синтаксис функции realloc():

ptr = realloc (ptr,newsize);

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

Пример realloc():

#include <stdio.h>
int main () {
   char *ptr;
   ptr = (char *) malloc(10);
   strcpy(ptr, "Programming");
   printf(" %s,  Address = %u\n", ptr, ptr);

   ptr = (char *) realloc(ptr, 20); //ptr is reallocated with new size
   strcat(ptr, " In 'C'");
   printf(" %s,  Address = %u\n", ptr, ptr);
   free(ptr);
   return 0;
}

Всякий раз, когда функция realloc() в C приводит к неудачной операции, она возвращает нулевой указатель, а предыдущие данные также освобождаются.

Динамические массивы в C

Динамический массив в C позволяет увеличивать количество элементов по мере необходимости. Динамические массивы C широко используются в алгоритмах информатики.

В следующей программе мы создали и изменили размер динамического массива в C

#include <stdio.h>
    int main() {
        int * arr_dynamic = NULL;
        int elements = 2, i;
        arr_dynamic = calloc(elements, sizeof(int)); //Array with 2 integer blocks
        for (i = 0; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        elements = 4;
        arr_dynamic = realloc(arr_dynamic, elements * sizeof(int)); //reallocate 4 elements
        printf("After realloc\n");
        for (i = 2; i < elements; i++) arr_dynamic[i] = i;
        for (i = 0; i < elements; i++) printf("arr_dynamic[%d]=%d\n", i, arr_dynamic[i]);
        free(arr_dynamic);
    }

Результат программы динамического массива C на экране:

 
arr_dynamic[0]=0
arr_dynamic[1]=1
After realloc
arr_dynamic[0]=0
arr_dynamic[1]=1
arr_dynamic[2]=2
arr_dynamic[3]=3

Часто задаваемые вопросы (FAQ)

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

Висячий указатель по-прежнему удерживает адрес памяти, которую уже освободила функция free(). Разыменование его приводит к неопределенному поведению, поэтому для безопасности сразу после освобождения присвойте указателю значение NULL.

Нет. В языке C нет сборщика мусора, поэтому память кучи никогда не освобождается автоматически. Программист должен явно освобождать каждый блок с помощью функции free(); в противном случае выделенная память сохраняется до завершения программы.

Нет. Двойной вызов функции free() для одного и того же адреса является двойным освобождением памяти и приводит к неопределенному поведению или сбою. Установка указателя в NULL после первого вызова free() делает любое последующее освобождение памяти безвредным.

Функция realloc(NULL, size) работает точно так же, как malloc(size), и выделяет новый блок памяти. Функция realloc(ptr, 0) освобождает блок, поэтому всегда захватывайте возвращаемый указатель, а не перезаписывайте исходный.

Такие инструменты, как Valgrind, AddressSanitizer и Dr. Memory. track каждое выделение и отчет о блоках, которые так и не были освобождены, вместе с точной строкой, helping Вы обнаруживаете утечки до выпуска.

Да. Искусственный интеллект-помощник в программировании создает последовательности вызовов malloc(), calloc() и free() на основе подсказки, объясняет ошибки указателей и отмечает отсутствующие вызовы free(). Всегда компилируйте и тестируйте сгенерированный код, прежде чем доверять ему.

Второй пилот GitHub Предлагаются блоки выделения памяти, проверки на NULL и соответствующие вызовы free() из комментария; перед выпуском проверьте каждое завершение на наличие утечек памяти и правильные размеры.

Подведем итог этой публикации следующим образом: