The C89 Standard was written primarily from a descriptive rather than a prescriptive standpoint, at a time when many actions would have behaviors specified by some but not all implementations, based in significant measure upon the target platforms and application fields. Further, many actions would have predictable and easily-specifiable consequences in some circumstances but not others. The authors of the Standard made no effort to exhaustively describe all combinations of circumstances when quality implementations should be expected to behave predictably, and even go so far as to note in their rationale document that a sufficiently poor-quality implementation could be simultaneously conforming and useless.
With reference to reading uninitialized data from an array, consider the following code:
float test(int a, int b, int c, int d)
{
float result;
float *f = malloc(10*sizeof float);
f[a] = 1.0f; f[b] = 2.0f; f[c] = 3.0f;
result = f[d];
free(f);
return result;
}
On some platforms, attempting to process certain bit patterns as floating-point numbers might trigger a possibly-non-configured trap handler. I think it's pretty clear that the authors of the Standard wanted to avoid requiring that implementations for such platforms prevent code like the above from firing the trap handler if e.g. code calls `test(1,2,3,4), nor requiring that they limit the consequences of such a trap if it fires. Further, I don't think they would have viewed negatively the quality implementations on such platforms where such code might yield arbitrary and unpredictable effects.
Suppose, however, the code had been:
typedef struct { float v; } flt;
flt test2(int a, int b, int c, int d)
{
flt result;
flt *f = malloc(10*sizeof flt);
f[a].v = 1.0f; f[b].v = 2.0f; f[c].v = 3.0f;
result = f[d];
free(f);
return result;
}
The Standard explicitly forbids structures from having trap representations. As a consequence, there would be no way that f[d]
could hold a trap representation, and thus no reason for anything weird to happen when it is read. If code which calls test2
were to attempt to use field v
of the result, that might trigger an unwanted trap handler, but if nothing ever does anything with that field there should be no problem.
Unfortunately, because the authors of the Standard were not trying to list out all the combinations of circumstances where quality implementations should be expected to behave predictably, they didn't think it necessary to distinguish between cases where certain actions might have unpredictable actions in some circumstances, from those where the actions should be viewed as never having predictable consequences even in cases where other parts of the Standard or an implementation's documentation would otherwise guarantee the behavior.
The authors of C89 stated that they wanted to avoid breaking existing code whenever possible, but they failed to explicitly specify behavior in many combinations of circumstances where programs would rely upon them. Unless they were lying or incompetent, such failure would most logically be attributed to a belief that implementers shouldn't need a complete list of all such behaviors in order to recognize and support them where required.
Should one be able to trust that a compiler which is configured to behave as a quality implementation suitable for low-level programming, which targets a platform that doesn't have trap representations, won't totally jump the rails when reading an uninitialized array value? Given the low cost of supporting such a guarantee, I would suggest that any implementation that can't uphold it isn't a quality compiler suitable for low-level programming.
Should one trust that gcc and clang won't behave oddly when given code which relies upon behaviors not mandated by the Standard, or even does things which are defined by the Standard but would, in the absence of optimization be equivalent to ones that aren't? No. It really doesn't matter whether the Standard defines a particular behavior if some compilers process the behaviors usefully and others try to avoid doing so and view the few cases where the Standard tries to explicitly defines the behavior as defects in the Standard.