Шаг 40.
Массивы указателей

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

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

     int *array[6]; 

вводит массив указателей на объекты типа int. Имя массива array, он состоит из шести элементов, тип каждого int *. Определение:

     int (*ptr)[6]; 
вводит указатель ptr на массив из шести элементов, каждый из которых имеет тип int. Возможность создания массивов указателей позволяет экономить память при использовании многомерных массивов.

    По определению массива, его элементы должны быть однотипные и одинаковые по длине. Пусть необходимо определить массив для представления списка фамилий. Если определять его как двумерный массив типа 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];
	}
}
Текст этой программы можно взять здесь.

    Следующий шаг будет посвящен динамическим массивам.


Предыдущий шаг Содержание Следующий шаг