На этом шаге мы рассмотрим способы передачи массивов в функцию и их
возврата.
При решении задач часто требуется использовать массив в качестве параметра функции, кроме того, функции могут возвращать указатель на массив в качестве результата. Рассмотрим реализацию этих возможностей.
При передаче массивов через механизм параметров возникает задача определения в теле функции количества элементов массива, использованного в качестве фактического параметра. При работе со строками, то есть с массивами типа char[], последний элемент каждого из которых имеет значение '\0', анализируется каждый элемент, пока не встретится символ '\0', и это считается концом строки-массива.
#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() строка-параметр представлена как массив, и обращение к его элементам выполняется с помощью явного индексирования.
Если массив-параметр функции не есть символьная строка, то нужно либо использовать только массивы фиксированного, заранее определенного размера, либо передавать значение размера массива в функцию явным образом. Часто это делается с помощью дополнительного параметра.
#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[]).
В качестве результата работы функции может быть указатель на массив, создаваемый в этой функции. Изменим предыдущий пример: количество элементов массивов будем расчитывать программно; функция возвратит указатель на созданный массив.
#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++ заключается в том, что по имени массива нельзя определить его размерность и размеры по каждому измерению. По определению, многомерный массив не существует, он рассматривается как одномерный массив, каждый элемент которого, в свою очередь, представляет собой массив. При необходимости передать в функцию многомерный массив нужно указать значение каждой размерности, что "отвергается" компилятором.
Указанные ограничения на возможность применения многомерных массивов в качестве параметров можно обойти нескольким путями. Первый путь - подмена многомерного массива одномерным и имитация внутри функции доступа к многомерному массиву. Второй путь - использование вспомогательных массивов указателей на массивы. Одномерные массивы служат в этом случае для представления строк матриц. Так как вспомогательный массив указателей и массивы-строки матрицы являются одномерными, то их размеры могут быть опущены в ее соответствующих спецификациях формальных параметров. Тем самым появляется возможность обработки в теле функции двухмерных, а в более общем случае и многомерных массивов с изменяющимися размерами. Конкретные значения размеров должны передаваться функции либо с помощью дополнительных параметров, либо с пользованием глобальных (внешних) переменных. Третий путь предусматривает применение классов представления многомерных массивов.
Приведем пример программы, иллюстрирующей первый из перечисленных способов. В функции будем передавать только размерность и указатель на массив, а внутри функций будем рассматривать этот указатель как указатель на квадратную матрицу заданной размерности.
#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), а после ее формирования печатается результат.
#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; }
Со следующего шага мы начнем знакомиться со строками.