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


Notice: Функция get_currentuserinfo с версии 4.5.0 считается устаревшей! Используйте wp_get_current_user(). in /hlds/web/u138079p19/code4life.ru/htdocs/wp-includes/functions.php on line 3840

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

Теория

C++ предоставляет такую возможность как неопределенно количество аргументов. Синтаксис:

void function(int arg1, ...)

Неопределенные аргументы обозначаются многоточием, однако хотя бы один аргумент должен быть объявлен. Как изъять неопределенные аргументы без одного явного я не знаю, если кто знает напишите об этом в комментариях pardon

Вся суть сводиться к тому чтобы пройтись по всему стеку данных и обойти таким образом все переданные аргументы функции (работает для __cdecl, для остальных не знаю, как подметил Евгений, есть соглашение в котором аргументы передаются через регистры, а не через стек и работоспособность этого всего в данном случае не гарантируется). Однако существуют некоторые ограничения, а именно, программисту нужно знать какие типы данных передаются в функцию посредством этих неопределенных аргументов. Ниже разъяснение.

Реализация

Примечание: Работает для __cdecl, для остальных соглашений вызовов не проверял  hi 

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

void function(int iArg1, ...)
{
	int *p1 = &iArg1;
	while(++p1)
	{
		int iArgNext = *p1;
		//...
	}
}

То есть, достаточно просто инкерментировать указатель на предыдущий аргумент и мы можем получить следующий, потому что инкрементируется не значение, а сам указатель, то есть адрес увеличивается на sizeof(тип аргумента). Сигналом к тому что аргументы закончились будет служить нулевой указатель, однако это выяснено опытным путем, и возможно это не так. Для более точного определения конца аргументов лучше использовать свой собственный сигнал. К примеру, если в качестве аргументов требуется передача указателей, то последний указатель в аргументах может быть нулевым, тогда нулевой указатель при проходе по списку аргументов будет надежным сообщением конца big_boss То есть? вызов функции должеy быть примерно таким:

function(p1, p2, 0);

где последний аргумент задан нулем.

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

void function(int iArg1, ...)
{
	int *p1 = &iArg1;
	while(++p1)
	{
		float fArgNext = *(float*)p1;
		//...
	}
}

Для того чтобы корректно изъять указатели необходимо разыменовывать указатель на указатель:

void function(int iArg1, ...)
{
	int *p1 = &iArg1;
	while(++p1)
	{
		char *szArgNext = *(char**)p1;
		//...
	}
}

stdarg

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

void function(int iArg1, ...)
{
	va_list va;
	va_start(va, iArg1);
	
	char *szArgNext = 0;
	while(szArgNext = va_arg(va, char *))
	{
		//...
	}
	va_end(va);
}

То есть, небольшая обертка действий mosking

  • va_list va; объявляем переменную которая будет у нас агрегировать аргументами
  • va_start(va, iArg1); — инициализация первого аргумента
  • va_arg(va, char *) — извлечение следующего аргумента с указанием его типа
  • va_end(va); — обнуление va, чтобы не было повторного использования

Неопределённое количество аргументов разного типа

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

printf("%d %s %f", 10, "string", 12.4);

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

void function(const char *szFormat, ...)
{
	va_list va;
	char buf[4096];
	va_start(va, szFormat);
	vsprintf_s(buf, 4096, szFormat, va);
	va_end(va);

	//...
}

char buf[4096]; и vsprintf_s(buf, 4096, szFormat, va); цифра 4096 означает размер буфера (строки в символах) я знаю то мои сообщения не будут превышать этот предел, поэтому именно число и указал, однако существуют разные случаи, и где-то будет логичнее выделять память динамически.

Таким образом buf будет содержать форматированную строку в соответствии с заданными параметрами в szFormat и значениями аргументов после него. Пример:

function("%d %s %f", 10, "string", 12.4);

Внутри функции в переменной buf будет строка: «10 string 12.4».


Вроде все что хотел написать, теперь буду знать куда смотреть когда следующий раз опять забуду smile

Поделиться:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*