Stephen said:
.... snip ...
Another thing I've picked up over the years is to only use assert()
for _impossible_ conditions, not errors. For instance, if you have
a function that takes a pointer and is _documented_ to never accept
NULL, then it's perfectly valid for a _debug_ build to abort when
it gets one. OTOH, a non-debug build should handle the condition
by doing something that makes sense in context and doesn't
crash/abort or otherwise lose user data.
Agreed, but you omit some necessary areas. You simply can't ensure
such 'safe' operation, because the library portions will crash.
You can't use a NULL FILE* to fread, or a NULL char * to strlen,
for example. So you normally attempt to prevent these from arising
early on, by putting the check right at the fopen, or malloc, for
example. However some people get sloppy, and don't install those
checks at first writing. I know it isn't you, but you do have
sloppy apprentices and helpers, don't you? So somewhere along the
line you think you have put all those checks in, and can turn off
the assert. Guess again.
There remains a rarely executed path, that nobody followed, and
that it takes an ingenious fool to trigger. It leads to UB and
heavy loss of data, but no crash. The customer is irate, and you
haven't the vaguest idea what happened. The ingenious fool only
did it by accident, because his girl friend called and mentioned
pregnancy during data entry.
If you had left the assert there would have been a message and a
clue, the heavy data loss wouldn't have occured (after all, you did
allow for power failures and so forth), and the customer, while
still irate, could be reasoned with.
This is why I try to define as much behaviour as possible in my
routines. For example I have been criticized for having my strlcpy
and strlcat versions interpret NULL input (but not output)
parameters as empty strings. My attitude is if you want to check
against NULL, do so before calling. The routine is intended to
work in production, not to rub your nose in your own misdeeds. The
C library that I use here will, when asked to printf a NULL string,
write out "{null}" instead of crashing. That has been known to
give me a clue as to my own misdeeds.
Crashes are bad. Crashes are better than data destruction.
Crashes with messages and no data destruction are better yet. Data
destruction is especially bad, and this includes recording
erroneous data. Some programmers are fallible (even I).