Question Details

No question body available.

Tags

c variadic-functions

Answers (3)

Accepted Answer Available
Accepted Answer
November 29, 2025 Score: 6 Rep: 233,940 Quality: Expert Completeness: 80%

This appears to be a defect in the C11 and earlier standards.


The manpage passage you quoted is derived from section 7.16.1.4p4 of the C11 standard regarding vastart:

The parameter parmN is the identifier of the rightmost parameter in the variable parameter list in the function definition (the one just before the , ...). If the parameter parmN is declared with the register storage class, with a function or array type, or with a type that is not compatible with the type that results after application of the default argument promotions, the behavior is undefined.

Since vastart is a macro and not a function, parameters that have array or function type don't undergo pointer adjustment as would happen for parameters passed to a function. So the constraints against function and array types was likely added for this reason.

The problem with this is that what would get passed in as the second argument to vastart is a parameter of the calling function, and function parameters can't actually have array or function type.

Section 6.7.6.3 regarding function declarations says the following in paragraph 7 regarding array parameters:

A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to type’’

And paragraph 8 states the following regarding function parameters:

A declaration of a parameter as ‘‘function returning type’’ shall be adjusted to ‘‘pointer to function returning type’’

The definition of vastart was modified in the C23 standard to make the second parameter optional for backward compatibility, and specifically states that any parameters after the first are not evaluated.


So to summarize, your usage should be fine because the first parameter to iterate_string actually has pointer type, not function type.

November 30, 2025 Score: 3 Rep: 760,032 Quality: Medium Completeness: 80%

You show the function:

void iteratestring(processort process, ...) {
   valist apis;  / is = iterate string /

while (srcidx < srclen) { vastart(apis, process); if (!process(&apis)) break; } vaend(apis); return; }

This has undefined behaviour if the loop executes more than once. You should have a call to vaend() for each invocation of vastart(). However, the loop doesn't invoke vaend(), so if the loop executes more than once, you're could have problems according to the C standard.

C11 §7.16.1 Variable argument list access macros:

Each invocation of the vastart and vacopy macros shall be matched by a corresponding invocation of the vaend macro in the same function.

Also, §7.16.1.2 The vacopy macro ¶2 and §7.16.1.4 The vastart macro ¶3 both say:

Neither the vastart nor vacopy macro shall be invoked to reinitialize ap without an intervening invocation of the vaend macro for the same ap.

This analysis is independent of questions about the restrictions on the arguments to the variadic function.

The code would be clean if the invocation of vastart() was outside the loop. You can pass a pointer to a valist to a function and the called function may invoked vaarg() appropriately, and the calling function may reuse the valist after the called function returns. In the example function, the valist is reused by (potentially) calling the called function again.

Footnote 253:

  1. It is permitted to create a pointer to a valist 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.
November 30, 2025 Score: 1 Rep: 13,190 Quality: Medium Completeness: 80%

Last argument to vastart cannot be

  1. It cannot be a variable mapped to a register because that variable needs to be addressable as a pointer. It needs to be an lvalue. You should specify there a parameter from the list of parameters of the argument list of the function. (Concretely the last fixed argument. If you don’t use the last you will get complaints from gcc and/or clang)

  2. It cannot be a function call. Functions produce rvalues, so this contradicts the first point also. You need to pass (by variable) the name of a local object that is declared in the list of parameters to the function. Don’t worry about the compiler, as it declares parameters of a vararg function never as registers, for this reason.

  3. It cannot be an array. Well it can never be an array. If you declare a parameter of a function as an array it will be actually implemented as a cell element pointer, so you are safe specifying the parameter as an array. It will always be declared as a pointer. So you get a lvalue anyway.

Concretely, the macro valist requires the name of the last specified parameter of the argument list. If you use that name you are free from complaints. If you specify something different you’ll be in trouble.