How does assert benefit your code really?

L

lovecreatesbeauty

Besides printing out for example

" a.out: p113.c:8: main: Assertion `0' failed.
Aborted "

and a switch option NDEBUG, what other benefits does assert() provide
in any scope of designing, debugging/coding and/or testing?

Do you prefer the if statement of the language to the assert MACRO of
the precompiler?
 
K

Kiru Sengal

lovecreatesbeauty said:
Besides printing out for example

" a.out: p113.c:8: main: Assertion `0' failed.
Aborted "

and a switch option NDEBUG, what other benefits does assert() provide
in any scope of designing, debugging/coding and/or testing?

Do you prefer the if statement of the language to the assert MACRO of
the precompiler?

assert is usually used to catch logical errors in your program, that if
deemed nonexistant after proper testing (noticing that the assert never
fires), would not require the error checking anymore in your finished
product
 
P

Pedro Graca

lovecreatesbeauty said:
Besides printing out for example

" a.out: p113.c:8: main: Assertion `0' failed.
Aborted "

and a switch option NDEBUG, what other benefits does assert() provide
in any scope of designing, debugging/coding and/or testing?

Do you prefer the if statement of the language to the assert MACRO of
the precompiler?

[newbie answering]
To me, assert() and if() are two completely different things, each used
for completely different reasons.

#include <assert.h>
int main(void) {
assert(argc >= 2); /* wrong use of assert() */
if (CHAR_BIT > 8) { /* wrong use of if() */
exit(EXIT_FAILURE);
}
return 0;
}


Or, for a more likely example, let's say you have a function that sums
all the int's in an array of some size. In the function description you
say the array must not be empty.

int sum_array(const int * array, size_t elems) {
int x = 0;
assert(array != NULL);
while (elems--) x += *array++;
return x;
}

When this code is compiled with NDEBUG defined there will be no checks
in place to catch programmer's mistakes.
 
B

Bill Pursell

lovecreatesbeauty said:
Besides printing out for example

" a.out: p113.c:8: main: Assertion `0' failed.
Aborted "

and a switch option NDEBUG, what other benefits does assert() provide
in any scope of designing, debugging/coding and/or testing?

Do you prefer the if statement of the language to the assert MACRO of
the precompiler?

Assert can be your best friend. For example, if instead of:
int
foo(int x, int y)
{ /* Return some function of x and y. Remember, x needs
to be between 5 and 10, and y must be between 15 and 150
or this will segfault!! */
....
}

you write:
int
foo(int x, int y)
{
assert(x > 5 && x <=10);
assert (y >=15 && y<150);
....
}

You get 2 major benefits.
1) Removes the ambiguity inherent in the English description
regarding whether the endpoints are inclusive,
2) When some code is changed so that a caller is using
the wrong arguments, you will be instantly aware of it
when you run, instead of getting a random segfault and
having to track down what caused it.

Also, placing assertions makes it clear to the maintainer
what the coder expected. Any time you expect something
to be true, place an assertion. If it fails, you either have
a coding error somewhere, or your expectation is wrong.
Either is good to know.

Assert is better than if for this type of thing, because it goes
away when you compile your non-debug version. You can
accomplish that with precompiler wrappers around your
if statements, but it is aesthetically unappealing in most
cases.
 
J

John Devereux

lovecreatesbeauty said:
Besides printing out for example

" a.out: p113.c:8: main: Assertion `0' failed.
Aborted "

and a switch option NDEBUG, what other benefits does assert() provide
in any scope of designing, debugging/coding and/or testing?

Do you prefer the if statement of the language to the assert MACRO of
the precompiler?


One thing you must be really careful of is that the assert() call must
not have any side effects. Otherwise, even after all your extensive
testing is done, the final build will be different and may fail. And
because it only fails when the asserts are gone, you won't know where
the problem is!

for example,

....
unsigned char *fred;
assert((fred = malloc(1000)) != 0);
memset(fred,0,1000);
....

would be a disaster since no memory would get allocated in the release
build.

A stupid mistake... but I did it once!

Also in embedded systems or time or memory critical code, the overhead
of the asserts may be too high to even run the program properly. For
example assert() may bring in printf and the rest of the stdio
library.
 
P

pemo

