среда, 16 июля 2008 г.

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

Данная статья посвящена тем, кто недавно только начал изучать С (С++). Здесь я хочу более или менее понятно объяснить то, как создавать динамические массивы на примере одно- и двумерных массивов, показать из связь с указателями. Итак, классическое задание - создать динамический массив. Кто не сталкивался с этим в начале изучени языка? Поехали...
Как должно быть уже известно, имя масива фактически является указателем на первый элемент.
Т.е. если имеем
int *ptrArr;
int Arr[10];
то можем записать следующее
ptrArr = &Arr[0];
или вот так
ptrArr = Arr;
теперь ptrArr содержит адрес первого элемента массива Arr
И если теперь записать
int x =*ptrArr

то в x запишется значение первого элемента массива
К элементу массива мы можем обращаться как с помощью индекса Arr[i],так и с помощью указателя *(Arr+i), либо в нашем случае еще и *(ptrArr+i). Выражение Arr[i] автоматически преобразуется к *(Arr+i).
После такого вступления мы уже можем создать динамический массив. Для этого будем использовать функцию выделения памяти calloc(), которая имеет следующий прототип
void *calloc (size_t num, size_t size)

Функция выделяеи num*size байтов и возвращает указатель на первый элемент выделенной области либо null, если выделит память невозможно. Для использования необходимо подключить заголовок
#include <cstdlib>

Создадим динамический массив на 10 элементов:
int *p;
p = (int*)calloc(10,sizeof(int)); // Выделяем память
//... // какой-либо код
free (p); // удалим массив, если он нам больше не нужен


Как видно из примеры, мы взяли указатель и выделили под него память на 10 int-значений. Вот так просто создаются одномерные массивы.
Теперь перейдем к двумерным. Пусть нам требуется создать массив 3*4 (3 строки, 4 столбца).
Двумерный массив мы можем представить как "массив одномерных массивов", т.е. как одномерный массив, элементами которого являеются так же одномерные массивы. Далее, следует, если у нас будет несколько массивов (элементов), стало быть для каждого из них должен быть свой указатель, а это уже получается, что мы должны иметь массив указателей. Этот массив указателей очевидно одномерный и надо его создать, а создавать одномерные динамические массивы мы уже умеем. Но тут есть дно "но"!
В предыдущем примере мы заводили массив int-элементов и указатель int* на первый элемент, а здесь у нас элементами являются указатели на int - int*, и тогда указатель на первый элемент у нас будет уже указателем на ... указатель! т.е. будет иметь тип int**. Стало быть под него надо выделить память
int **m; // указатель на указатель на int
m = (int**)calloc(3,sizeof(int*));

Итак, массив указателей создан и он где-то в памяти. Теперь надо под каждый из этих указателей выделить память, соответствующую второй размерность. Это мы делать умеем.
for (int i=0; i<3; i++)
*(m+i) = (int*)calloc(4,sizeof(int));
Обратите внимание на запись *(m+i). Как говорилось выше, мы используем доступ к элементу массива с помощью указателя. Получив доступ к этому элементу массива указателей, мы можем выделить память и после этого перейти к следующему элементу. Можно бы было написать и так
m[i] = (int*)calloc(4,sizeof(int));
Для удаления массива нужно проделать обратные действия: удалить массив под каждый указатель
for (int i=0; i<3; i++)
free(*(m+i)); затем нужно удалить сам массив указателей
free(m);

Теперь мы можем создавать двумерные динамические массивы. В заключении хотелось бы отметить, что таким образом мы можем создавать динамические массивы, размерность которых больше двух. Допустим трехмерный массив можно представить как одномерный массив двумерных массивов, ну а с двумерныи массивами мы уже разобрались! Теперь всё!

2 комментария :

sadgb комментирует...

вероятно в коде

И если теперь записать
x = &ptr

то в x запишется значение первого элемента массива

Имелось в виду
x = *ptrArr

Руслан комментирует...

Спасибо, поправил.