Accessing an uninitialized variable is always undefined, in the following case:
* the storage can be declared with the register keyword
Because this is an array of uninitialized data, this is not relevant. In that case, the standard specifies that the value produced is indeterminate. An indeterminate value can either be:
* a trap representation
* an unspecified value
unsigned char never has any trap representations. Its value when uninitialized is always unspecified. Other types may have a trap representation, in theory: this is implementation defined. On your system this is almost certainly not true for integer types, but you may have signaling floats. Accessing a value that is a trap representation is undefined behavior.
If you don’t have any trap representations, then the value produced is unspecified. An unspecified value is, morally, “some random value from the range of values this thing can take”. Of note, the value produced is not required to be consistent: two “reads” can produce different results. However the act of performing the read and the results it produces (bounded by what I just mentioned) are well defined.
Putting it all together, in the case of this algorithm, on a system where integers do not have trap representations, the uninitialized read will produce an unspecified value, which the algorithm does not rely on a consistent result from. So, overall, it’s probably ok.
If you are using this code and don’t have a comment that describes exactly what I just said then you need to add one immediately. There are not many cases where it is crucial to juggle the precise definition of well-defined, undefined, implementation-defined, indeterminate, and unspecified. This is one of them.