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

Обращения к функциям

Обращение к функциям, не возвращающим значение, выглядит следующим образом:

namef(a1,a2,...);

Здесь namef – имя вызываемой функции;

a1, a2, … – список фактических параметров

Если формальный параметр в заголовке функции объявлен как значение, то соответствующим ему фактическим параметром может быть выражением такого же типа. Если формальный параметр объявлен как указатель, то на месте соответствующего ему фактического параметра может находиться только адрес данного такого же типа. Исключение составляют формальные параметры, объявленные с помощью нетипизированного указателя void *, им могут соответствовать адреса переменных любого типа. Для одиночных (скалярных) переменных адрес формируется из имени переменной с предшествующим ему символом &. Для массивов адресом считается имя массива. Например:

char str[80];

int x;

........

scanf("%d %s",&x,str);

Однако если нам предстоит ввод в определенный элемент массива, то нужно указывать его адрес. Например, &str[5] или str+5:

scanf("%c",&str[5]);

scanf("%c",str+5);

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

void swap(int &a,int &b)

{ int tmp=a;

a=b; b=tmp;

}

..........

swap(x,y);

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

y=a*fin1(x,-2)+b;

В этом случае сначала будет вычислено значение операнда fin1(x,-2), которое затем будет использовано при подсчете значения выражения.

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

k=scanf("%d %s",&x,str);

Параметры-значения

Формальный параметр в заголовке функции называют параметром-значением, если перед его именем указан только тип. Например, функция mid, вычисляющая среднее арифметическое двух величин, получает в качестве фактических аргументов два числовых значения определенного типа:

double mid(double x, double y)

{ return (x+y)/2.; }

В качестве фактических аргументов, соответствующих параметрам-значениям, могут быть заданы любые числовые выражения (формулы):

w1 = mid(x*cos(fi)+y*sin(fi), x*sin(fi)-y*cos(fi));

Значения этих выражений вычисляются и записываются в стек, откуда их извлекает функция mid и помещает переданные значения в свои локальные переменные x и y (формальные параметры-значения можно рассматривать как локальные переменные функции). При необходимости, значение вычисленного выражения автоматически приводится к типу формального параметра. После работы функции возвращаемый результат возвращается в специально выделенном регистре.

Почти все математические функции раздела math.h используют передачу аргумента по значению.

Параметры-указатели

Формальный параметр в заголовке функции называют явным параметром-указателем, если перед его именем находится символ *. Например, функция swap1, осуществляющая перестановку местами значений двух переменных, должна получить в качестве параметров адреса этих переменных, т.е. указатели на них:

void swap1(int *x,int *y) //явные параметры-указатели

{ int tmp=*x;

*x=*y; *y=tmp;

}

Кроме явных параметров-указателей в объявлении функции можно использовать и косвенные параметры-указатели, описанные с помощью механизма подстановок:

#define pint int*

...............

void swap1(pint x,pint y) //косвенные параметры указатели

{ int tmp=*x;

*x=*y; *y=tmp;

}

Косвенное объявление указателей может использовать и другую синтаксическую конструкцию:

deftype int* pint;

Представим себе, что функция swap1 была бы оформлена с параметрами-значениями:

void swap1(int x,int y)

{ int tmp=x;

x=y; y=tmp;

}

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

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

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

int x=2,y=3;

swap1(&x,&y);

Рассмотрим еще один пример передачи указателя по указателю:

void swap(int **v1, int **v2)

{ int *tmp=*v1; *v1=*v2; *v2=tmp; }

К этой функции можно обратиться следующим образом:

int i=10, j=20;

int *pi=&i, *pj=&j;

swap(&pi,&pj);

после такого обращения указатели pi и pj "смотрят" на новые значения, т.к. они поменялись адресами (*p1=20, *pj=10), но сами переменные i и j свои значения не поменяли.

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

int sum_v(int *a,int n)

{ int j,s=0;

for(j=0; j<n; j++)

s += a[i];

return s;

}

Обращение к такой функции может выглядеть следующим образом:

int q[20];

..........

k1=sum_v(q,20); //суммирование всех компонент вектора

k2=sum_v(q,10); //суммирование первых 10 компонент вектора

k3=sum_v(&q[5],3); //суммирование q[5]+q[6]+q[7]

k4=sum_v(q+5,3); //суммирование q[5]+q[6]+q[7]

Не забывайте, что имя массива одновременно является и указателем на его первый элемент (т.е. q и &q[0] – это одно и то же).

Параметры-ссылки

Формальный параметр в заголовке функции называют явным параметром-ссылкой, если перед его именем находится символ &. Например, функция swap2, осуществляющая перестановку местами значений двух переменных, должна получить в качестве параметров ссылки наэти переменные, т.е. их адреса:

void swap2(int &x,int &y) //явные параметры-ссылки

{ int tmp=x;

x=y; y=tmp;

}

Параметры-ссылки могут объявлены в заголовках функций и с помощью косвенных типов, предварительно описанных в конструкциях #define или deftype:

#define rint int&

//или

deftype int& rint;

...................

void swap2(rint x,rint y)

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

int x=2,y=3;

swap2(x,y);

По ссылке можно передавать не только имена переменных, но и имена указателей:

void swap3(int *&v1,int *&v2)

{ int *t=v2; v2=v1; v1=t; }

К этой функции можно обратиться следующим образом:

int i=10, j=20;

int *pi=&i, *pj=&j;

swap(pi,pj);

После этого обращения указатели pi и pj "смотрят" на новые значения (*pi=20, *pj=10), но сами переменные i и j сохранили свои прежние значения.

Билет 9

Наши рекомендации