assert in C

C

cuiyouzhi0000

Dear all,

I wrote a C program with assert ( str != NULL ) to check char * str is
NULL or not.

then I transferred str with NULL to assert, and I used gcc -g ... to
compile it, sure, I got
assertion.

While,
1. When I used gcc -O to compile the same program, I got the assertion
as well.
2. When I added #define NDEBUG after #include<assert.h>, used gcc -g
to compile the program,
I got the assertion as well.

I knew, if we used -O or define NDEBUG, then assert statement will not
be run. but seems I am wrong or someone was wrong?

Anyone can help me?
 
S

Seebs

While,
1. When I used gcc -O to compile the same program, I got the assertion
as well.
2. When I added #define NDEBUG after #include<assert.h>, used gcc -g
to compile the program,
I got the assertion as well.
"after"

I knew, if we used -O or define NDEBUG, then assert statement will not
be run. but seems I am wrong or someone was wrong?

Try defining NDEBUG *before* including <assert.h>.

-s
 
C

cuiyouzhi0000

Try defining NDEBUG *before* including <assert.h>.

-s

thanks, you are right, after I defined the #define NDEBUG before
#include<assert.h>, assert statement doesn't work yet.

while, how about the -O option in gcc? can this make assert not
workable?
 
M

Michael Tsang

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

thanks, you are right, after I defined the #define NDEBUG before
#include<assert.h>, assert statement doesn't work yet.

while, how about the -O option in gcc? can this make assert not
workable?

The -O option in gcc must not change the behaviour of a program (unless it
has undefined or unspecified behaviour).
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)

iEYEARECAAYFAks0xggACgkQG6NzcAXitM+uOwCfQWH0/eH9IO/EyN7XvmRfpx18
kigAn3aILVbsQrHjZZXMRFle/wrQiiGl
=ewEl
-----END PGP SIGNATURE-----
 
K

Keith Thompson

thanks, you are right, after I defined the #define NDEBUG before
#include<assert.h>, assert statement doesn't work yet.

while, how about the -O option in gcc? can this make assert not
workable?

No, the "-O" option doesn't affect the behavior of assert. Why did
you think it would?
 
C

cuiyouzhi0000

No, the "-O" option doesn't affect the behavior of assert.  Why did
you think it would?

--
Keith Thompson (The_Other_Keith) (e-mail address removed)  <http://www.ghoti.net/~kst>
Nokia
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

OK, then if I compile the c program in release mode, the assert
statement should not workable, is it wrong?
If it is wrong, then how to disable the assert statement by gcc?
 
N

Nick

[Keith's signature snipped]
OK, then if I compile the c program in release mode, the assert
statement should not workable, is it wrong?
If it is wrong, then how to disable the assert statement by gcc?

Can you avoid quoting people's signatures please?

That's right (although I remember heated debates about whether removing
asserts from releases is a good idea or not). But the way you do it is
not by changing optimisation level (I develop at high optimisation
levels - partly because of the better checking I get from the compiler -
only turning it down if debugging goes funny). You do it just the way
you've shown, by defining NDEBUG. You can do that with a compiler
option, you don't have to wire it into the program (-DNDEBUG for gcc).
 
C

cuiyouzhi0000

OK, thanks

It is. asserts suck and are the sign of sloppy programmers who only use
them to appear to be concerned about reliability. An assert condition
should simply not occur.

I don't agree that assert condition should simply not occur.
such as, assert NULL pointer, I think it can occur at high
possibility.

So, gcc -NDEBUG -g *.c *.c -o * will work without assert?
 
E

Eric Sosman

I don't agree that assert condition should simply not occur.
such as, assert NULL pointer, I think it can occur at high
possibility.

The claim that "asserts suck" sucks, but their purpose must
be properly understood: They are useful to the programmer, but
not to the end user. They can alert the programmer to programming
errors, but are not good for things like validating user input.

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!
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.
 
K

Kenny McCormack

Richard said:
Asserts suck because of the termination. In 99% of test cases where that
assert triggers you can also merely run the program in a debugger and
get a backtrace there and then and "live debug". Asserts, like
littering code with printfs, is amateur and sucks in most cases I have
come across them polluting the code.

Indeed. Asserts, like the regs in this newsgroup, are fossils left over
from a day and age where debuggers didn't exist.
 
K

Keith Thompson

