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

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

Пример 7. Передача параметров со значениями по умолчанию. intf(inta, intb = 0); voidf1(int, int = 100, char* = 0); /* обратите внимание на пробел между * и = */ /* без него получилась бы операция сложного присваивания *= */ voiderr(interrValue = errno); // errno - глобальная переменная <...> f(100); f(a, 1); // варианты вызова функции f f1(a); f1(a, 10); f1(a, 10, "Vasia"); // варианты вызова функции f1 f1(a,,"Vasia") // неверно!

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

Если список формальных параметров функции заканчивается многоточием, это означает, что при ее вызове на этом месте можно указать еще несколько параметров. Проверка соответствия типов для таких параметров не выполняется. При этом char и short передаются как int, float как double.

Для примера можно рассмотреть функцию printf (). Её прототип имеет вид:

int printf(const char*,...);

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

Так, например, следующие варианты вызова функций будут верными:

printf("Введите исходные данные"); // один параметр printf("Сумма: %5.2f рублей", sum); // два параметра printf ("%d %d %d %d", а, Ь, с, d); // пять параметров

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

За правильность выбора дополнительных аргументов из стека и определение числа аргументов, находящихся в стеке отвечает программист!

Макрокоманды

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

Макрокоманда va_start

Макрокоманда 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

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

type_arg va_arg(arg_ptr,type);

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

Последовательность действий макроса va_arg имеет следующий вид:

    1. извлечение значения типа type по адресу, заданному указателем arg_ptr;
    2. увеличение значения указателя arg_ptr на длину использованного параметра (длина type).

Таким образом параметр arg_ptr будет указывать на следующий параметр вызываемой функции.

Макрокоманда va_end

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

Пример использования макросов.

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

Анализ:

    1. Поскольку функция имеет переменное число параметров будем считать концом списка значение равное -1.
    2. Поскольку в списке должен быть хотя бы один элемент, у функции будет один обязательный параметр
Пример 8. Реализация поставленной задачи. #include <stdio.h> #include<stdarg.h> intmain() { intn; intsred_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); } intsred_znach(int x,...){ inti=0, j=0, sum=0; va_listuk_arg; // установка указателя uk_arg на первый необязятельный параметр va_start(uk_arg,x); 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); }

Замечания к примеру:

    1. тип va_list предназначен для хранения указателя на очередной аргумент;
    2. макрос va_start инициализирует этот указатель
    3. макрос va_arg возвращает значение очередного аргумента, каждый его вызов приводит к продвижению указателя, хранящегося в va_list
    4. после перебора аргументов, но до выхода из функции с переменным числом аргументов необходимо обратиться к макросу va_end.

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

Рекурсивные функции

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

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

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

Пример 9. Рекурсивная функция – вычисление факториала. long fact(long n){ if (n==0 || n==1) return 1; return (n * fact(n - 1)); }

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

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: