Reputation: 1283
A function with the following header:
int max(int n, va_list vals)
Called inside the function:
int max_first(int n, ...)
Needs a va_start(vals, n)
invocation at the body's beginning? I tried without and it works but I don't understand which is the correct way of doing it.
int max(int n, va_list vals)
{
va_start(vals, n);
// etc
}
Upvotes: 3
Views: 2650
Reputation: 133879
The question is if
int max(int n, va_list vals)
Called inside the function:
int max_first(int n, ...)
Needs a
va_start(vals, n)
invocation at the body's beginning?
No, it does not and it must not, the correct pattern is as follows:
int max_first(int n, ...) {
va_list vals;
va_start(vals, n);
int rv = max(n, vals);
va_end(vals);
return rv;
}
then
int max(int n, va_list vals) {
for (int i = 0; i < n; i ++) {
int val = va_arg(vals, int);
...
}
...
}
i.e. you can call va_start
only in a function that has ...
and you need to pass in the argument immediately preceding the ...
, and each call to va_start
must be always followed by va_end
for the same value, and if you pass it to a function then you must call va_end
right afterwards without using it in the calling function; if you want to process the arguments again, you must then call va_start
again.
Upvotes: 6
Reputation: 6522
Let's start with some background: per the documentation, you need to call va_start
before invoking va_arg
on any va_list
:
"va_start should be invoked with an instance to a valid va_list object ap before any calls to va_arg". (Source)
"Prior to calling va_arg, ap must be initialized by a call to either va_start or va_copy, with no intervening call to va_end". (Source)
I would imagine not calling va_start
is undefined behavior, but I couldn't find a specific callout to state as such.
In your question, the function int max(int n, va_list vals)
is not "really" variadic, since it accepts a fixed number of arguments: 2
. These are int n
and va_list vals
.
Per the documentation "The declaration of a variadic function uses an ellipsis as the last parameter, e.g. int printf(const char* format, ...);
"
So, it depends on how you implement it, but I would recommend documenting the int max(int n, va_list vals)
to accept a va_list
that is already initialized with a va_start
call. The rationale being that it is not technically "variadic" and does not really "own" the va_list
. It just accepts it as an input from some other source.
The actual variadic function int max_first(int n, ...)
should be the one creating the va_list
and priming it with the call to va_start
before passing it anywhere.
Although, as far as I can tell, there's no way to check if a va_list
already has va_start
called on it or not. And there's no guarantee that it will be called before being passed to your function, so I suspect this would have to be enforced by documentation and convention.
Upvotes: 3
Reputation: 16043
You must initialize va_list
with va_start
in max_first
. But you must not redo it in max
, because that function doesn't have necessary callframe information.
Upvotes: 4