TL;DR: is C23's universal initialization type var = {};
safe for all types, including standard opaque ones, such as va_list
?
I have code that uses variable arguments. An older version of a static analyzer Coverity has started to report a dubious warning about uninitialized variable when processing code compiled with GCC15 that chooses C23 standard by default.
That compiler internally uses a new builtin __builtin_c23_va_start()
to implement va_start()
(instead of older __builtin_va_start()
). Apparently, Coverity does not yet understand that this builtin is meant to initialize the passed variable.
E.g. in the following function:
#include <stdarg.h>
#include <stdio.h>
void warning(const char *file, unsigned line, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
fprintf(stderr, "warning %s:%u: ", file, line);
vfprintf(stderr, fmt, va);
va_end(va);
fprintf(stderr, "\n");
}
The analyzer reports: Using uninitialized value "va" when calling "__builtin_c23_va_start".
on the second line of warning()
.
Possible approaches to suppress such false positives are: 1) update Coverity to a newer version (currently in progress), 2) suppress individual warning sites by annotations in comments (does the thing but makes code messier).
I am considering a third alternative: use empty initalization at the same line where the va
is defined. That is, to have code like this:
va_list va = {};
va_start(va, fmt);
Adding = {}
makes Coverity stop complaining. But I am not sure if this is allowed for objects of opaque standard types such as va_list
. va
will be initialized by va_start()
on the next line, so it should be OK to use it afterwards. But isn't it undefined behavior already at that point?
If it is truly opaque - an incomplete struct not visible to whoever includes the header - then you wouldn't be able to declare an object of it in the first place. So it can't be. Furthermore, C actually guarantees that it is not an opaque type:
C23 7.16.1
va_list
which is a complete object type suitable for holding information needed by the macros...
(There's also a non-normative note saying that passing around pointers to a va_list
object is fine.)
Meaning it is either an object defined in some internals of a header or it is a macro/typedef
hiding a pointer. In either case you can initialize it to zero. va_list va={0};
is valid C for all types (no matter if aggregate/union/scalar etc).
In case of "scalars", for example int x = {0};
, this is fine, the braces are optional (C23 6.7.11). In case of pointers the zero becomes a null pointer constant. And so on.
As for {}
in C23 it is just "syntactic sugar" for {0}
, as per the rules of default initialization (C23 6.7.11).