Передача массивов в качестве параметров
ВВЕДЕНИЕ
С увеличением объема программы становится все более сложным удерживать в памяти все детали. Естественным способом борьбы со сложностью любой задачи является ее разбиение на части. В С+ + задача может быть разделена на более простые подзадачи с помощью функций, после чего программу можно рассматривать на уровне взаимодействия функций.
Использование функций является первым шагом к повышению степени абстракции программы и ведет к упрощению структуры.
Следующим шагом в повышении уровня абстракции программы является группировка функций и связанных с ними данных в отдельные файлы (модули), компилируемые раздельно. Получившиеся в результате компиляции объектные модули объединяются в исполняемую программу с помощью компоновщика.
Функции
Объявление и определение функций
Функция − это именованная последовательность описаний и операторов, выполняющая какое-либо законченное действие. Функция может принимать параметры и возвращать значение. Любая программа на С++ состоит из функций, одна из которых должна иметь имя main − с нее начинается выполнение программы. Функция начинает выполняться в момент вызова. Любая функция должна быть объявлена и определена. Объявление функциизадает ее имя, тип возвращаемого результата и список передаваемых параметров. Определение функции содержит кроме объявления тело функции, представляющее собой последовательность операторов и описаний в фигурных скобках:
[класс ] тип имя_функции ([список параметров ])
[throw (исключения)]
{тело функции}
С помощью необязательного модификатора класс можно явно задать область видимости функции, используя ключевые слова extern и static:
- extern − глобальная видимость во всех модулях программы (по умолчанию);
- static − видимость только в пределах модуля, в котором определена функция.
Тип возвращаемого функцией значения может быть любым, кроме массива и функции, но может быть указателем на массив или функцию. Если функция не должна возвращать значение, указывается тип void.
Список параметров определяет величины, которые требуется передать в функцию при ее вызове. Элементы списка параметров разделяются запятыми. Для каждого параметра, передаваемого в функцию, указывается его тип и имя (в объявлении имена параметров можно опускать).
Для вызова функции в простейшем случае нужно указать ее имя, за которым в круглых скобках через запятую перечисляются имена передаваемых аргументов. Если тип возвращаемого функцией значения не void, она может входить в состав выражений или, в частном случае, располагаться в правой части оператора присваивания.
Пример. Функция, возвращающая сумму двух целых величин:
# include <iostream.h>
int sum (int a, int b); // объявление функции
int main ( ) {
int a = 2, b = 3, c,d;
c = sum(a, b); // вызов функции
cin >> d;
cout << sum(c,d);
return 0;
}
int sum (int a, int b) { // определение функции
return a+b;
}
Все величины, описанные внутри функции, а также ее параметры являются локальными. Областью их действия является функция. Для того чтобы сохранить значение локальной переменной между вызовами функции, такую переменную нужно объявить с помощью модификатора static.
!!! При совместной работе функции должны обмениваться информацией. Это можно осуществить с помощью глобальных переменных, через параметры и возвращаемое функцией значение.
Глобальные переменные
Глобальные переменные видны во всех функциях, где не описаны локальные переменные с теми же именами, поэтому использовать их для передачи данных между функциями легко. Однако делать это не рекомендуется. поскольку затрудняет отладку программы и препятствует помещению функций в библиотеки общего пользования.
Возвращаемое значение
Механизм возврата из функции в вызывающую ее функцию реализуется оператором
return [выражение];
Функция может содержать несколько операторов return. Если функция описана как void, выражение не указывается. Оператор return можно опускать для функции типа void, если возврат из нее происходит перед закрывающей скобкой, и для функции main.
Примеры
int f1 ( ) {return 1;} // Правильно
void f2 ( ) {return 1;} // Неправильно
Параметры функции
Механизм параметров является основным способом обмена информацией между функциями. Параметры, перечисленные в заголовке описания функции, называются формальными, а записанные в операторе вызова функции − фактическими.
Существует два способа передачи параметров в функцию − по значению и по адресу.
При передаче по значению операторы функции работают с копиями значений фактических параметров. Доступа к исходным значениям параметров у функции нет, а следовательно, нет возможности их изменить.
При передаче по адресу функция осуществляет доступ к ячейкам памяти по соответствующим адресам и может изменить исходные значения параметров.
Пример
# include <iostream.h>
void f (int i, int *j, int & k);
int main ( ) {
int i = 1, j = 2, k= 3;
cout << "i j k \n";
cout << i << ' '<< j << ' ' << k << '\n';
f(i, &j, k);
cout << i << ' '<< j << ' ' << k << '\n';
return 0;
}
void f(int i, int *j, int &k) {
i ++; (*j)++; k++;
}
На экране будет напечатано:
i j k
1 2 3
1 3 4
Первый параметр функции f (i) передается по значению. Его изменение в функции не влияет на исходное значение. Второй параметр (j) передается по адресу с помощью указателя. Третий параметр (k) передается по адресу с помощью ссылки.
Если требуется запретить изменение параметра внутри функции, используется модификатор const:
int f(const char *a);
Передача массивов в качестве параметров
При использовании в качестве параметра массива в функцию передается указатель на его первый элемент, иначе говоря, массив всегда передается по адресу. При этом информация о количестве элементов теряется, и следует передавать его размерность через отдельный параметр (в случае массива символов, т.е. строки, ее фактическую длину можно определить по положению нуль-символа).
Пример
# include <iostream.h>
int sum (const int *mas, const int n);
int const n = 10;
int main ( ) {
int marks [n] = {3,4,5,4,4};
cout << "Сумма элементов массива" << sum (marks,n);
return 0;
}
int sum (const int *mas, const int n);
// Варианты заголовка: int sum (int mas [ ], int n);
// или int sum (int mas[n], int n);
int s = 0;
for (int i = 0; i<n; i + +;) s +=mas[ i ];
return s;
}
При передаче двумерных массивов все размерности, если они не известны на этапе компиляции, должны передаваться в качестве параметров. В приведенном ниже примере с помощью функции подсчитывается сумма элементов двумерных массивов. Размерность массива b известна на этапе компиляции, под массив с именем a память выделяется динамически.
Пример
include <stdio.h>
include <stdlib.h>
int sum (const int *a, const int nstr, const int nstb);
int main ( ) {
int b[2] [2] = {{2,2},{4,3}};
printf ("Сумма элементов b: %d \ n",sum (&b[0][0],2,2));
int i, j, nstr, nstb, *a;
printf( "Введите количество строк и столбцов а: \n");
scanf("%d%d", &nstr, &nstb);
a=(int *) malloc(nstr*nstb*sizeof(int));
for (i = 0; i<nstr; i++)
for (j = 0; j<nstb; j++) scanf ("%d", &a[i * nstb + j]);
printf("Сумма элементов a: %d \n", sum(a, nstr, nstb));
return 0;
}
int sum (const int *a, const int nstr, const int nstb) {
int i, j, s =0;
for (i = 0; i < nstr; i++)
for (j = 0; j<nstb; j++) s +=a[i*nstb+j];
return s;
}
!!! Двумерный массив внутри функции интерпретируется как одномерный, а его индекс пересчитывается в программе, поэтому возможно такое обращение к элементу с номерами i и j: a[i*nstb + j].
Альтернативный способ работы с двумерным массивом − использование операций new и delete.
Пример
# include <iostream.h>
int sum (int **a, const int nstr, const int nstb);
int main( ) {
int nstr, nstb;
cin >> nstr >> nstb;
int **a, i, j;
a = new int* [nstr];
for (i =0; i<nstr; i++) a[ i ]=new int [nstb];
for (i =0; i<nstr; i++)
for (j =0; j<nstb; j++) cin >> a[ i ][ j ];
cout << sum (a, nstr,nstb);
return 0;
}
int sum (int **a, const int nstr, const int nstb) {
int i, j, s =0;
for (i =0; i<nstr; i++);
for (j =0; j<nstb; j++) s += a[ i ][ j ];
return s;
}
Задания
1. Разработать функцию, возвращающую минимальный элемент одномерного числового массива, и написать с ней программу обработки массива.
2. Разработать функцию, вычисляющую количество неотрицательных элементов в двумерном числовом массиве. Размерность массива не известна на этапе компиляции.