Шаг 59.
Функции и массивы

    На этом шаге мы рассмотрим способы передачи массивов в функцию и их возврата.

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

    При передаче массивов через механизм параметров возникает задача определения в теле функции количества элементов массива, использованного в качестве фактического параметра. При работе со строками, то есть с массивами типа char[], последний элемент каждого из которых имеет значение '\0', анализируется каждый элемент, пока не встретится символ '\0', и это считается концом строки-массива.


    Пример 1. Проиллюстрируем передачу строк, описанных по-разному, в функцию.
#include <iostream.h>
int len(char e[]) //Функция вычисления 
{                 //длины строки.
  int m=0;
  while (e[m++]);
  return m-1;
}

void main()
{
  char *E="Пример строки";
  char  F[ ]="Еще пример строки";
  char *t;
  t=new char[80];
  t="Последний пример строки";
  cout << "\nДлина строки \""<< E << "\" равна " << len(E);
  cout << "\nДлина строки \""<< F << "\" равна " << len(F);
  cout << "\nДлина строки \""<< t << "\" равна " << len(t);
  delete [] t;
}
Текст этой программы можно взять здесь.

    В функции len() строка-параметр представлена как массив, и обращение к его элементам выполняется с помощью явного индексирования.

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


    Пример 2. Составить массив, каждый элемент которого равен максимальному из соответствующих значений двух других массивов.
#include <iostream.h>
void max_vect(int n, int *x, int *y, int *z)
    {
        for (int i=0;i<n;i++)
                z[i]=x[i]>y[i]?x[i]:y[i];
     }

void main()
    {
       int a[]={1,2,3,4,5,6,7};
       int b[]={7,6,5,4,3,2,1};
       int c[7];
       max_vect(7,a,b,c);
       for (int i=0;i<7;i++)
                   cout<<"\t"<<c[i];
     }       
Текст этой программы можно взять здесь.

    Заголовок функции max_vect() можно записать по-другому, учитывая тот факт, что имя массива - это указатель на начальный элемент массива: void max_vect(int n, int x[], int y[], int z[]).

    В качестве результата работы функции может быть указатель на массив, создаваемый в этой функции. Изменим предыдущий пример: количество элементов массивов будем расчитывать программно; функция возвратит указатель на созданный массив.


    Пример 3. Составить массив, каждый элемент которого равен максимальному из соответствующих значений двух других массивов. Функция должна вернуть указатель на созданный массив.
#include <iostream.h>
int *max_vect(int n, int *x, int *y)
{
  int *z = new int[n];   //Выделение памяти для
                         //элементов массива.
  for (int i=0;i<n;i++)  //Расчет элементов массива.
	 z[i]=x[i]>y[i]?x[i]:y[i];
  return z;
}
//--------------------------//
void main()
{
 int a[]={1,2,3,4,5,6,7};
 int b[]={7,6,5,4,3,2,1};
 int kc=sizeof(a)/sizeof(a[0]); //Количество элементов
                                //массива.
 int *c;                 //Указатель на результирующий массив.
 c=max_vect(kc,a,b);  //Создание массива.
 for (int i=0;i<kc;i++)   //Вывод результата.
	 cout<< "\t" << c[i];
 delete []c;      //Возврат памяти в "кучу".
}
Текст этой программы можно взять здесь.

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

    Указанные ограничения на возможность применения многомерных массивов в качестве параметров можно обойти нескольким путями. Первый путь - подмена многомерного массива одномерным и имитация внутри функции доступа к многомерному массиву. Второй путь - использование вспомогательных массивов указателей на массивы. Одномерные массивы служат в этом случае для представления строк матриц. Так как вспомогательный массив указателей и массивы-строки матрицы являются одномерными, то их размеры могут быть опущены в ее соответствующих спецификациях формальных параметров. Тем самым появляется возможность обработки в теле функции двухмерных, а в более общем случае и многомерных массивов с изменяющимися размерами. Конкретные значения размеров должны передаваться функции либо с помощью дополнительных параметров, либо с пользованием глобальных (внешних) переменных. Третий путь предусматривает применение классов представления многомерных массивов.

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


    Пример 4. Составить программу транспонирования квадратной матрицы.
