CommonCodingErrors

Common TINA Coding Mistakes

This page lists some common mistakes made when coding in TINA, which lead to compiler warnings. I am conducting a ruthless campaign to ensure that TINA compiles with no warnings when the -Wall flag is used, and about 99% of the warnings can be traced to the following problems, so please try to avoid them.

The Function-type Memory Allocation Macros

TINA contains a set of function-type preprocessor macros that allocate, copy and free simple memory structures, testing for errors like multiple frees. They include:

darray_free dupper_free dlower_free dvector_free

farray_free fupper_free flower_free fvector_free

iarray_free iupper_free ilower_free ivector_free

zarray_free zupper_free zlower_free

parray_free pupper_free plower_free pvector_free

sarray_free supper_free slower_free svector_free

carray_free cupper_free clower_free cvector_free

TINA programmers have developed a habit of casting the memory structures passed to these macros, like this:

darray_free((char **)array...

The preprocessor resolves this into

narray_free((char **)(char **)array...

Now, recent versions of gcc (3.1 and above) give a warning in this situation stating that the use of cast expressions as lvalues is deprecated. The lvalue here is the first cast (an lvalue is simply the left side of certain expressions: a thing that does not have a defined address). The warning is a bit over-sensitive in this case, but you should avoid it anyway as it may be upgraded to an error at some point in the future. I have tested all of the above functions using valgrind, and none of them need a cast on their first argument.

Common Memory Leaks

There are a few functions in TINA that don't work in the way you would imagine, and can lead to huge memory leaks. One common example is im_alloc, which takes an Imregion as one of its arguments. This is not the Imregion that will end up in the Imrect structure: if it is present, it will be copied; if not, a new one will be generated using the width and height arguments. Therefore, if you want to allocate an Imrect with the same Imregion as an image you are processing (e.g. to hold the results) just pass the pointer to that Imregion: do not copy it. The amount of memory that you can leak in this way when you are processing sequences is huge. In general, if you allocate any memory, make sure that you either free it or call a function that frees it.

Variables that May be Used Uninitialised

The following is a simple function to find the element in vector that is larger than threshold, and return the position (a bit contrived I know, but it illustrates the point):

int foo(double *vector, int length, double threshold)

{

int i, pos;

for(i=0; i< length; i++)

{

if(vector[i]>threshold) pos=i;

}

return pos;

}

Notice that pos is not initialised when it is declared, and is initialised within an if statement. Now, you may know that vector contains an element larger than the threshold, and so the function will work. However, the compiler does not know this, and will give a warning stating that pos may be used uninitialised (in the return statement). It is in general dangerous to do this, since the response of the code to an unexpected error will be undefined. Simply by writing

int foo(double *vector, int length, double threshold)

{

int i, pos=-1;

for(i=0; i< length; i++)

{

if(vector[i]>threshold) pos=i;

}

return pos;

}

and testing pos on return, the code can be protected against unexpected errors.

Missing Header Files/Inlined Prototypes

TINA 5 introduced a well-defined structure for the header files, which consists of three levels. For example, sysGen_format.c contains code for the format command. The name describes the location of the file: it is in the sys library/directory and the Gen group of files. Every directory, group and file has its own header file for function prototypes, in this case called sysPro.h, sys_GenPro.h and sysGen_format.h respectively. If you are coding in the same group (e.g. the file sysGen_mycode.c) and need to use a function in sysGen_format.c, you include the lowest level header file (sysGen_format.h). If you are coding in the same directory but a different group, you include the group header sys_GenPro.h, and if you are coding in a different directory, you use the directory header file sysPro.h. The same is true for the definintion header files (in this case sys_GenDef.h and sysDef.h) except that there are no file-level definition headers: all of the definitions are held in the group-level header. Adhering to these rules makes the libraries more portable, since it is easier to strip out specific parts of the code. Therefore, when you use a function you should prototype it in this way. On no account put inlined prototypes into the code simply because you known the function name and arguments but can't remember where it is: you can use the Lxr pages on the website to find out which header you need in a matter of moments.

Unused Variables/Functions

In the process of algorithmic development, functions will be optimised and may no longer need all of the variables that the first draft used. If this is the case, delete the initialisations. The same is true of functions: if you wrote a static function for testing, but no longer use it, then either delete it or comment it out and include a comment stating what it does and why you might want to use it in future.

Bad Function Return Types

There are only a few of these, but they are the most difficult to fix. Several function in TINA are declared to have a non-void return type, but in fact are designed only to work in their arguments and return nothing. I suspect that this has cropped up because the function was changed at some point during the algorithmic development process. If you write a function to return a value, but then realise that it doesn't need to, change it to a void function before you commit it to the libraries.

Beware Of Float To Int Conversions

Care must be taken when using the tina_int function (or doing any other float to int conversion), in that if you contain another arithmetic expression in the function, it might not give the number you expect. For example, say t0 = 17.1, tscale=1.9. The expression

tina_int(t0/tscale);

yields 8, not 9 as it should, because the number resolved within the brackets is 8.9999999.. ad infinitum so is rounded down to 8.

However,

b = t0/tscale;

tina_int(b);

does give 9 as the answer. This sort of problem is not a bug in TINA: it is found in many programming languages, and is a result of rounding errors due to the internal binary representation of the numbers involved.