Опубликован: 25.08.2010 | Уровень: для всех | Доступ: свободно
Лекция 4:

Функции

< Лекция 3 || Лекция 4: 1234 || Лекция 5 >

Параметры функции

Механизм параметров является основным способом обмена информацией между вызываемой и вызывающей функциями. В операторе вызова функции записывают аргументы функции, а в заголовке описания функции перечисляют параметры. В С++ передача параметров осуществляется двумя способами: по значению и по ссылке. Это определяется видом объявления параметра в заголовке функции.

При передаче по значению объявление параметра похоже на объявление переменной:

тип имя

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

параметр = выражение

Выражение вычисляется; если тип полученного значения не соответствует типу параметра, то выполняется (если это возможно) преобразование типов, и значение присваивается параметру. В частности, если в качестве аргумента задана константа или переменная, совпадающая по типу с параметром, то значение просто копируется в параметр. Копирование требует времени, поэтому способ передачи параметров по значению обычно применяется для данных встроенных типов, время копирования которых мало.

Никакие изменения значения параметра внутри функции не отражаются на значении переменной-аргумента, так как параметр является локальной переменной.

При передаче по ссылке объявление параметра представляет собой объявление ссылки без инициализации:

тип &имя

Параметр-ссылка локальна в функции: ни в заголовке, ни в ее теле не должно быть объявлено других параметров и переменных с таким же именем. Инициализация параметра-ссылки выполняется во время вызова функции. При этом способе передачи параметров в качестве аргумента может задаваться только L -значение.

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

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

void swap( int &a, int &b )   // определение функции обмена
{   
int t = a; a = b; b = t; }
// ...
int x = 5, y = 6;
swap( x, y );           // вызов функции обмена

Функция swap фактически работает с исходными переменными x и y. Изменение функцией нелокальных переменных называется побочным эффектом.

Использование параметров-ссылок вместо передачи по значению более эффективно, поскольку не требует времени и памяти для копирования аргументов в локальные переменные. Это имеет значение при передаче структур данных большого объема.

Пример передачи параметров:

#include <iostream>
using namespace std;
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;
}
void f(int i, int* j, int& k)
{    i++; (*j)++; k++; }

Результат работы программы:

i j k 
1 2 3
1 3 4

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

Если требуется запретить изменение параметра, используется модификатор const:

int f(const char*);
char* t(char* a, const int* b);

СОВЕТ

Рекомендуется указывать const перед всеми параметрами, изменение которых в функции не предусмотрено. Это облегчает отладку. Кроме того, на место параметра типа const& может передаваться константа.

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

Передача массивов в качестве параметров

Массив всегда передается по адресу. При этом информация о количестве элементов массива теряется, и следует передавать его размерность через отдельный параметр:

#include <iostream>
using namespace std;
int sum(const int* mas, const int n);
int const n = 10;
void main()
{
int marks[n] = {3, 4, 5, 4, 4};
cout << "Сумма элементов массива: " 
	 << sum(marks, n);
}
int sum(const int* mas, const int n) 
/*варианты: int sum(int mas[], int n) или */
/*int sum(int mas[n], int n) (n должна быть константой) */
{   int s = 0;
	for (int i = 0 ; i<n; i++) s += mas[i];
	return s;}

При передаче многомерных массивов все размерности, если они не известны на этапе компиляции, должны передаваться в качестве параметров.

Внутри функции массив интерпретируется как одномерный, а его индекс пересчитывается в программе. В приведенном ниже примере с помощью функции подсчитывается сумма элементов двух двумерных массивов. Размерность массива b известна на этапе компиляции, под массив a память выделяется динамически:

#include <cstdio> using namespace std;
int sum(const int *a, const int nstr, const int nstb); 
void 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)); 
}
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;
}

Для того чтобы работать с двумерным массивом естественным образом, можно применить альтернативный способ выделения памяти:

#include <iostream>
using namespace std;
int sum(const int **a, const int nstr, const int nstb);
void main() 
{
int nstr, nstb;
cin >> nstr >> nstb;
int **a;
a = new int* [nstr];
for (int i = 0; i<nstr; i++)
  a[i] = new int [nstb];
/* * формирование матрицы a */
cout << sum(a, nstr, nstb);
}
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][j];
return s;
}

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

Передача имен функций в качестве параметров

Функцию можно вызвать через указатель на нее. Для этого объявляется указатель соответствующего типа и ему с помощью операции взятия адреса присваивается адрес функции:

void f(int a ){ /* * */ }	//определение функции
void (*pf)(int);		//указатель на функцию
...
pf = &f; /* указателю присваивается адрес */
		 /*функции (можно написать pf = f;) */
pf(10);		 /* функция f вызывается через указатель pf */
	     /*(можно написать (*pf)(10) ) */

Для того чтобы сделать программу более читаемой, при описании указателей на функции используют переименование типов ( typedef ). Можно объявлять массивы указателей на функции (это может быть полезно, например, при реализации меню):

/* описание типа PF как указателя на функцию с одним параметром типа int */
typedef void (*Pf)(int);	
/* описание и инициализация массива указателей  */
PF menu[]={&new, &open, &save}	
menu[1](10);	 //вызов функции open

Указатели на функции передаются в подпрограмму таким же образом, как и параметры других типов:

void fun(PF pf)	/* функция fun получает в качестве*/
			/* параметра указатель типа PF  */
{* pf(10); *}	//вызов функции, переданной через указатель

Тип указателя и тип функции, которая вызывается посредством него, должны совпадать в точности.

Параметры со значениями по умолчанию

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

int f(int a, int b = 0);
void f1(int, int = 100, char* = 0);	
void err(int errValue = errno);	//	errno - глобальная переменная 
f(100); f(a, 1);			// варианты вызова функции f
// варианты вызова функции f1
f1(a); 
f1(a, 10); 
f1(a, 10, "Vasia"); 
f1(a,,"Vasia")	 // неверно!
< Лекция 3 || Лекция 4: 1234 || Лекция 5 >
Dana Kanatkyzi
Dana Kanatkyzi
Здравствуйте.Помогите решить задачу минимум 4 чисел.Условие такое:"Напишите функцию int min (int a, int b, int c, int d) (C/C++)"находящую наименьшее из четырех данных чисел."Заранее спасибо!