C – is an indeterminate value indeterminable?

The fact that it is indeterminate not only means that it is unpredictable at the first read, it also means that it is not guaranteed to be stable. This means that reading the same uninitialized variable twice is not guaranteed to produce the same value. For this reason you cannot really “determine” that value by reading it. (See DR#260 for the initial discussion on the subject from 2004 and DR#451 reaffirming that position in 2014.)

For example, a variable a might be assigned to occupy a CPU register R1 withing a certain timeframe (instead of memory location). In order to establish the optimal variable-to-register assignment schedule the language-level concept of “object lifetime” is not sufficiently precise. The CPU registers are managed by an optimizing compiler based on a much more precise concept of “value lifetime”. Value lifetime begins when a variable gets assigned a determinate value. Value lifetime ends when the previously assigned determinate value is read for the last time. Value lifetime determines the timeframe during which a variable is associated with a specific CPU register. Outside of that assigned timeframe the same register R1 might be associated with a completely different variable b. Trying to read an uninitialized variable a outside its value lifetime might actually result in reading variable b, which might be actively changing.

In this code sample

{
  int i, j;

  for (i = 0; i < 10; ++i)
    printf("%d\n", j);

  for (j = 0; j < 10; ++j)
    printf("%d\n", 42);
}

the compiler can easily determine that even though object lifetimes of i and j overlap, the value lifetimes do not overlap at all, meaning that both i and j can get assigned to the same CPU register. If something like that happens, you might easily discover that the first cycle prints the constantly changing value of i on each iteration. This is perfectly consistent with the idea of value of j being indeterminate.

Note that this optimization does not necessarily require CPU registers. For another example, a smart optimizing compiler concerned with preserving valuable stack space might analyze the value lifetimes in the above code sample and transform it into

{
  int i;

  for (i = 0; i < 10; ++i)
    printf("%d\n", <future location of j>);
}

{
  int j;

  for (j = 0; j < 10; ++j)
    printf("%d\n", 42);
}

with variables i and j occupying the same location in memory at different times. In this case the first cycle might again end up printing the value of i on each iteration.

Leave a Comment