Using Assertions for Robust Software Development
During the course of the software development in C, there are deliverables for customers and deadlines, but developers will find bugs and issues in their design or execution, or both. How do software developers build programs and maintain it across these years with changing feature requests? On a simpler level, how do change software, code – programs, and algorithms – without losing the original functionality?
Software development learns from a maxim, ‘History repeats itself, if you don’t learn from your mistakes‘. In this blog post, you will learn about writing a simple C program and updating it as requirements change all the time using unit tests to capture this behavior. This could be your beginning, exciting baby steps to advanced graphics programming, and game development!
Meet the Assert Macro
If you a beginning C user, you may benefit from using the C compiler in small steps. In Windows, you can use the Mingw GNU tool chain, Borland C/C++ compilers or Microsoft Visual Studio, or the shell in Linux. Start by typing in the shell,
$ gcc --version
and you should see the following output, then you are ready to dive into C development.
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 Copyright (C) 2011 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Follow along by typing into a file these lines of code and save it as ‘assert_true.c’
#include <stdio.h> #include <assert.h> typedef unsigned char bool; bool TRUE = 1; bool FALSE = 0; int main() { assert( TRUE ); assert( 1 + 1 ) ; assert( 1 + 1 > 0 ); assert( 1 + 1 – 4 ) ; }
To compile and execute the program you can run the commands,
$ gcc assert_true.c -o assert_true $ ./assert_true
And you can see no real output except the next prompt, which means you code did not trigger any assertion faliures. Lets try again with another program, and save it as ‘assert_fail.c’
#include <stdio.h> #include <assert.h> typedef unsigned char bool; bool TRUE = 1; bool FALSE = 0; int main() { assert( FALSE ); assert( 1 - 1 ) ; assert( 1 - 1 > 0 ); assert( 1 - 1 - 4 > 0 ) ; }
To compile and execute the program you can run the commands,
$ gcc assert_fail.c -o assert_fail $ ./assert_fail assert_fail: assert_fail.c:10: main: Assertion `FALSE' failed. Aborted
Your empirical experiments have shown you something about assert macro; i.e. on True evaluation of expression assert is quiet, on false evaluation of the statement it triggers the assert_fail and aborts the program execution.
Syntax of Assert Macro
The assert is a macro in C language. To include the assert function in your program you can add the header file definition,
#include <assert.h>
where the assert macro evaluates the expression and aborts execution when the said expression has a false value in C. If the expression evaluates to NULL, false, 0, then assertion is said to have occurred. Only calls like setjmp, and longjmp and atexit have a change of completing after the assert() has triggered. For more details read the manual page on Linux for assert function.
$ man assert
should bring up a page like,
ASSERT(3) Linux Programmer's Manual ASSERT(3) NAME assert - abort the program if assertion is false SYNOPSIS #include <assert.h> void assert(scalar expression); DESCRIPTION If the macro NDEBUG was defined at the moment <assert.h> was last included, the macro assert() generates no code, and hence does nothing at all.
Program Design
Programmers use the assert macro macro to ensure the result of factorial is always positive.
Clearly a way of writing this as a program in iterative and recursive ways would be,
#include <stdio.h> #include <assert.h> typedef unsigned char bool; bool TRUE = 1; bool FALSE = 0; /* recursive way of writing factorial */ int fact( int n ){ if ( n == 0 ) { return 1; } return fact( n - 1)*n; } int main() { int fact_val; int i; for( i=0; i < 10; i++ ) { fact_val = fact(i); /*factorial is always non-zero*/ assert( fact_val > 0 ); printf("%d! = %d\n",i,fact_val); } return 0; }
Running the Program
You can download C package for your platform from the source website, C.org, and run the tests and programs as, $gcc fact_assert.c -o fact_assert && ./fact_assert, is the command to interpret the code and then run the program
0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880
Bugs and Fixes
There are various bugs in the function in the program few of which are,
Function does blows up with negative inputs. You need to have include guards or asserts.
Numerical overflow for large input n
It is left as an exercise to the reader to update the code for the following and run it on the C interpreter to see a message which shows the 2 unittest have passed, and happy ‘OK’ message is printed on screen.
Summary
Software developers use assertions to enforce invariants in the design of programs, and testing those programs against regression. The C assert macro is one of key tools to enforce invariants in code, and in testing enabling users and developers of programs to progress without regression. Learn more about regression testing and software design. Learning C can lead you to other things like programming your iPhone, and more complex projects.