Вариадические функции в C
-
21-12-2019 - |
Вопрос
У меня есть функция, которая выглядит как
void demo_function_v(const char * mask, va_list ap)
{
for (; *mask; mask++) {
// get one or more args from ap, depending on *mask
}
}
Это работает в системе AVR, которая имеет разные методы доступа к флэш-памяти.Чтобы продублировать эту функцию, я хотел бы извлечь ее основную часть:
void demo_function_1char(char mask, va_list ap)
{
// get one or more args from ap, depending on mask
}
void demo_function_v(const char * mask, va_list ap)
{
for (; *mask; mask++) {
demo_function_1char(*mask, ap)
}
}
Увы, это не гарантированно сработает, так как
Объект
ap
может быть передан в качестве аргумента другой функции;если эта функция вызываетva_arg
макрос с параметромap
, значениеap
в вызывающей функции является неопределенным.
И это правда - на х86 не работает, на х64 работает.Если на AVR работает, завтра смогу проверить.Но я бы предпочел не полагаться на это, если оно описывается как «неопределенное» (что якобы является разновидностью UB).
Поэтому я хотел бы пойти по другому пути:
void demo_function_1char(char mask, va_list * pap)
{
// get one or more args from *pap, depending on mask
}
void demo_function_v(const char * mask, va_list ap)
{
for (; *mask; mask++) {
demo_function_1char(*mask, &ap)
}
}
Это должно сработать, или я тоже прострелю себе ногу?
Решение
В стандарте C99 есть следующая сноска, разъясняющая это:
Разрешено создать указатель на va_list и передать этот указатель другой функции, и в этом случае исходная функция может продолжать использовать исходный список после возврата из другой функции.
Я считаю, что такое поведение также было целью C90, даже если в стандарте это не отмечалось.В обосновании C90 действительно говорится следующее:
А
va_list
тип не обязательно является назначаемым.Однако функция может передать указатель на свой инициализированный объект списка аргументов, как указано ниже....
va_start
должен вызываться внутри тела функции, список аргументов которой необходимо просмотреть.Затем эта функция может передать указатель на своюva_list
объектap
другим функциям для фактического обхода.(Конечно, он может перемещаться по самому списку.)
Я думаю, что, проясняя, что доступ к va_list
объект через указатель действует так, как вы ожидаете (что va_list
состояние сохраняется в экземпляре объекта, из которого был взят адрес), даже если в нем явно не указано, что исходный va_list
объект продолжит работу с того места, где остановилось использование указателя.Чтобы это не работало таким образом, указатели на va_list
объекты должны будут вести себя иначе, чем указатели на другие объекты C.