Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Материалы для подготовки к экзамену по ОП.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
298 Кб
Скачать
☆

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

malloc

Итак, уже обсуждалось, что локальные переменные кладутся на стек и существуют до тех пор, пока мы не вышли из функции. С одной стороны, это позволяет автоматически очищать память, с другой стороны, существует необходимость в переменных, время жизни которых мы можем контролировать самостоятельно. Кроме того, нам необходимо динамическое выделение памяти, когда размер используемого пространства заранее не известен. Для этого используется выделение памяти на куче. Недостатков у такого подхода два:

  • во-первых, память необходимо вручную очищать,

  • во-вторых, выдеение памяти – достаточно дорогостоящая операция.

Для выделения памяти на куче в си используется функция malloc (memory allocation) из библиотеки stdlib.h

void * malloc(size_t size);

Функция выделяет size байтов памяти и возвращает указатель на неё. Если память выделить не удалось, то функция возвращает NULL. Так как malloc возвращает указатель типа void, то его необходимо явно приводить к нужному нам типу. Например, создадим указатель, после этого выделим память размером в 100 байт.

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

void main() {

int *p = NULL;

p = (int*) malloc(100); /* индексация через 4байта */

free(p);

}

После того, как мы поработали с памятью, необходимо освободить память функцией free.

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

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

void main() {

const int maxNumber = 100;

int *p = NULL;

unsigned i, size;

do {

printf("Enter number from 0 to %d: ", maxNumber);

scanf("%d", &size);

if (size < maxNumber) {

break;

}

} while (1);

p = (int*) malloc(size * sizeof(int));

for (i = 0; i < size; i++) {

p[i] = i*i;

}

for (i = 0; i < size; i++) {

printf("%d ", p[i]);

}

_getch();

free(p);

}

Разбираем код

p = (int*) malloc(size * sizeof(int));

Здесь (int *) – приведение типов. Пишем такой же тип, как и у указателя.

size * sizeof(int) – сколько байт выделить. sizeof(int) – размер одного элемента массива.

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

Теперь представим на рисунке, что у нас происходило. Пусть мы ввели число 5.

Выделение памяти.

Выделение памяти.

Функция malloc выделила память на куче по определённому адресу, после чего вернула его. Теперь указатель p хранит этот адрес и может им пользоваться для работы. В принципе, он может пользоваться и любым другим адресом.

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

Это очень похоже на съём номера в отеле. Мы получаем дубликат ключа от номера, живём в нём, а потом сдаём комнату обратно. Но дубликат ключа у нас остаётся. Всегда можно зайти в этот номер, но в нём уже кто-то может жить. Так что наша обязанность – удалить дубликат.

Иногда думают, что происходит "создание" или "удаление" памяти. На самом деле происходит только перераспределение ресурсов.