Здесь мы разберем, что из себя представляют массивы указателей.
Для понимания конструкций, состоящих из набора звездочек, скобок и имен типов, нужно аккуратно применять синтаксические правила, учитывающие последовательность выполнения операций. Например, определение:
int *array[6];
вводит массив указателей на объекты типа int. Имя массива array, он состоит из шести элементов, тип каждого int *. Определение:
int (*ptr)[6];
По определению массива, его элементы должны быть однотипные и одинаковые по длине. Пусть необходимо определить массив для представления списка фамилий. Если определять его как двумерный массив типа char, то в определении элементов массива необходимо задать предельные размеры каждого из двух индексов, например: char spisok[25][20];. Таким образом, количество фамилий в списке не более 25 и что длина каждой фамилии не превышает 19 символов (букв). При определении массива одну из его предельных размерностей (значение самого левого индекса) можно не указывать. В этом случае количество элементов массива определяется, например, инициализацией (рис.1):
char spisok[][20] = {"ИВАНОВ", "ПЕТРОВ", "СИДОРОВ"}; .
Рис.1. Схема размещения в памяти элементов массива
spisok
Теперь в массиве spisok только 3 элемента, каждый из них длиной 20 элементов типа char. В противоположность этому при определении и инициализации этими же символьными строками одномерного массива указателей типа char * память распределяется гораздо рациональнее (рис.2):
Рис.2. Схема размещения в памяти элементов с помощью
массива указателей
Для указателей массива pointer, в котором 3 элемента и каждый является указателем-переменной типа char *, выделяется всего 3*sizeof(char *) байтов. Кроме того, компилятор размещает в памяти три строковые константы "ИВАНОВ" (7 байт), ПЕТРОВ" (7 байт), "СИДОРОВ" (8 байт), а их адреса становятся значениями элементов pointer[0], pointer[l], pointer[2] (рис.2).
Применение указателей и их массивов позволяет рационально решать, в частности, задачи сортировки сложных объектов с неодинаковыми размерами. Например, рассмотрим задачу сортировки строк матрицы. Матрица с элементами типа double представлена двумерным массивом double array [n][m], где n и m - целочисленные константы. Предположим, что нужно упорядочить строки матрицы в порядке возрастания сумм их элементов. Чтобы не переставлять сами строки исходного массива введен вспомогательный одномерный массив указателей double * par[n]. Инииализируем его элементы адресами массивов строк. В качестве значений элементов массива используем номера строк. Матрицу напечатаем три раза: до и после сортировки с помощью вспомогательного массива указателей и (после сортировки) с использованием основного имени массива.
Приведем текст этой программы.
#include <iostream.h> void main() { const int n=5; //Количество строк. const int m=7; //Количество столбцов. double array[n][m]; //Основной массив. for (int i=0;i<n;i++) for (int j=0;j<m;j++) array[i][j]=n-i; //Заполнение массива. double *par[n]; //Массив указателей. for (i=0;i<n;i++) //Цикл перебора строк. par[i]=(double*)array[i]; //Печать массива через указатели. cout << "\n До перестановки элементов массива указателей: "; for (i=0;i<n;i++) { cout<< "\n строка " << i+1 <<": "; for (int j=0;j<m;j++) cout << "\t" << par[i][j]; } //Упорядочение указателей на строки массива. double si,sk; for (i=0;i<n-1;i++) { for (int j=0, si=0;j<m;j++) si+=par[i][j]; for (int k=i+1;k<n;k++) { for (j=0,sk=0;j<m;j++) sk+=par[k][j]; if (si>sk) { double *pa=par[i]; par[i]=par[k]; par[k]=pa; double a=si; si=sk; sk=a; } } } //Печать массива через указатели. cout << "\nПосле перестановки элементов массива:"; for (i=0;i<n;i++) { cout << "\n строка " << i+1 <<": "; for (int j=0;j<m;j++) cout << "\t" << par[i][j]; } cout << "\nИсходный массив остался неизменным: "; for (i=0;i<n;i++) { cout << "\nstroka " << i+1 <<": "; for (int j=0;j<m;j++) cout << "\t" << array[i][j]; } }
Следующий шаг будет посвящен динамическим массивам.