John said:
One thing you must be really careful of is that the assert() call must
not have any side effects. Otherwise, even after all your extensive
testing is done, the final build will be different and may fail. And
because it only fails when the asserts are gone, you won't know where
the problem is!

for example,

....
unsigned char *fred;
assert((fred = malloc(1000)) != 0);
memset(fred,0,1000);
....

would be a disaster since no memory would get allocated in the release
build.

A stupid mistake... but I did it once!

Also in embedded systems or time or memory critical code, the overhead
of the asserts may be too high to even run the program properly. For
example assert() may bring in printf and the rest of the stdio
library.

A very good point about the 'type' of things that should/should-not be
/asserted/.

For example, should this be /asserted/ ...

char * p = malloc(10);

assert(p != NULL);

malloc() can legitimably return NULL, so, is this worth asserting, or should
one really build in a proper test, and, if the test proves negative, the
more difficult 'recovery'?

char * p = malloc(10);

if(NULL == p)
{
// Now what? It's all too easy to call abort() etc?
}
 
I

Ian Collins

John said:
Also in embedded systems or time or memory critical code, the overhead
of the asserts may be too high to even run the program properly. For
example assert() may bring in printf and the rest of the stdio
library.
That's where it pays to roll one's own assert macro. If the device has
no means to display the message, don't.
 
M

Manuel Tobias Schiller

lovecreatesbeauty wrote:
assert is usually used to catch logical errors in your program, that if
deemed nonexistant after proper testing (noticing that the assert never
fires), would not require the error checking anymore in your finished
product

Sometimes, leaving in a few of those assert statements in the finished
product is a good idea as well because it will give a better indication
of where things fail in production builds (because of misuse of the
program, not buggy coding). I've seen this method being useful in the
printer driver I maintain (a driver to use a certain GDI printer under
*nix-like OS), so I thought sharing this "trick" with others (who might
not yet know it) might be a good idea.

Regards

Manuel
 
J

John Devereux

Ian Collins said:
That's where it pays to roll one's own assert macro. If the device has
no means to display the message, don't.

That's what I usually do. For some platforms I set it to just halt the
program, then at least you can usually see what happened in a
debugger.
 
S

Stephen Sprunk

Manuel Tobias Schiller said:
Sometimes, leaving in a few of those assert statements in the finished
product is a good idea as well because it will give a better indication
of where things fail in production builds (because of misuse of the
program, not buggy coding). I've seen this method being useful in the
printer driver I maintain (a driver to use a certain GDI printer under
*nix-like OS), so I thought sharing this "trick" with others (who might
not yet know it) might be a good idea.

Robust code should have all kinds of error-checking built in so that if the
impossible happens, it can be silently dealt with. Aborting a production
build in customers' hands is rarely the correct answer.

assert() is useful because, whenever you code in those checks to handle the
impossible, you can put an assert() right before them and crash the code in
a development build (where such is somewhat expected). This prevents bad
things from silently "working" as they would in the production build.

For instance, most of the time when I write a function that is documented to
accept only a non-NULL pointer as a parameter, it'll look like this:

void func(void *param) {

assert(param);
if (!param) return;

/* do useful stuff with param */

}

Anyone using my function incorrectly in a debug build will get a nasty error
and crash making it obvious what they did, whereas if it's due to a bug that
never appeared in testing, the production behavior at least has a chance of
being correct.

One might also redefine assert() to print a message to stderr in production
builds instead of having the test preprocessed out. That's the best of both
worlds.

S

--
Stephen Sprunk "Stupid people surround themselves with smart
CCIE #3723 people. Smart people surround themselves with
K5SSS smart people who disagree with them." --Aaron Sorkin

*** Free account sponsored by SecureIX.com ***
*** Encrypt your Internet usage with a free VPN account from http://www.SecureIX.com ***
 
I

Ian Collins

Stephen said:
Robust code should have all kinds of error-checking built in so that if
the impossible happens, it can be silently dealt with. Aborting a
production build in customers' hands is rarely the correct answer.
Unfortunately, in embedded systems it is often the only option. A
(quick) reset of the device is often preferable to a lock-up or other
undefined behaviour.
 
I

Ian Collins

