ФУНКЦИИ С ПЕРЕМЕННЫМ ЧИСЛОМ ПАРАМЕТРОВ.

 

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

Таким образом, список объявлений параметров может завершаться многоточием, отделённым запятой от списка объявлений параметров, этого многоточия в списке параметров может не быть, а возможно также, что кроме многоточия в списке параметров вовсе ничего нет.

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

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

Примерами функций с переменным числом параметров являются функции из библиотеки функций языка СИ, осуществляющие операции ввода-вывода информации (printf,scanf и т.п.).

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

Рассмотрим прототип и определение функции с переменным количеством параметров:

тип имя(…);

тип имя(…)

{

тело функции

}

например:

int PP(…);

int PP(…)

{

return 100;

}

 

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


Следующий фрагмент программы демонстрирует варианты возможных вызовов функции PP().

 

int retVal;

retVal = PP();

retVal = PP(1,2 + retVal,3,4,5,25*2);

PP('z',25,17);

 

#include<iostream.h>

int PP(...)

{

return 100;

}

main()

{

int retVal;

retVal = PP();

cout<< " retVal 1="<<retVal;

retVal = PP(1,2 + retVal,3,4,5,25*2);

cout<< " retVal 2="; cout<< retVal;

PP('z',25,17);

cout<< " retVal 3="; cout<< retVal;

}

Результатом работы этой программы станет вывод на экран 100

независимо от значений параметров.

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

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


На практике обычно ограничиваются такими вариантами:

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

 

#include <iostream.h>

long PP(int n, ...);

void main (void)

{

long RR;

RR = PP(5, 1, 2, 3, 4, 5 );

/* Вызвали функцию с 6 параметрами. Единственный обязательный параметр

определяет количество передаваемых параметров. */

cout << RR << endl;

}

long PP(int n ...)

{

int *p = &n;

// область памяти с параметрами...

int Sum = 0;

for ( ; n; n--) Sum += *(++p);

return Sum;

}

Результатом работы программы станет число 15 -

сумма пяти чисел, записанных в списке параметров (1+2+ 3+ 4+ 5)

 

2. Известен тип элементов списка и признак завершения списка

передаваемых параметров. Процедура доступа к параметрам в этом случае:

 

#include <iostream.h>

long PP(int par1 ...);

void main (void)

{

long RR;

RR = PP( 1, 2, 0, 4, 0 );

/* Вызвали функцию с 5 параметрами. Единственный обязательный параметр -

первый параметр в списке параметров. */

cout << RR << endl;

}


long PP(int par1 ...)

{

int *p = &par1;

/* область памяти с параметрами. Признак конца списка - параметр с нулевым значением. */

int Sum = 0;

for ( ; *p != 0; p++) Sum += *p;

//до конца списка дойти не удалось - встретился 0

return Sum;

}

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

 


Лекция 9.

 

9.1. Функции с переменным числом параметров.

 

Можно разрабатывать свои функции с переменным числом параметров. Для обеспечения удобного способа доступа к аргументам функции с переменным числом параметров имеются три макроопределения (макросы) va_start, va_arg, va_end, находящиеся в заголовочном файле stdarg.h. Эти макросы указывают на то, что функция, разработанная пользователем, имеет некоторое число обязательных аргументов, за которыми следует переменное число необязательных аргументов. Обязательные аргументы доступны через свои имена как при вызове обычной функции. Для извлечения необязательных аргументов используются макросы va_start, va_arg, va_end в следующем порядке.

Макрос va_start предназначен для установки аргумента arg_ptr на начало списка необязательных параметров и имеет вид функции с двумя параметрами:

void va_start(arg_ptr,prav_param);

Параметр prav_param должен быть последним обязательным параметром вызываемой функции, а указатель arg_prt должен быть объявлен с предопределением в списке переменных типа va_list в виде:

va_list arg_ptr;

Макрос va_start должен быть использован до первого использования макроса va_arg.

Макрокоманда va_arg обеспечивает доступ к текущему параметру вызываемой функции и тоже имеет вид функции с двумя параметрами

type_arg va_arg(arg_ptr,type);

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

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

Макрос va_end используется по окончании обработки всех параметров функции и устанавливает указатель списка необязательных параметров на ноль (NULL).

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

 


Пример:

 

#include

int main()

{ int n;

int sred_znach(int,...);

n=sred_znach(2,3,4,-1);

/* вызов с четырьмя параметрами */

printf("n=%d",n);

n=sred_znach(5,6,7,8,9,-1);

/* вызов с шестью параметрами */

printf("n=%d",n);

return (0);

}

int sred_znach(int x,...);

{

int i=0, j=0, sum=0;

va_list uk_arg;

va_start(uk_arg,x); /* установка указателя uk_arg на */

/* первый необязaтельный параметр */

if (x!=-1) sum=x; /* проверка на пустоту списка */

else return (0);

j++;

while ( (i=va_arg(uk_arg,int))!=-1)

/* выборка очередного */

{ /* параметра и проверка */

sum+=i; /* на конец списка */

j++;

}

va_end(uk_arg); /* закрытие списка параметров */

return (sum/j);

}

Задание для самоконтроля:

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