Programs for Programmers

 

My Program Doesn't Work!

Sometimes program bugs are just logic errors, which can be explained easily - perhaps the wrong formula is used, or operations are done in the wrong order.  Often though, errors seem to defy logic; a program may produce different results each time it runs, or crash for no apparent reason.  The following table attempts to classify the commonest causes of this sort of error, with particular reference to Fortran programs.  

Fortran compilers differ greatly in their ability to diagnose some of these errors, just as they vary in the speed of generated code (there are tables elsewhere on this site which show the capabilities of many current compilers).  Many professional programmers find that it helps to have two or more compilers available.  Typically one might be used for development and debugging, a second for quality assurance, and a third for generating production code.  Other tools, such as static and dynamic analyzers can eliminate whole classes of errors (e.g. Used before Set) from your code.

Problem

Definition

Symptom Diagnosis
Array bound violation. Access to elements above or below the declared bounds of an array.  For example given the declaration

INTEGER arr(10)

then access to arr(0) or arr(11) would constitute an array bound violation.

Anything may happen.  The program may appear to run normally, or may crash, or may behave unpredictably. Most compilers have a compile switch which enables run-time checks for array bound violation, at the cost of slower execution.  

Static analysis is generally ineffective.

Array bound violation - assumed size and shape arrays. Where an array is passed as an argument, and declared using assumed size or shape, as in

INTEGER arr(*) 

it is more difficult to spot array bound violations.

As above. Some compilers have switches to allow this sort of problem to be identified.

Static analysis is ineffective.

Used before Set Error A program uses a variable or array element which hasn't been assigned a value

For example the statement

z(i) = x(i) * y(i) 

has an undefined outcome if y(i) has never been assigned a value.  Note that, z(i) will be undefined, even if x(i) is known to be zero!

As above.  Some compilers silently initialize variables to a predefined value, so that programs behave consistently, but incorrectly. Many compilers can spot some uninitialised variables by static analysis, but this is very far from foolproof.  Other compilers have a run time check which is far more effective.  However most such checks do not operate on global data (in COMMON blocks or modules).  The Salford FTN77 and FTN95 compilers have a run-time check which includes global data.  

See also Note 1 below.

Numerical problems. Because of rounding errors, two mathematically equivalent expressions may give different answers, and one may have much greater errors than another.  For example:

x*y - x*z 

may give a different answer to

x*(y-z)

Answers are always the same for a given executable, but may vary between platforms or compilers. Try a higher precision.  Some compilers have switches to allow automatic precision changes, for example from single to double precision.

The availability of quad precision can be particularly helpful in sorting out this sort of problem.

Optimisation problems. Sometimes optimizers rearrange code to something mathematically, but not numerically the same.  As above. As above - also try changing the optimization level.
Argument mismatch The type of an argument in a CALL statement differs from that in the corresponding SUBROUTINE.  The program will probably behave consistently, but give incorrect answers. Some compilers check for this type of error, but very few will do so if the caller and callee are compiled separately.

Static analysis is effective for this sort of problem.

IMPLICIT typing errors In the absence of "IMPLICIT NONE" a misspelled variable name will wrongly cause a new variable to be created.

INTEGER I0

IO = 2

May result in symptoms of  "Used before Set"  (see above).  Program logic is incorrect. May result in "Used before Set" error.  Otherwise difficult to find in existing code.  Use IMPLICIT NONE in new code.

Static analysis may help.

Unix text file format  Lines in Unix text files are terminated by <LF>.  Windows and VMS systems use <CR><LF>.  Program may not read Unix data files correctly.  Output files may be unacceptable on Unix systems. Use "type filename" to see if file has <CR>s.  If it does not, the output will appear garbled.
Unchangeable Program   "I keep changing the program but nothing I do makes any difference". The program you're running isn't the one you're editing.  Perhaps you forgot to recompile (We've all done it!)
DLL problems A program which requires a Dynamic Link Library (e.g. salflibc.dll or mfc42.dll) links to the wrong version.  Program fails after something else installed.  Program works on one machine but not another.  May even depend of working directory. Copy required version of DLL into directory containing executable file.

The Windows search order for DLLs is summarized below2.

Allocation Failure A program attempts to use memory which does not exist because of an allocation error. Anything may happen.  Program may crash or behave erratically. Try larger stack.  Single step in debugger.  Some compilers Ensure all ALLOCATEs are guarded.  Unfortunately, allocation of AUTOMATIC arrays cannot be guarded.  
Dangling Pointers Memory used after it has been de-allocated. As above. One or two compilers can check for these errors.
Non-conformant arrays An array is the wrong size or shape for an operation (e.g.   A = B where A and B are differently sized arrays). As above. One or two compilers can check for these errors.
Undiagnosed stack overflow. A program uses more stack memory than is available. As above. Ensure stack checking is active.  Try a larger stack.
Compiler Errors The compiler crashes or generates incorrect code. Errors may occur at any stage during the compile, link or run processes. Eliminate other possible errors.  Try the code with a different compiler.  Submit small sample to compiler vendor.

Notes

(1)    The plusFORT toolkit includes a tool which instruments source code so that uninitialised data (including global data) can be spotted at run time using any compiler and platform. 

(2)    The search rule used by Windows for DLLs is as follows: 

1 The directory from which the application loaded.
2 The current directory.
3 The Windows system directory (the directory containing such system files as GDI32.DLL). For Windows NT, the 32-bit Windows system directory.
4 Windows NT only: The 16-bit Windows system directory.
5 The Windows directory
6 The directories listed in the PATH environment variable.
7 The list of directories mapped in a network.