OK, then if I compile the c program in release mode, the assert
statement should not workable, is it wrong?
If it is wrong, then how to disable the assert statement by gcc?

It depends on what you mean by "release mode". That's not a term
defined either by the C language or, as far as I can tell, by gcc.

Defining the macro NDEBUG before the "#include <assert.h>"
disables asserts. You can do that by adding "#define NDEBUG"
to your source code, or by using some compiler-specific option.
For gcc (and some other compilers), the option is "-DNDEBUG".
 
C

cuiyouzhi0000

     The claim that "asserts suck" sucks, but their purpose must
be properly understood: They are useful to the programmer, but
not to the end user.  They can alert the programmer to programming
errors, but are not good for things like validating user input.

     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.

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.

I am clear now, I think. We should design another
way to handle error code, such as by return status. Am I right?
     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.
 
N

Nick

Richard Heathfield said:
In
<bccb957c-6c21-423d-9d65-2fdf2452078f@u16g2000pru.googlegroups.com>,
(e-mail address removed) wrote:



Assertions were never intended to handle errors. Their purpose is to
allow you to make explicit your assumptions about the state of your
program at a given point so that, if ever a situation arises where
those assumptions don't hold, you can get the program to tell you
about it - by aborting with a message giving you the file name and
line number of the broken assumption.

Nobody would ever want to use such a cumbersome "all or nothing"
mechanism for error-handling.

p = fgets(buf, sizeof buf, stdin);
assert(p != NULL); /* daft */

but for assumption-checking, it works well:

