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

У меня есть функция, которая выглядит как

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, я смогу проверить завтра. Но я бы предпочел не полагаться на это, если оно описывается как «неопределенное» (что якобы является своего рода УБ).

Таким образом, я хотел бы пойти другим путем:

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)
    }
}

Это должно сработать, или я так же прострелю себе ногу?


person glglgl    schedule 17.11.2013    source источник


Ответы (1)


В стандарте C99 есть следующая поясняющая сноска:

Разрешается создать указатель на va_list и передать этот указатель другой функции, и в этом случае исходная функция может использовать исходный список в дальнейшем после возврата другой функции.

Я считаю, что такое поведение также было намерением в C90, даже если стандарт этого не отмечал. В обосновании C90 говорится следующее:

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

...

va_start должен вызываться в теле функции, список аргументов которой должен быть пройден. Затем эта функция может передать указатель на свой va_list объект ap другим функциям для фактического обхода. (Конечно, он может пройти по всему списку.)

Я думаю, что достаточно ясно, что доступ к объекту va_list через указатель действует так, как вы ожидаете (что состояние va_list поддерживается в экземпляре объекта, из которого был взят адрес), даже если это явно не указано что исходный объект va_list продолжит работу с того места, где остановилось использование указателя. Чтобы это не работало таким образом, указатели на объекты va_list должны вести себя иначе, чем указатели на другие объекты C.

person Michael Burr    schedule 17.11.2013
comment
Также C99 представил va_copy(). - person alk; 17.11.2013
comment
@alk Это мне здесь не поможет, я хочу использовать тот факт, что вызываемая функция передвинула курсор. - person glglgl; 17.11.2013
comment
@MichaelBurr Спасибо! Это помогает мне. У меня есть только черновик без этой сноски. Тогда мне явно разрешено это делать, и я могу продолжить свою работу. - person glglgl; 17.11.2013