John said:
That's what I usually do. For some platforms I set it to just halt the
program, then at least you can usually see what happened in a
debugger.
On embedded devices, I prefer to log an error message (if possible) and
reset. At least with a log, service personnel can see what has happened
to the device if it is returned as faulty.
 
C

CBFalconer

Manuel said:
Sometimes, leaving in a few of those assert statements in the
finished product is a good idea as well because it will give a
better indication of where things fail in production builds
(because of misuse of the program, not buggy coding). I've seen
this method being useful in the printer driver I maintain (a
driver to use a certain GDI printer under *nix-like OS), so I
thought sharing this "trick" with others (who might not yet
know it) might be a good idea.

Nonsense, to put it mildly. The only asserts that should be left
in production code should be intended to catch hardware errors. It
should not be possible to trigger them in any other manner.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
J

John Devereux

Ian Collins said:
On embedded devices, I prefer to log an error message (if possible) and
reset. At least with a log, service personnel can see what has happened
to the device if it is returned as faulty.

Ah, so you leave the asserts in the production code? Not saying that
is always bad, but I think it is not the normal (assumed) usage. AFAIK
the main thing that distinguishes the standard assert() is that it is
removed when NDEBUG is defined.
 
K

Keith Thompson

Stephen Sprunk said:
For instance, most of the time when I write a function that is
documented to accept only a non-NULL pointer as a parameter, it'll
look like this:

void func(void *param) {

assert(param);
if (!param) return;

/* do useful stuff with param */

}

In C90, assert()'s argument is required to be of type int, so this
isn't guaranteed to work (though any reasonable definition of assert()
is unlikely to care). In C99, the argument merely has to be a scalar,
so the call is legal. Nevertheless, I prefer the more explicit:

assert(param != NULL);

both because I find it clearer in the source code, and because the
argument is stringized and used in the error message:

assertion "param != NULL" failed: file "tmp.c", line 6

[...]
One might also redefine assert() to print a message to stderr in
production builds instead of having the test preprocessed out. That's
the best of both worlds.

I'm not sure you can legally do that. What you can do is define an
assert-like macro with a different name.
 
I

Ian Collins

John said:
Ah, so you leave the asserts in the production code? Not saying that
is always bad, but I think it is not the normal (assumed) usage. AFAIK
the main thing that distinguishes the standard assert() is that it is
removed when NDEBUG is defined.
Always expect the unexpected!
 
I

Ian Collins

CBFalconer said:
Nonsense, to put it mildly. The only asserts that should be left
in production code should be intended to catch hardware errors. It
should not be possible to trigger them in any other manner.
I disagree, they can be useful to catch error conditions that slipped
through testing.
 
K

Keith Thompson

Ian Collins said:
I disagree, they can be useful to catch error conditions that slipped
through testing.

Agreed. Asserts should not be used in production to handle errors
that can actually happen (failing to open a vital file, running out of
memory), but it's reasonable IMHO to use them for
this-should-never-happen errors (that can show up only if there's a
bug in the program).
 
P

pinkfog

lovecreatesbeauty said:
Besides printing out for example

" a.out: p113.c:8: main: Assertion `0' failed.
Aborted "

and a switch option NDEBUG, what other benefits does assert() provide
in any scope of designing, debugging/coding and/or testing?

Do you prefer the if statement of the language to the assert MACRO of
the precompiler?
assert just let you avoid the errors which are not expected to happen.
you use if statement or try_catch statement to mend the errors which
may happen by accident.
Hope what i say is right.
 
M

Michael Mair

Keith said:
Agreed. Asserts should not be used in production to handle errors
that can actually happen (failing to open a vital file, running out of
memory), but it's reasonable IMHO to use them for
this-should-never-happen errors (that can show up only if there's a
bug in the program).

Depends; at my day job, we have "internal" and "release" "asserts".
The former are used as debugging aid in the debug version, i.e.
....
else
{
INTERNAL_ASSERT(0);
return F;
}
where F is a return value indicating failure for a function which
must not return F in normal circumstances. Even some of the paranoia
checks work this way. There are some places in the code where
assumptions that are integral and should always be completely true
but for hardware errors or some sneaky scenario that one can ask
for the permission to use a release assert instead. These are rare.
Usually we have an orderly "death" with numbered error messages even
for some more obscure cases.


Cheers
Michael
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top