Вопрос

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

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top