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

Освобождение памяти с помощью free

Теперь рассмотри, как происходит освобождение памяти. Переменная указатель хранит адрес области памяти, начиная с которого она может им пользоваться. Однако, она не хранит размера этой области. Откуда тогда функция free знает, сколько памяти необходимо освободить?

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

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

  • Второе решение более распространено. Информация о размере хранится на куче до самих данных. Таким образом, при выделении памяти резервируется места больше и туда записывается информация о выделенном участке. При освобождении памяти функция free "подсматривает", сколько памяти необходимо удалить.

Функция free освобождает память, но при этом не изменяет значение указателя, о чём нужно помнить.

Работа с двумерными и многомерными массивами

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

Для удаления массива необходимо повторить операцию в обратном порядке - удалить сначала подмассивы, а потом и сам массив указателей.

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#define COL_NUM 10

#define ROW_NUM 10

void main() {

float **p = NULL;

unsigned i;

p = (float**) malloc(ROW_NUM * sizeof(float*));

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

p[i] = (float*) malloc(COL_NUM * sizeof(float));

}

//Здесь какой-то важный код

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

free(p[i]);

}

free(p);

}

Сначала мы создаём массив указателей, а после этого каждому элементу этого массива присваиваем адрес вновь созданного массива. Это значит, что можно

  • Создавать массивы "неправильной формы", то есть массив строк, каждая из которых имеет свой размер.

  • Работать по отдельности с каждой строкой массива: освобождать память или изменять размер строки.

Создадим "треугольный" массив и заполним его значениями

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#define SIZE 10

void main() {

int **A;

int i, j;

A = (int**) malloc(SIZE * sizeof(int*));

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

A[i] = (int*) malloc((i + 1) * sizeof(int));

}

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

for (j = i; j > 0; j--) {

A[i][j] = i * j;

}

}

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

for (j = i; j > 0; j--) {

printf("%d ", A[i][j]);

}

printf("\n");

}

for (i = SIZE-1; i > 0; i--) {

free(A[i]);

}

free(A);

_getch();

}

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

calloc

Функция calloc выделяет n объектов размером m и заполняет их нулями. Обычно она используется для выделения памяти под массивы. Синтаксис

void* calloc(size_t num, size_t size);

realloc

Ещё одна важная функция – realloc (re-allocation). Она позволяет изменить размер ранее выделенной памяти и получает в качестве аргументов старый указатель и новый размер памяти в байтах:

void* realloc(void* ptr, size_t size)

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

Пример – пользователь вводит слова. Для начала выделяем под слова массив размером 10. Если пользователь ввёл больше слов, то изменяем его размер, чтобы хватило места. Когда пользователь вводит слово end, прекращаем ввод и выводим на печать все слова.

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define TERM_WORD "end"

#define SIZE_INCREMENT 10

void main() {

//Массив указателей на слова

char **words;

//Строка, которая используется для считывания введённого пользователем слова

char buffer[128];

//Счётчик слов

unsigned wordCounter = 0;

//Длина введённого слова

unsigned length;

//Размер массива слов. Для уменьшения издержек на выделение памяти

//каждый раз будем увеличивать массив слов не на одно значение, а на

//SIZE_INCREMENT слов

unsigned size = SIZE_INCREMENT;

int i;

//Выделяем память под массив из size указателей

words = (char**) malloc(size*sizeof(char*));

do {

printf("%d: ", wordCounter);

scanf("%127s", buffer);

//Функция strcmp возвращает 0, если две строки равны

if (strcmp(TERM_WORD, buffer) == 0) {

break;

}

//Определяем длину слова

length = strlen(buffer);

//В том случае, если введено слов больше, чем длина массива, то

//увеличиваем массив слов

if (wordCounter >= size) {

size += SIZE_INCREMENT;

words = (char**) realloc(words, size*sizeof(char*));

}

//Выделяем память непосредственно под слово

//на 1 байт больше, так как необходимо хранить терминальный символ

words[wordCounter] = (char*) malloc(length + 1);

//Копируем слово из буффера по адресу, который

//хранится в массиве указателей на слова

strcpy(words[wordCounter], buffer);

wordCounter++;

} while(1);

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

printf("%s\n", words[i]);

}

_getch();

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

free(words[i]);

}

free(words);

}

Хочу обратить внимание, что мы при выделении памяти пишем sizeof(char*), потому что размер указателя на char не равен одному байту, как размер переменной типа char.