What are asserts good for?

The ASSERT() macro (assert() in userland) is one of the more useful testing tools you can build into your code. By crashing into a debugger the instant one of your assumptions is violated, ASSERT() gives you a great chance to catch a caller doing something it shouldn’t, or to cause you to re-evaluate your assumptions.

I see a lot of first-time ASSERT users do something like this:

char x(char *a)
{
	ASSERT(a);

	return *a;
}

Yes, this ASSERT will verify that you haven’t passed a null pointer to x(), but… so will the next line. If you’re just going to move a guaranteed crash from one spot in the function to another, I’m not sure ASSERT adds much value.

On the other hand, the situation changes when you start masking errors by being fault-tolerant:

char x(char *a)
{
	ASSERT(a);

	if(!a)
		return 0;

	return *a;
}

In this case, the ASSERT is doing something useful – it’s restoring the crash that you would otherwise have masked away, but only in debug builds where you want it.

Another useful form of ASSERT is as a logic check. For instance:

int x(char *a)
{
	/*
	 * It doesn't make sense for the
	 * first character to be NULL
	 */
	ASSERT(*a);

	return *a;
}

This kind of check can be the most useful for finding subtle design problems. Use them liberally during development, and leave them there for the next time you have to work on the code.

And, as an excellent side benefit, ASSERT macros make your code easier to maintain for the next coder who comes along. Not only are your assumptions clearly and precisely documented, but the presence of the ASSERTs will help to prevent the new person from breaking anything by pointing out any problems with changes.

One Response to “What are asserts good for?”

  1. strik says:

    Your 2nd example (ASSERT(a); if (!a) return 0;) contradicts “Do not change program semantics in the debug build” (http://blogs.msdn.com/oldnewthing/archive/2006/08/15/701130.aspx) which has a point in itself, too. Your program behaves differently (crash vs. “recovery”) when being compiled as checked build vs. being compiled as free build, which can make debugging harder.

    I don’t feel the first example to be that bad. I like checking all parameters with ASSERT()s, even if it does not make much sense (now). It might make sense later, when this function is changed.

    But I think ASSERT()s make most sense in a circumstance where they are rarely used: To check loop invariants, thus, to help in proving that a loop does exactly what it is expected to do.

    - Spiro.

Leave a Reply