new = tree_insert(&tree, data, sizeof data);
if(new != NULL)
{
assert(tree_balances(&tree));
/* ... if okay, carry on... */
}
else
{
/* ...handle insertion error ... */

That also provides a bit of documentation - on reading the code it's
obvious that the programmer expects the tree to be balanced after
insertion, so you don't sit there thinking "I wonder if tree_insert
always returns a balanced tree?".
 
S

Squeamizh

Eric Sosman said:
[...]
It is. asserts suck and are the sign of sloppy programmers who only use
them to appear to be concerned about reliability. An assert condition
should simply not occur.
I don't agree that assert condition should simply not occur.
such as, assert NULL pointer, I think it can occur at high
possibility.
     The claim that "asserts suck" sucks, but their purpose must
be properly understood: They are useful to the programmer, but
not to the end user.  They can alert the programmer to programming
errors, but are not good for things like validating user input.
     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!
     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.

Asserts suck because of the termination. In 99% of test cases where that
assert triggers you can also merely run the program in a debugger and
get a backtrace there and then and "live debug". Asserts, like
littering code with printfs, is amateur and sucks in most cases I have
come across them polluting the code.

That doesn't make sense. A lot of times the assert gets triggered
under a very specific set of conditions that might not even be
something you can test in your lab environment. Running the program
in a debugger does nothing to reproduce that kind of problem.

Consider a watchdog task in an embedded system. If a task is hung due
to a unforeseen set of peculiar circumstances, the periodic assertion
that all tasks have checked in can be invaluable. I can't think of
any obvious alternative to debugging this kind of problem.

Where I work, a failed assertion generates a coredump in our
production code. The ability to see what state the device was in when
something has gone unexpectedly wrong in the field has proven to be
extremely valuable. It isn't about programmer laziness; quite the
opposite. Diligent sanity checking through the use of asserts is a
means of proactively providing visibility into the problems that will
invariably arise one the product is put to actual use.
 
E

Eric Sosman

[...]
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.
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 ...
 
B

Beej Jorgensen

"I dont need to rigorously test my code flow and ensure things are
only called with valid parameters (eg non null pointers) because if
they are called with null pointers the assert will trap it". It's
for the lazy.

Bah, I just tripped one of my asserts just the other day. There were
two entities in the game that would interrogate one another for
information. I added a new state to one of the entities, but forgot to
prep the other one for it.

When it ran, the second entity saw the new state, didn't know what to do
with it, and asserted. And I thought, "Oh yeah--this guy needs to react
to that."

Whether lazy, sloppy, or just too late at night, I'm glad I put that
assert in there.

I agree that programmers should know to not rely on asserts for
production code.

-Beej
 
C

cuiyouzhi0000

     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;

looks debugLevel here was assigned at other place.
        #define ASSERT(level,truth) \
            assert(debugLevel < (level) || (truth))

ASSERT can be controlled by debugLevel
        ...
        session = idToSession(sessionID);
        ASSERT(RELEASE, session != NULL);
        ...
        ASSERT(PARANOID, expensiveSanityCheck());
        ...

... can be used for the "tagging."

Good way to control assert in large project indeed.
While, I donot
     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.

Good summary about "error", for the first 2 items, we used assert to
debug our programs,
for the others, we really need the error handling method.

So, I agree with you about assert:

* assert can be used in debug and release mode, you can decide by your
case ( such as, control assert by ASSERT above, or disable assert for
performance reason)
* assert often used to debug the wrong stuff in programs, such as
logic error in program and implement error, while, user error and
other env errors should be handled by error code or exceptions.
* -DNODEBUG is used to disable assert by gcc, or define NODEBUG macro
before #include<assert.h>

Anything else need to add?
 
N

Nick

Richard Heathfield said:
Beej Jorgensen wrote:



Likewise. They are a development-time tool. The ease with which they
can be disabled is wonderful, as it means that we can have all the
benefits of assertions during development, without any of the hassles
- extra code, extra time - in the production version.

You will, however, not be surprised to learn that there is a
considerable body of opinion to the effect that assertions should be
left *on* in production code. The analogy that this view's supporters
like to trot out is "you wouldn't take the lifeboats off a ship before
sending it out to sea"... which just goes to show that proof by
analogy is fraud. The counter-"proof" is that assertions are like
scaffolding -
it's very useful while the building is undergoing construction or
repair, but you wouldn't want to leave it up during normal usage.

I think one of the reasons for the heat is that there are two different
reasons to use assert - and the arguments apply differently to each.

One is where you are carrying out a very-near-free check: that a pointer
passed to a function isn't NULL for example. Without the assert the
function is going to blow up when you dereference the NULL pointer, so
adding the assert just documents clearly that the pointer mustn't be
NULL, and guarantees a particular failure mode if it gets that way
through a bug elsewhere in the program. These seem good reasons to
leave this sort of assert turned on.

The second is your tree-balance-checking example, where there is
presumably some reasonable cost to carrying out the check. In that case
turning it off in the production code for efficiency reasons makes
sense. Also, perhaps, you might in many cases get away with the tree
being unbalanced, so blowing up in the face of the user isn't the best
response. These seem good reasons to turn this sort of assert off.

In fact, thinking about and writing this makes me wonder about using
assert for the latter and a simple if/abort() line for the former.
 
C

cuiyouzhi0000

(e-mail address removed) wrote:




If you need to leave the assertions in your production code, it is not
yet ready for release.

If I want to keep the assert statements in production code, what else
do
I need to do?
One might think of it this way: assertions are intended to reveal bugs
in the program, not bugs in the data. A wrong program needs the
programmer's attention, but wrong data is, at least in theory,
susceptible to correction by the user, so one should make it
(relatively) easy for the user to correct the data, for example by
offering another opportunity to provide it. One does not do this by
bombing out of the program.

classify by program error and data error is better.
In another of those mildly infuriating abbrvs, it's NDEBUG, not NODEBUG.


Or, slightly more traditionally and readably:

#include <assert.h>

Thanks for correcting the details.
Yes: don't believe everything you read.
sorry, I don't understand this line.
 
E

Eric Sosman

[...]
You will get conflicting advice from different sources. Don't assume
that every source is reliable. Many are not.

Also, when two sources disagree, do not assume that
one (at least) of them is unreliable. Even experts differ.

<off-topic reason=anecdote>

A customer once got upset because he heard differing advice
from a software vendor and from my employer about how to tune
my company's hardware to run the other company's software. I
used to employ automotive analogies in such cases, pointing
out that different sources give different advice about how
often to change the engine oil, what kind of oil to use, and
so on. But since this customer was in the automotive industry
I thought perhaps I should avoid that analogy, and went looking
for another.

To prepare for the conference call where this "scandal"
would be aired, I pulled out three cookbooks and looked up their
recipes for guacamole. On the call, I listed the credentials of
the cooks and chefs responsible for the recipes, to establish
their expertise in matters culinary, and then pointed out that
the three books called for different amounts of lemon juice (on
a per-avocado basis). Indeed, one of them used no lemon juice
at all, but recommended lime juice instead!

The point was made: Experts can disagree. And to this day
that customer knows me as The Guacamole Man.

</off-topic>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,046
Latest member
Gavizuho

Latest Threads

Top