简体   繁体   中英

Variadic functions in C

I have a function which looks like

void demo_function_v(const char * mask, va_list ap)
{
    for (; *mask; mask++) {
        // get one or more args from ap, depending on *mask
    }
}

This runs on a AVR system which has different access techniques for the flash memory. In order to duplicate this function, I'd like to extract the main part of it:

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

Alas, this is not guaranteed to work, as

The object ap may be passed as an argument to another function; if that function invokes the va_arg macro with parameter ap , the value of ap in the calling function is indeterminate.

And this is true - on x86, it doesn't work, on x64, it does. If it works on AVR, I'll be able to test tomorrow. But I'd rather not rely on this if it is described as "indeterminate" (which is supposedly a kind of UB).

Thus, I'd like to go another track:

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

Is this supposed to work, or will I shoot my foot this way as well?

The C99 standard has the following footnote clarifying this:

It is permitted to create a pointer to a va_list and pass that pointer to another function, in which case the original function may make further use of the original list after the other function returns.

I believe that behavior was also the intent in C90 even if the standard didn't note it. The C90 rationale document does have this to say:

The va_list type is not necessarily assignable. However, a function can pass a pointer to its initialized argument list object, as noted below.

...

va_start must be called within the body of the function whose argument list is to be traversed. That function can then pass a pointer to its va_list object ap to other functions to do the actual traversal. (It can, of course, traverse the list itself.)

Making it pretty clear, I think, that accessing the va_list object through a pointer acts as you'd expect (that the va_list state is being maintained in the object instance that the address was taken from), even if it doesn't explicitly state that the original va_list object will pick up where the pointer usage left off. For it to not work this way, pointers to va_list objects would have to behave differently than pointers to other C objects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM