51

I am writing a function in C. As a matter of style, when is it good to use assert compared to returning an error code. Lets say the function is dividing two numbers. Should I assert the divisor be non-zero or should I return an error code? Please give more examples, if you can, to make the distinction clear.

4
  • See this answer Commented Nov 13, 2011 at 19:24
  • Dup? - See stackoverflow.com/questions/1081409/why-should-i-use-asserts Commented Nov 13, 2011 at 19:25
  • asserts only work in debug mode, no? So they're for testing only. I'd post that as an answer because I know it's the case for C++, but I'm not sure for C. Commented Nov 13, 2011 at 19:25
  • 9
    The two links in the comments above are to questions referring to C++ exceptions. Not an option in C. Commented Nov 13, 2011 at 19:25

10 Answers 10

63

assert aborts the process, but is turned into a no-op when the program is compiled with -DNDEBUG, so it's a rather crude debugging tool and nothing more than that. You should only use assert to check for situations that "can't happen", e.g. that violate the invariants or postconditions of an algorithm, but probably not for input validation (certainly not in libraries). When detecting invalid input from clients, be friendly and return an error code.

An example use of assert could be: you've implemented an incredibly smart sorting algorithm and you want to check whether it really sorts. Since the sorting function is supposed to "just work" and therefore doesn't return a value, you can't add error returns without changing the API.

void sort(int *a, size_t n) { recursive_super_duper_sort(a, 0, n); assert(is_sorted(a, n)); } static bool is_sorted(int const *a, size_t n) { for (size_t i=0; i<n-1; i++) if (a[i] > a[i+1]) return false; return true; } 

In the long run, you'd really want a proper unit testing framework for this kind of thing instead of assert, but it's useful as a temporary debugging tool.

Sign up to request clarification or add additional context in comments.

Comments

24

In general, asserts are for the programmer (i.e. you) to find logic/programming errors before releasing the program to real users. Asserts should not be used for detecting runtime input errors -- use error codes for these.

Comments

20

An error code signals runtime behaviour. An assertion is a debugging tool that allows the developer to assert that their assumptions about the program logic are indeed true.

They're two completely different things with different applications.

Error codes are part of your normal program flow. Assertions are only for debugging, and if an assertion is triggered, that means that your program is not written correctly.

Comments

12

The conventional wisdom is to use assert() to help debug your code, to warn you when something "impossible", something that must not happen, has happened. This "warning" takes the form of exiting your program.

I've heard Jim Coplien (general C++ guru and SCRUM trainer) advocate leaving your asserts active in deployed code. (It sounds crazy, I know...) This was specifically for high-reliability server code. The motivation was that it's better to fail, hard, and let another node take over, than to tolerate your server being in an "impossible" state.

(And of course, track the failures, and analyze them. It means there is a bug or incorrect assumption.)

1 Comment

I fully agree with leaving asserts active in production code! I think it's stupid that the jvm defaults to the inverse. Your program cannot/should not/must not continue after an assert triggers!
11

This is really a matter of taste. Here is my opinion.

The main rule of thumb: an assertion failure is always a bug in the program.

Use an assert to check function parameters if you expect the caller to ensure that the argument is correct and you want to indicate that any other behavior is a bug in the caller. Dividing by zero is, IMO, a very good example.

Use an error code if you expect the caller not to be able to ensure that the argument is correct before calling. For example, it could be very computationally expensive to check the arguments beforehand.

Never use an assert to check user input.

Comments

6

First, assert from the <assert.h> header can be disabled (e.g. by compiling with gcc -DNDEBUG), and sometimes is disabled for the "production" version of a binary.

Second, as stated by Linux man page,

 The purpose of this macro is to help the programmer find bugs in his program. The message "assertion failed in file foo.c, function do_bar(), line 1287" is of no help at all to a user. 

So assert should fail only in buggy situations. In exceptional or error situations, you should do something else.

Some tools (or even compilers) might use assert-ions to e.g. optimize your code.

In your example of a quotient function, you'll use assert if, inside your entire program, you are sure that the divisor should be non-zero (but then it could make sense to name the function differently, perhaps quotient_by_non_zero). If you consider that it could happen, make it a fatal message, an exception (i.e. longjmp in C), an error code, etc.

Comments

4

Since C does not support exceptions you don't have any real option other than to return an error code. A failing C assert() results in abort() being called which bombs the process. That's not really comparable with standard error handling.

For floating point operations you can use NaN to signal error conditions. For integer operations an error code is simply your only option.

2 Comments

Some people (me included) consider that longjmp could be used to implement poor-men's exceptions in C.
@BasileStarynkevitch True, but we are talking about extreme poverty!! ;-)
3

Use an assert when your program meets a situation that does not allow to continue. Assertions are a 'contract' and I use them as a 'contract' with OS and situation 'danger in delay'.

In order to emulate exceptions you can still use GOTO 'ERRORLABEL' and terminate clean after running a clean up function.

Comments

3

Here's a real-world example of an assertion I wrote yesterday.

I had two parallel arrays — let's call them a and b — and I was about to run an index i from 0 up to the size of a, and then do something with a[i] and b[i]. But if there were fewer elements in b than in a, my code would have an array bounds violation on b. But other parts of the code are supposed to keep the sizes of a and b identical. So I put an assertion before the loop, asserting that the sizes of a and b were equal.

The sizes should be equal — but if somehow they're not, I want the code to fail on the assertion that tells me why, rather than failing strangely on the Undefined Behavior that would result when I tried to read beyond the end of array b, if it were smaller.

Comments

2

The essence of assert is to catch errors that the programmer believes should never happen, but may happen

Let us see it in action and why it helps us.

We have designed a function with a control statement to test whether our animal is a dog or a cat. Our code only deals in dogs and cats, and as a programmer, we believe the else statement will never execute. But it may execute despite our designs due to an unknown error (accidental initialisations and assignments, undetected overflows, etc).

void isDogOrCat(T_animal x){ if(isDog(x)){ printf("Woof\n"); } else if(isCat(x)){ printf("Meow\n"); } else{ assert(false); } } 

If an error happens to materialise, we are immediately pointed to the assertion that failed and our consequent debug attempt is a breeze. Compare this with just having:

 if(isDog(x)){ printf("Woof\n"); } else{ printf("Meow\n"); } 

If a bug slips in here, you are in much bigger trouble. You will think you are passing in a cat on account of the output, but unbeknownst to you, you are dealing with a variable that is erroneously outside your designed bounds!

(One could add in an else if(isCat(x)) instead of the else but in that case, neither statement would execute and the functions wouldnt output anything - again, against our design)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.