В объявлении и определении такой функции переменное число параметров задается многоточием в конце списка формальных параметров, при этом запятая после последнего фиксированного параметра перед многоточием необязательна. Функция с переменным числом параметров должна иметь хотя бы один фиксированный параметр. При вызове функции с переменным числом параметров задается любое требуемое число параметров.
В языке C предусмотрены специальные средства, определённые в заголовочном файле stdarg.h и используемые в самой функции для работы с её параметрами:
тип данных
| va_list
|
макрос
| va_start(va_list list, last_fixed)
|
макрос
| arg_type va_arg(va_list list, arg_type)
|
макрос
| va_end(va_list list)
|
Тип данных va_list по своей сути является указателем типа void и используется специально для доступа к параметрам функции с переменным числом параметров.
Макрос va_start устанавливает параметр list типа va_list на начало списка необязательных параметров, поле last_fixed должно содержать имя последнего обязательного параметра. Макрос va_start должен быть использован до первого использования макроса va_arg.
Макрос va_arg осуществляет последовательный доступ к переменным параметрам функции, извлекая очередной параметр функции по адресу, заданному параметром list, тип данного параметра функции определяется полем arg_type. Тип должен быть таким типом данных, чтобы объект данного типа мог быть передан в качестве параметра функции, проверка соответствия типа не производится, то есть совпадение типа в va_arg при извлечении параметра и типа фактического параметра при вызове функции должно контролироваться программистом. При каждом извлечении нового параметра макросом va_arg указатель list модифицируется в соответствии с типом извлекаемого параметра и указывает на следующий необязательный параметр функции. Возвращаемое макросом значение – извлеченный переменный параметр функции, имеющий тип arg_type.
Макрос va_end завершает работу с параметром list типа va_list и должен быть использован после извлечения всех параметров макросом va_arg. Его следует использовать в целях переносимости программы на другие платформы и компиляторы, хотя никаких особых действий в среде он не производит.
Основная проблема при использовании переменного числа параметров заключается в том, что в функции не известны ни их количество, ни их типы. Для определения этого существуют следующие подходы:
- можно использовать подход как в функции printf(), где строка формата является обязательным параметром и полностью определяет количество и типы необязательных параметров через спецификации формата;
- можно зарезервировать какое-либо значение необязательного параметра как показатель того, что необязательный параметр с данным значением – последний переданный параметр, но при этом все необязательные параметры будут для всех вызовов иметь один тип;
- можно передавать число необязательных параметров в качестве фиксированного параметра, но при этом все необязательные параметры также будут для всех вызовов иметь один тип.
В файле stdio.h определено семейство функций v…printf() и v…scanf(), которые можно использовать внутри функций с переменным числом параметров.
Пример 10
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
double sum1(char *string...)
{
va_list args;
int length=strlen(string);
double result=0;
va_start(args,string);
for(int j=0;j<length;j++)
{
switch(*(string+j))
{
case 'i':
result+=va_arg(args,int);
break;
case 'c':
result+=va_arg(args,char);
break;
case 'f':
result+=va_arg(args,double);
break;
case 'd':
result+=va_arg(args,double);
break;
default:
va_end(args);
return 0;
}
}
va_end(args);
return result;
}
int sum2(int a,...)
{
va_list args;
int result=a,t;
va_start(args,a);
while((t=va_arg(args,int))!=0)
result+=t;
va_end(args);
return result;
}
int sum3(int num,int a...)
{
va_list args;
int result=a;
va_start(args,a);
for(int i=0;i<num;i++)
result+=va_arg(args,int);
va_end(args);
return result;
}
void main(void)
{
char c=2;
float f=0.5;
printf("%f %d %d",sum1("cfdidcf",c,f,0.4,1,0.1,c,0.2f),sum2(1,2,3,0),sum3(2,4,5,6));
}
На экране будет напечатано: 6.200000 6 15
Использование типов данных при извлечении зависит от реализации компилятора. В примере 10 в функции sum1 при извлечении числа типа float используется тип double.