Массивы указателей и моделирование многомерных массивов
Хотя синтаксисом языка С массив определяется как одномерная совокупность его элементов, но каждый элемент может быть, в свою очередь, массивом. Именно так конструируются многомерные массивы. Язык С не накладывает явных ограничений на размерность массива, однако каждая реализация обычно вводит такое ограничение. Максимальное значение размерности массивов определяет константа (препроцессорная), определенная в заголовочном файле стандартной библиотеки. Обычно это предельное значение размерности массивов устанавливается равным 12.
Очевидного способа прямого определения многомерных массивов с несколькими переменными размерами в языке С не существует. Решение, как и для случая одномерных массивов динамической памяти, находится в области системных средств (библиотечных функций) для динамического управления памятью. Но прежде чем рассматривать их возможности для многомерных массивов, необходимо ввести массивы указателей.
Массив указателей фиксированных размеров вводится одним из следующих определений:
тип * имя_массива [размер];
тип * имя_массива [ ]=инициалшатор;
тип * имя_массива [размер]=инициализатор;
где тип может быть как одним из базовых типов, так и производным типом;
имя_массива – свободно выбираемый идентификатор;
размер – константное выражение, вычисляемое в процессе трансляции;
инициализатор – список в фигурных скобках значений типа тип *.
Примеры:
int data[6]; /*обычный массив*/
int *pd[6]; /* массив указателей */
int *pi[ ]={&data[0], &data[4], &data[2] };
Здесь каждый элемент массивов pd и pi является указателем на объекты типа int. Значением каждого элемента pd[j] и pi[k] может быть адрес объекта типа int. Все 6 элементов массива pd указателей типа int * не инициализированы. В массиве pi три элемента, которые инициализированы адресами конкретных элементов массива data.
Интересные возможности массива указателей появляются в тех случаях, когда его элементы адресуют либо элементы другого массива, либо указывают на начало одномерных массивов соответствующего типа. В таких случаях очень легко решаются задачи сортировки сложных объектов с разными размерами. Например, можно ввести массив указателей, адресовать с помощью его элементов строки матрицы и затем решать задачи упорядочения строк матрицы, не переставляя строки двумерного массива, представляющего матрицу. Если с двумерным массивом связать несколько одномерных массивов указателей, то можно упорядочить строки матрицы одновременно по разным правилам, "добираясь" до строк с помощью разных массивов указателей.
"Матрица" со строками разной длины Массив в языке Си должен иметь элементы одного типа и, естественно, одного размера. В ряде случаев возникает необходимость обрабатывать, подобно элементам массива, объекты разного размера. Например, попытка оформить в виде двумерного массива строки чисел с разным количеством числовых значений при обычном подходе потребует определить двумерный массив с максимально допустимыми размерами. Наличие более коротких строк не может уменьшить общих размеров массива – излишние требования к памяти налицо. Использование массивов указателей и средств динамического выделения памяти позволяет обойти указанные затруднения и более рационально распределить память. Для иллюстрации этих возможностей рассмотрим следующую задачу.
Необходимо ввести и распечатать в обратном порядке набор строк числовых значений. Количество строк в наборе вводится в начале работы программы, а длина каждой строки (т.е. количество чисел – элементов в ней) вводится перед каждой последовательностью числовых значений элементов строки.
Программа для решения задачи может быть такой:
#include <stdio.h>
#include <alloc.h>
void main( ){
double **line; /* line - указатель для блока памяти, выделяемого для массива указателей */
int i,j,n;
double dd;
int * m; /* Указатель для блока памяти */
printf("\п\пВведите количество строк n=");
scanf("%d",&n);
line=(double* *)calloc(n,sizeof(double*));
m=(int *)malloc(sizeof(int)*n) ;
/* Цикл по количеству строк*/
for (i=0 ; i<n ; i++){
printf ( "Введите длину строки m[%d]=",i};
scanf ("%d",&m[i]) ;
line [i]= (double* )calloc (m[i] , sizeof (double));
for(j=0;j<m[i] ; j++){
printf ("line [%d] [%d]=",i, j) ;
scanf ("%le",&dd) ;
line[i][j]=dd;
}
}
printf ("\nРезультаты обработки: ") ;
for(i=n-l;i>=0;i -- ){
printf ("\nСтрока %d, элементов %d:\n",i,m[i] ) ;
for(j=0;j<m[i] ;j++)
printf ("\t%f", line[i][j]);
free(line[i] ); /* Освободить память строки */
}
/* Освободить память от массива указателей: */
free (line) ;
/* Освободить память от массива int: */
free(m);}
В программе использованы:
line – указатель на массив указателей типа double *, каждый из которых, т.е. line[i], адресует динамически выделяемый участок памяти длиной в m[i] элементов типа double.
m – указатель на массив целых (int), значение каждого элемента равно длине массива, на который указывает line[i].
Для иллюстрации разных функций выделения памяти в программе использованы calloc() и malloc( ). Различие между ними заключается в количестве и смысле параметров, а также в том, что функция calloc() обнуляет содержимое выделенного блока памяти.
При выходе из программы все блоки динамически выделяемой памяти рекомендуется явным образом освобождать. Для этих целей используется несколько вызовов функции free(). В цикле по i после печати очередной строки функция free (line[i]) освобождает участок памяти, адресуемой указателем line[i]. После окончания цикла освобождаются блоки, выделенные для массива указателей free(line) и массива целых free(m).
Все выражения в С, содержащие обращения к массивам, переводятся в выражения с указателями. Выражение a[6] эквивалентно выражению с указателем *(a+6). Выражение a[2][3] эквивалентно выражению с указателем *(*(a+2)+3). Многомерные массивы располагаются в памяти так, что быстрее всего меняется последний индекс.
Вопросы:
1.Понятие указателя.
2.Операция разыменования.
3.Операция получения адреса.
4.Модели памяти, поддерживаемые в С;
5.Арифметические действия над указателями.
6.Понятие динамического массива.
7.Функции для выделения и освобождения памяти.
8.Массивы указателей и многомерные массивы.
9.Матрицы со строками разной длины.
Задание на выполнение:
1. Написать программу, в которой выполнить следующие действия:
a) Описать указатели на все известные типы данных;
b) Выполнить для каждого указателя операцию разыменования;
c) Получить значение каждого указателя;
d) Получить значение по адресу, указываемому указателем;
e) Выполнить операцию получения адреса.
2. Написать программу, в которой выделить память под массив размерности n´m´l. Подсчитать среднее арифметическое всех элементов каждой матрицы размерности n´m.
3. Написать программу, в которой выделить память под массив размерности n´m´l. Подсчитать среднее геометрическое всех элементов каждой матрицы размерности m´l.
4. Написать программу, в которой выделить память под матрицу со строками разной длины. В каждую стоку занести информацию о возрасте сотрудников i-го отдела. На печать вывести возраст и номер отдела самого молодого и самого пожилого сотрудника. Предполагается, что в каждом отделе различное количество сотрудников.
ЛАБОРАТОРНАЯ РАБОТА №6