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

Последнее обновление: 10.01.2023

Язык программирования Си допускает использование функций, которые имеют нефиксированное количество параметров. Более того может быть неизвестным не только количество, но и типы параметров. То есть точное определение параметров становится известным только во время вызова функции.

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

тип имя_функции(обязательные параметры, ...)

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

int sum(int n, ...)
{
	//..........
}

Для упрощения работы с переменным количеством параметров неопределенных типов в языке Си в стандартом заголовочном файле stdarg.h определены специальные макрокоманды:

va_start();
va_arg();
va_end();

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

Макрос va_start имеет следующее определение:

void va_start(va_list param, последний_явный_параметр);

Первый параметр макроса - param связывает объект va_list с первым необязательным параметром. Для его определения в качестве второго параметра в макрос передается последний обязательный параметр функции. Таким образом, используя последний обязательный параметр, мы можем нацелить объект va_list на адрес первого необязательного параметра. То есть фактически va_list выступает в данной роли как указатель.

Макрос va_arg имеет следующее определение:

type va_arg(va_list param, type);

Этот макрос позволяет получить значение параметра типа type, а также переместить указатель va_list на следующий необязательный параметр.

Макрос va_end позволяет выйти из функции с переменным списком параметров. Она имеет следующее определение:

void va_end(va_list param);

В качестве параметра va_end принимает указатель va_start, который ранее был задействован в макросах va_start и va_arg.

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

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...)
{
	int result = 0;
	va_list factor;			//указатель va_list
	va_start(factor, n);	// устанавливаем указатель
	for(int i=0;i<n; i++)
	{
		result += va_arg(factor, int);	// получаем значение текущего параметра типа int
	}
	va_end(factor);	// завершаем обработку параметров
	return result;
}

int main(void)
{	
	printf("%d \n", sum(4, 1, 2, 3, 4));
	printf("%d \n", sum(5, 12, 21, 13, 4, 5));
	return 0;
}

В функции sum() вначале определяется указатель va_list factor;.

Далее связываем этот указатель с первым необязательным параметром: va_start(factor, n);.

В цикле пробегаемся по всем необязательным параметрам и их значение прибавляем к переменной result: result += va_arg(factor, int);

В конце завершаем обработку параметров: va_end(factor);.

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

Но стоит отметить, что используемые нами функции ввода-вывода printf() и scanf() то же имеют неопределенное число параметров, но их типы также неопределены:

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

Для идентификации типов аргументов параметр format использует спецификаторы %d, %c и так далее. Например, определим собстенную функцию, которая будет выводит текст на экран, принимая параметры разных типов:

#include <stdio.h>
#include <stdarg.h>

void display(char* format, ...)
{
	int d; 
	double f;
	va_list factor;			// указатель на необязательный параметр
	va_start(factor, format);	// устанавливаем указатель
	
	for(char *c = format;*c; c++)
	{
		if(*c!='%')
		{
			printf("%c", *c);
			continue;
		}
		switch(*++c)	// если символ - %, то переходим к следующему символу
		{
			case 'd': 
				d = va_arg(factor, int);
				printf("%d", d);
				break;
			case 'f': 
				f = va_arg(factor, double);
				printf("%.2lf", f);
				break;
			default:
				printf("%c", *c);
		}
	}
	va_end(factor);
}

int main(void)
{	
	display("Count: %d \tPrice: %f", 24, 68.4);
	return 0;
}

Для упрощения примера здесь взяты только два спецификатора: d (для типа int) и f (для типа double). В самой функции display с помощью указателя char *c пробегаемся по всем символам переданной строки format, пока этот указатель не станет указывать на нулевой символ (*c!='\0'). Если символ не равен знаку %, то выводим этот символ. Иначе смотрим, какой символ идет после знака % - d или f. В зависимости от этого получаем либо объект int, либо объект double.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850