Question Details

No question body available.

Tags

c variadic-functions

Answers (10)

February 27, 2026 Score: 4 Rep: 333 Quality: Low Completeness: 40%

The standard approach is to have a common function both of these functions call and which takes a valist instead of .... For example, printf calls vprintf (which takes a valist) under the hood, and so do all the other functions in the printf family

February 27, 2026 Score: 3 Rep: 146,319 Quality: Medium Completeness: 60%

You can do that idea. Pass valist * via a pointer, instead of valist to make the assignment work. The real suggestion is to consider using the standard (i.e. printf vs vprintf) way? Just define a function that takes valist and use it:

# include 

include

include

enum What { THIS, THAT, VARG };

void aaaa
v(enum What what, valist varg) { switch (what) { case THIS: { int aarg; char *barg;

aarg = va
arg(varg, int); barg = vaarg(varg, char*); printf("THIS: %d, %s.\n", aarg, barg); } break; case THAT: { char *carg; int darg;

carg = va
arg(varg, char); darg = va_arg(varg, int); printf("THAT: %s, %d.\n", carg, darg); } break; } }

void aaaa(enum What what, ...) { va_list varg; va_start(varg, what); aaaa_v(what, varg); va_end(varg); }

void bbbb(enum What what, ...) { va_list varg; va_start(varg, what); aaaa_v(what, varg); va_end(varg); }

int main(int argc, char
argv[]) { aaaa(THIS, 123, "abc"); aaaa(THAT, "def", 456); bbbb(THIS, 123, "abc"); bbbb(THAT, "def", 456); return EXIT_SUCCESS; }
February 27, 2026 Score: 3 Rep: 27,524 Quality: Low Completeness: 60%

I believe that both of your solutions are legally correct.

The standard explicitly states that a va_list object may be passed as an argument to other functions (see §7.16 ¶4 of the N3220 draft of the C23 standard). As far as I can tell, there is no mention of any restriction on passing it as a variadic argument.

February 27, 2026 Score: 2 Rep: 13,088 Quality: Low Completeness: 60%

Re. "I tested it with GCC and MS Visual C++ and it seems to work finely in both": godbolt says otherwise -- regardless of warning level/standard. Or have I missed something?

February 27, 2026 Score: 2 Rep: 18,143 Quality: Low Completeness: 50%

This would also allow bbbb to process the first few arguments before calling vaaaa to process the remaining arguments. bbbb cannot continue processing the arguments after vaaaa returns (it would have to start from scratch or use a copy of the valist from before it called vaaaa). If bbbb needs to continue processing arguments after vaaaa returns, then a another function vpaaaa that has a valist * parameter would be required. Then vaaaa would just call vpaaaa with the address of its valist parameter, and bbbb would call vpaaaa with the address of its valist variable (after calling va_start).

February 27, 2026 Score: 1 Rep: 18,143 Quality: Medium Completeness: 60%

The implementation only needs to support modification of a valist object via the vastart, vaend, and vacopy macros. Assignment to an object of type valist does not need to be supported.

I think you can replace varg = vaarg(temp, valist); with vacopy(varg, vaarg(temp, valist)); and replace if (what != VARG) vaend(varg); with an unconditional vaend(varg); at the end.

EDIT: That still will not work if valist is an array type, probably because the vaarg() call is not expecting its second parameter to have an array type because it only expects legal function parameter types. (If a variable of type valist has an array type, then a parameter declared as type valist will be automatically adjusted to the the type that is a pointer to the element type.)

You could change aaaa to expect a valist * as the third parameter when what == VARG:

    if (what != VARG)
        vastart(varg, what);
    else {
        valist temp;
        valist vparg;

va_start(temp, what); what = va_arg(temp, enum What); vparg = va_arg(temp, va_list ); vacopy(varg, *vparg); vaend(temp); }

It still needs the unconditional vaend(varg); call at the end of the aaaa.

Change the call to aaaa from bbbb to pass the address of the valist as the third argument:

void bbbb(enum What what, ...)
{
    valist varg;

vastart(varg, what); aaaa(VARG, what, &varg); va_end(varg); }

:END OF EDIT

February 27, 2026 Score: 1 Rep: 18,143 Quality: Medium Completeness: 60%

TL;DR: Although you can have a valist object in the variadic arguments of a function call, there is no portable way for the called function to retrieve the value of the original valist object because it might have been converted to a different type that is incompatible with valist. This is the case when valist is implemented as an array type.

Passing a valist * instead of a valist allows the called function to modify the caller's valist object with vaarg, or copy the caller's valist object with vacopy, just by dereferencing the valist * value in the first argument of the invocation of vaarg or in the second argument of the invocation of vacopy.

N.B. In the more common case of a valist object passed as a non-variadic argument of a function call, the called function's use of the vaarg macro on the valist function parameter would modify the caller's valist object if valist is implemented as an array type. This is why the C standard says that the representation of the valist object in the calling function becomes indeterminate if the called function invokes the vaarg macro on it.

February 27, 2026 Score: 1 Rep: 18,143 Quality: Low Completeness: 50%

Sure, you can pass a valist as a variadic argument but there is no portable way to retrieve its value. The problem is that the second argument of vaarg() macro is expected to be a valid type name of a function parameter, but a function parameter declared as type valist might be incompatible with the type valist. That would be the case if va_list has an array type because the parameter type would be adjusted to be a pointer to its element type.

February 27, 2026 Score: 0 Rep: 72,612 Quality: Low Completeness: 0%

Learn C++. Looking for what you are doing - you would love it

February 27, 2026 Score: 0 Rep: 2,679 Quality: Low Completeness: 0%

Thanks, this is very helpful. Mine is GCC 13.4, Cygwin. Judging from the tests the support varies quite a bit and many compilers balk at this.