Для решения задачи передачи неопределённого количества параметров C++ располагает также средствами объявления переменных списков параметров.
Вспомним несколько форм Бэкуса-Наура, определяющих синтаксис списка параметров в определении и прототипе функции.
СписокОбъявленийПараметров ::= [СписокОбъявленийПарам] [...] ::= СписокОбъявленийПарам, ... СписокОбъявленийПарам ::= ОбъявлениеПараметра ::= [СписокОбъявленийПарам,] ОбъявлениеПараметра
Таким образом, список объявлений параметров может завершаться многоточием, отделённым запятой от списка объявлений параметров, этого многоточия в списке параметров может не быть, а возможно также, что кроме многоточия в списке параметров вовсе ничего нет.
Так вот это многоточие предупреждает транслятор о том, что определяемая или объявляемая функция может вызываться с произвольным списком параметров.
В этом случае количество и тип параметров становятся известны из списка выражений, определяющих значения параметров в выражении вызова функции.
Рассмотрим прототип и определение функции с переменным количеством параметров.
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> 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 *pPointer = &n; // Настроились на область памяти с параметрами... int Sum = 0; for ( ; n; n--) Sum += *(++pPointer); return Sum; }
Либо известен тип элементов списка и признак завершения списка передаваемых параметров. Процедура доступа к параметрам также проста, как и в первом случае:
#include <iostream.h> long PP(int par1 ...); void main (void) { long RR; RR = PP( 1, 2, 0, 4, 0 ); /* Вызвали функцию с 5 параметрами. Единственный обязательный параметр - первый параметр в списке параметров. */ cout << RRR << endl; } long PP(int par1 ...) { int *pPointer = &par1; /* Настроились на область памяти с параметрами. Признак конца списка - параметр с нулевым значением. */ int Sum = 0; for ( ; *pPointer != 0; pPointer++) Sum += *pPointer; // Что-то здесь не так… Мы так и не обработали до конца весь список. return Sum; }
Назад | Содержание | Вперед