How much undefined behaviour can you stand?
Quite a bit, *provided* that this "undefined" is only in terms of
the C standard.
As I have noted elsewhere, doing something like:
#include <graphics.h>
invokes undefined behavior. I have no problem with including such
a file, though, where the behavior defined by some *other* document
is required.
What I try to avoid is:
- depending on behavior that is not only not defined by the C
standard, but also not defined by anything else, and merely
"happens to work today";
- making use of implementation(s)-specific behavior when there is
a well-defined variant of the code that also meets whatever
specifications are in use.
The latter covers things like doing arithmetic in "int" that
deliberately overflow temporarily, assumes that the overflow does
not trap, and then "un-overflows" back into range. If one codes
this in "unsigned int" arithmetic instead, one gets guaranteed
mod-2-sup-k behavior, and the code is just as small and fast as
the not-guaranteed version.
That's one of the main things that I like about assembly language, btw: it
might be all kinds of painful to express an algorithm (although generally
not really all that bad), but the instruction descriptions in
the data books tell you *precicely* what each one will do, and
you can compose your code with no doubts about how it will perform.
Actually, there are a number of instruction sets (for various
machines) that tell you to avoid particular situations with particular
instructions. Consider the VAX's "movtuc" ("move translated until
character") instruction, which takes a source-and-source-length,
destination (and destination-length?), and translation-table. The
manual says that the effect of the instruction is unpredictable if
the translation table overlaps with the source (and/or destination?).
Someone put a comment into a piece of assembly code in 4.1BSD that
read "# comet sucks". I wondered what this was about.
It turns out that whoever implemented the printf engine for the
VAX used "movtuc" to find '%' and '\0' characters, and did the
movtuc with the source string having "infinite" length (actually
65535 bytes, the length being restricted to 16 bits) so that
it often overlapped the translation table. On the VAX-11/780,
this "worked right" (as in, did what he wanted it to). On the
VAX-11/750 -- known internally as the "Comet" -- it did not behave
the way he wanted. The result was that printf() misbehaved for
various programs, because the assembly code depended on
undefined behavior.
(The "fix" applied, along with the comment, was to limit the length
of the source so as not to overlap the table. Of course, when we
rewrote the printf engine in C for portability and C89 support, we
stopped using movtuc entirely.)