On 12/26/2009 11:37 PM,
wrote:
> On Dec 27, 12:08 am, Eric Sosman<esos...@ieee-dot-org.invalid> wrote:
>> [...]
>> Since an unsuccessful assert() terminates the program (unless
>> you're doing tricky things with SIGABRT), an assert() that fires
>> with "high probability" means the program dies with that same
>> high probability -- which indicates that the program is not very
>> useful, because it keeps dying!
>
> Then you thought that the assert can be used to debug the program,
> not for error handling.
Yes, more or less. The actual effect of an assert() is
to cause a programming error to behave predictably and quite
visibly, which is useful as a starting point for debugging.
> In the debug side, we used assert to check if all tests passed or
> not,
> if passed, then we can disable the assert statements in nodebug mode
> for
> it cost any time.
There are two (or more) schools of thought on this topic.
One approach sprinkles assert() liberally throughout the code
during development and then disables them all for "release"
versions or when performance measurements become important.
Another says "ship what you tested" and leaves the assert()
calls enabled even in the final product, the idea being that
the end user may do things the test suite didn't anticipate.
In large projects a blended strategy may be used: Load the
code with tons and tons of assert() calls during development,
tagging each with an "importance" or "level." In the release
version, disable the less important calls but leave the critical
assertions intact. Sometimes a wrapper along the lines of
extern enum { RELEASE, NORMAL, PARANOID } debugLevel;
#define ASSERT(level,truth) \
assert(debugLevel < (level) || (truth))
...
session = idToSession(sessionID);
ASSERT(RELEASE, session != NULL);
...
ASSERT(PARANOID, expensiveSanityCheck());
...
.... can be used for the "tagging."
> I am clear now, I think. We should design another
> way to handle error code, such as by return status. Am I right?
It depends on what you mean by "error." Usually, there
are many possible kinds of error, including (but not limited to)
- Logic errors: The programmer reasoned incorrectly or from
incorrect precepts, so the code does not behave as desired
- Implementation errors: The programmer chose the right
algorithm, but slipped up in coding it
- Environmental errors: The code is fine, but for some reason
the "configfile.dat" file can't be opened
- User errors: While running the program, the user entered
his date of birth as 1964-02-30
An assert() can be helpful in cases like the first two, but is
probably not appropriate for the final two.
>>> So, gcc -NDEBUG -g *.c *.c -o * will work without assert?
>>
>> The command line as shown will probably not work at all.
>> Changing it to something like "gcc -DNDEBUG -g *.c" will cause
>> the NDEBUG macro to be defined at the start of each .c file's
>> compilation. If it's still defined when<assert.h> is included
>> (that is, if you don't #undef it first), the assert() calls in
>> that file will expand to no-ops that have no effect on the
>> program's execution.
>
> Ah, yes, we should add -D before NDEBUG, thanks.
You might also want to review just how many .c files are
being compiled, and where the -o sends the output ...
--
Eric Sosman
lid