Keep at least two versions of the code

You will want a fast version, compiled with optimization turned on.  This version will not have symbol tables included, so it will not be possible to use the debugger if it crashes.  The macro NDEBUG should be set TRUE in this version.

You will also need a debugging version.  It should have the macro DEBUG set true, to turn on asserts (described next).  In Microsoft VC++ this will also set uninitialized variables to garbage so that you can detect variables that are used before they are defined.

It is possible to create Cloudy in a version that will print a statement when each routine is entered and exits.  This output is generated if DEBUG_FUN is set TRUE.  The output is very long and is only used when the debugger cannot find why the code crashes.

The assert macro

Use asserts to validate internal code logic, not user input data.  In the following example the routine confirms that it has been called with the proper charge:

#include <assert.h>
void routine( long ipZ )
    assert( ipZ > 0);
    assert( ipZ <= 30 );
    code code code ---

If the routine is called with ipZ outside the range 0 to 30 the system will stop with a run time error, giving the routine name and line number.  The assert macro is only active if the macro NDEBUG is not defined.  In practice two versions of a code are kept, the version compiled for debugging with NDEBUG set to false, and a second version with a high level of optimization and NDEBUG set TRUE.  The assert macro will have no effect in the second case, so these can be freely placed throughout a code with no impact on the execution speed of the fast version.

Debugging code

The same NDEBUG macro can be used together with the proprocessor to put in code that is only executed in the debug version of the code.

debugging blocks

Variables defined within a block only exist within that block.  You can put special debugging code within a block so that it does not disturb other code.

    enum {DEBUG=FALSE};
    if( DEBUG )
        static int count = 0; /*only exists here*/
        fprintf( stderr , "called %i times\n",count);

In this example, the printout only occurs when DEBUG is set TRUE.  The compiler will check the code for syntax no matter what value DEBUG has, but a good compiler will know not to generate the block of code when FALSE appears.

commenting out code

Use a preprocessor directive to comment out code:
#if 0
this code never used, since 0 is false

debugging hooks into the program

You can call any routine inside the debugger.  Write special routines that are there only to be used in the debugger.

writes to stderr are not cached

Normal writes are to cashed output, and will disappear if the code crashed, unless the cache is flushed.  Writes to stderr are not cached, are much slower, but will not disappear.