#include <iostream.h>
#include <stdlib.h>
void main()
{
  void print (int,int*); //Прототип функции печати матрицы.
  void trans (int,int*); //Прототип функции транспонирования.
  int n;
  cout << "\nЗадайте размерность таблицы: ";
  cin >> n; 
  int *a= new int[n*n]; //Резервируем место под элементы таблицы.
  cout << "\nДана таблица: \n";
  randomize();                     //Используем датчик
  for (int i=0;i<n;i++)      //псевдослучайных чисел.
	 for (int j=0;j<n;j++)
			*(a+i*n +j)=10+random(50);
  print (n,a);               //Печать исходной таблицы.
  trans(n,a);              //Транспонирование матрицы.
  cout << "\nТаблица -результат: \n";
  print (n,a) ;         //Печать результирующей таблицы.
  delete []a;             //Возвращение памяти в "кучу".
}

void print (int x, int *b)  //Функция печати таблицы.
{
  for (int i=0;i<x;i++)
  {
	 for (int j=0;j<x;j++)
         	 cout << "\t" << *(b+x*i+j);
	 cout << "\n";
  }
}

void trans (int x, int *b) //Функция транспонирования матрицы.
{
  int c;
  for (int i=0;i<x-1;i++)
	 for (int j=i+1;j<x;j++)
	  {
		 c= *(b+i*x+j);
		 *(b+i*x+j)=*(b+j*x+i);
		 *(b+j*x+i) = c;
	}
}
Текст этой программы можно взять здесь.

    Многомерный массив с переменными размерами, сформированный в функции, непосредственно невозможно вернуть в вызывающую программу как результат выполнения функции. Однако возвращаемым функцией значением может быть указатель на одномерный массив указателей на одномерные массивы с элементами известной размерности и заданного типа. В следующей программе функция single_matr() возвращает именно такой указатель, так как имеет тип int **. В тексте функции формируется набор одномерных массивов с элементами типа int и создается массив указателей на эти одномерные массивы. Количество одномерных массивов и их длины определяются значением параметра функции, описанного как int n. Совокупность создаваемых динамических массивов представляет квадратную матрицу порядка n. Диагональным элементам матрицы присваиваются единичные значения, остальным - нулевые, то есть создается единичная матрица. Локализованный в функции single_matr() указатель int** p "настраивается" на создаваемый динамический массив указателей и используется в операторе возврата из функции как возвращаемое значение. В основной программе вводится с клавиатуры желаемое значение порядка матрицы (int n), а после ее формирования печатается результат.


    Пример 5.
#include <iostream.h>
#include <process.h> //Для exit().
int **single_matr(int n)
{
    //Вспомогательный указатель на матрицу.
 int **p;
    //Массив указателей на строки - одномерные массивы.
 p= new int *[n];
 if (p==NULL)
 {
	cout << "Не создан динамический массив!";
	exit(1);
 }
 for (int i=0;i<n;i++)
 {    //Формирование строки элементов типа int.
	 p[i]=new int [n];
	 if (p[i]==NULL)
		 {
		   cout << " Не создан динамический массив!";
		   exit(1);
		 }
        //Заполнение текущей строки.
	 for (int j=0;j<n;j++)
		if (j!=i) p[i][j]=0;
		else p[i][j]=1;
 }
 return p;
}

void main()
{
	int n;
	cout << "\nЗадайте порядок матрицы: ";
	cin >> n;
	int **matr; //Указатель для формируемой матрицы.
	matr = single_matr(n);
	for (int i=0;i<n;i++)
	       {
		cout << "\nстрока " << (i+1) << ": ";
		for (int j=0;j<n;j++)
			cout << "\t" << matr[i][j];
	       }
	for (i=0;i<n;i++) //Очистка памяти.
		delete matr[i];
	delete [] matr;
}
Текст этой программы можно взять здесь.

   

    Со следующего шага мы начнем знакомиться со строками.


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