My point is that the standard should not allow undefined behavior. ...
In general, the more you pin things down, the slower they will have to
go. As we see over and over again in computing, it is more important to
get the wrong answer as fast as possible.
Seriously, there *are* languages that have no undefined behavior, and
languages that are much better specified than C (Ada, for instance, falls
into the latter category). If that is what you want, you should consider
these languages. There is a price to pay for this, though.
As for defining all behavior precisely, consider the following function:
void f(int *p, int *q) {
*p = (*q)++;
}
The semantics of this function, as defined by the C standard, say that
*p will be set to whatever *q used to have, and *q will be incremented,
so that:
int a, b = 42;
...
f(&a, &b);
leaves a set to 42, and b set to 43.
But what happens if we do:
f(&b, &b);
? The "C answer" is: "the effect is undefined". This makes it
easy for a compiler to generate code for f() that runs as fast as
possible, even if that means that this has some strange effect on
b() in a call like the last one.
Now, you might argue that the compiler should be able to detect
that p and q both point to b, so that the call to f() should draw
a diagnostic. In some languages, it is possible to specify this
-- but C is not one of those languages. In C, f() can be compiled
separately from the call to f(), and by the time you go to join
the two together (the "link phase"), the information needed is
allowed to have been, and usually has been, discarded.
Alternatively, you might argue that the compiler should be required
to generate code for f() that does something "well defined" even if
p and q both point to b. Some languages take such an approach --
but C is not one of those languages either; C allows compilers to
generate the fastest possible code, as long as it gets the right
answer whenever p and q point to different variables.
The original C standardization effort, which by most accounts was
wildly successful, attempted -- for the most part at least -- to
standardize existing practice, without inventing new features like
detecting invalid calls to f(). The C89 standard was rapidly
adopted quite widely. The later standardization effort, resulting
in C99, was clearly less successful: and it took the approach of
improving the language in various ways, inventing new features to
make programming easier and/or safer, instead of sticking with
existing practice.
I leave it to the reader to draw conclusions. (Well, one of mine --
not 100% serious, admittedly -- is at the top of this article.
)