I'm have a function that roughly looks like so:
typedef struct SomeType {
...
} SomeType;
void TakesArgs(SomeType *t1, ...) {
// iterates through arguments
}
// usage:
TakesArgs(&a, &b, &c);
Do I run any strange risks with memory (or otherwise) if I were to change TakesArgs to a no-op, while still leaving all calling code unchanged?
void TakesArgs(SomeType *t1, ...) {
return;
}
// usage unchanged:
TakesArgs(&a, &b, &c);
In other words, will skipping the va_list/va_start dance that was executed in the original implementation have any strange side effects?
Yes, it is perfectly safe. You are not required to read all variadic arguments just for the sake of reading them. You are not required to even start the variadic argument reading sequence inside TakesArgs.
Under the hood it usually means that the burden of performing any argument-passing maintenance tasks is bestowed upon the calling code (they way it works in what is usually recognized as "traditional" C calling convention). The callee does not have to do anything.
[Edited in response to comments.]
There are systems where by default it is the callee's job to pop arguments. That would obviously get you into trouble. But, it's precisely for this reason that variadic functions have to be properly declared, and the declaration has to be properly visible to callers. The reason is that on those systems, variadic functions have to use a non-default calling sequence where the caller pops the arguments, because only the caller can be sure how many there are. So you should be safe as long as your functions are properly declared.
I believe it's safe, though the standard isn't quite explicit on this point.
N1570 7.16 paragraph 3 says:
If access to the varying arguments is desired, the called function shall declare an object (generally referred to as
apin this subclause) having typeva_list.
Note the beginning of that sentence: "If access to the varying arguments is desired". The implication is that if access to the varying arguments is not desired, there's no need to declare a va_list object -- which would make it impossible to invoke va_start, va_arg, or va_end.
A possible counterargument is that the description of va_end (N1570 7.16.1.3) says:
If there is no corresponding invocation of the
va_startorva_copymacro, or if theva_endmacro is not invoked before the return, the behavior is undefined.
However, given the context, I believe that applies if va_end is not invoked after va_start is invoked.