Coding Tricks
  internet advertising

Coding tricks are the way to code an application to minimize the error. This is also known as a defensive programming. Some tricks are programming language specific (compiler may or may not suuport).

The meaning of the term "Defensive" may varies from people to people. For some people it means to security or for few it may related to correctness of code... and may be for other it may a thought of error free coding.

Depending on the context, can mean several things.

  • The act of putting in redundant error-checks (assertions and the like) in a program, to make sure that errors are caught early and caught as close to where they actually occur, as possible. Some methods/methodologists frown on this (BetrandMeyer? and DesignByContract in particular; one rule Meyer gives is never re-check a precondition in the function body). Others view this as a legitimate form of DefenseInDepth--useful especially if a process such as DesignByContract isn't followed rigorously.
  • Avoidance of programming constructs known to cause trouble. This can be good or bad--often times the "advice" is based on rumor, folklore, or plain inexperience (and may not be relevant to your environment anyway). Sometimes useful--one way to successfully program in C++ is to adopt a style of coding which avoids HeapAllocation? as much as possible (that way, you don't have to worry about deallocating the memory; a common bugbear for C++ programmers).... more

First of all remember few things before start coding:

  • Dont make any assumptions about the compiler behavior

There are many version of compilers exist in the market. Even if all are bound to some standards, it will better to go through the compiler manual first. There are coding statements about which we can predict the behaviour and its totally depending on the compiler.

For example consider the following code snippet:

array[ i ] = i++;

What could be result of above statement ? Will the assignment statement execute first or 'i' will be incremented by one and then assignment happens ?

The behaviour of the above expression is undefined and depends on the compiler. So there are two options and compiler will choose any one of them. In such cases, it will be error prone to make assumption about the code execution. The Standard does not require that a compiler make an obvious choice, and some compilers don't. In this case, not only do we not know whether a[i] or a[i+1] is written to, it is possible that a completely unrelated cell of the array (or any random part of memory) is written to, and it is also not possible to predict what final value i will receive.

So best way is "Avoid the writting of such kind of code whose behaviour is not predictable". Still you wan to take risk then make sure what compiler will think rather than using your own intelligancy. This kind of code will not be portable.

  • Be familiar with your compiler

Becoming familiar with compiler settings will help in reducing the errors as well as amount of time invested in development. Use the settings that is most suitable for your application. Specifically while you are working on embedded system then depending the IDE (and so integrated compiler) you choose, u may have some additional features along with the IDE. There will be some default settings for compiler, but it may not provide you the best option for your application. Also there may be some code optimization option in compliler by which you can optimize your code in different different ways.

Now come to the point. Here, i will describe the mistake proofing techniques by which one can avoid bugs while coding an application.

1.'if' Statement for constant comparison

This is a matter of coding style while using 'if' statement to compare certian variable value with some constant. Follwoing are then ways you can write this comparision statement:

if (input_temp == SET_TEMPERATURE_VAL) {

// ............. do some calculations ........

}

another way is

if (SET_TEMPERATURE_VAL == input_temp) {

// ............. do some calculations ........

}

So, which one is better ? It seems that first style is good one. Second style is looking something weirg. But i will preffer to use second option because its an mistake proof statlement.

Why ? How ?

Suppose if one is using the first style and by mistake statement is written as:

if (input_temp = SET_TEMPERATURE_VAL) {

// ............. do some calculations ........

}

Now you can imagine the disaster of above code statement. IF condition will always be evaluated as TRUE. Irrespective of input_temp value, the conditional statement becomes an assignment statement and always evaluated as TRUE. So finally code may crash and developer may invest his/her time in finding out such buggy portion of the code.

[Note: if SE_TEMPERATURE_VAL is defined as 0x00, then condition will evaluated as zero - Øystein Pettersen]

In case of second style, even if you forget to use ==, compiler will give an error as constant can not be assiged to another value.

if (SET_TEMPERATURE_VAL = input_temp) {

// ............. do some calculations ........

}

2. Operator Precedence

If an expression consists of more than one operation then either devide the entire operation statement in small operations or use ( ) to make sure that the operations are performed correctly.

Consider the following simple code snippet:

temp = old_temp + cur_temp * TEMP_COEF ;

Assume TEMP_COEF = 4, old_temp = 2 and cur_temp = 10, then what should be the value of 'temp' ?

The above expression will be evaluated as temp = 2 + 10 * 4. This would result in the temp = 42.

Opssss ..... Does it go wrong ? Predicated result should temp = 48. Then ???

This is due to the operator precedence. Multiplication operation will perform first than addition as ' * ' operator has higher precedence than ' + '. So be careful while using multiple operators in a single code expression. If you are aware of operator precedence and desire result shoud be evaluated as 42 then above expression is ok, but still it sometimes make confusion for new developer. So either one should provide the comment for perticular formula or enclosed the small operations. Based on the operation required, above code snippet can be re-written as:

temp = old_temp + (cur_temp * TEMP_COEF) ; // To avoid the confusion

temp = (old_temp + cur_temp) * TEMP_COEF ; // To avoid the mistake in calculation

// For Simplicity

temp = old_temp + cur_temp ;

temp *= TEMP_COEF ;

It may also happen in conditional statement used multiple operations for checking conditions as:

if ( time = read_rtc() != set_time) {

// ......... do some operations ..............

}

3. Assertion

C language provides two very much helpful standard predefined macros for error message generation called __FILE__ and __LINE__. As these are standard macros, they are available with all compilers that implement those standards. __FILE__ and __LINE__ were both standardized by ANSI C; they may not be supported by (really) old compilers.

__FILE__ : This macro expands to the name of the current input file, in the form of a C string constant. This is the path by which the preprocessor opened the file, not the short name specified in #include or as the input file name argument. For example, "/usr/local/include/myheader.h" is a possible expansion of this macro.

__LINE__ : This macro expands to the current input line number, in the form of a decimal integer constant. While we call it a predefined macro, it's a pretty strange macro, since its “definition” changes with each new line of source code.

These macros are very much helpful in displaying the error message whenever unpredictable event happens. Especially while writting the code for embedded system where it will be difficult to debug an application run-time, then these macro can provide the great amount of help in debugging. In embedded system where you may have small LCD display or some display device where you can print the error message with the help of display interface routines.

Suppose for a temperature monitoring routine, you are expecting that the input temperature value should not be greater than MAX_TEMP, otherwise it indicates inconsistancy in the system. One of the possible implementation of such kind of function is shown as below:

void monitor_temperature (unsinged char input_temp) {

if (input_temp > MAX_TEMP) {

display_error ("Temperature Error", __LINE__, __FILE__);

// ............. Take corrective action .............

}

display_error( ) routine may display the error message, name of the source file where above routine is written and line number of the display_error routine in the source file. So one can goto this location and debug the code.



4. Checking the return code of Standard library functions

It is quite common to ignore the error return codes of standard library functions. It will be dangerous to ignore the return code by assuming that these functions will always return true. If one forgets to provide check for return code then it may crash your application.

Consider the example of malloc which is a part of standard C library function. malloc( ) is used to dynamically allocate the memory at run-time. The prototype of malloc is as below:

void *malloc(size_t size);

malloc( ) returns either a pointer to an uninitialised newly-allocated space for an object of size size, or NULL if it fails to allocate the requested amount of memory. Ignoring the return code of malloc( ) may crash the system.

Consider the follwoing code snippet which tries to allocate space for 100 elements of an array of int type:

int *stk_ptr = malloc(100 * sizeof (int));

if (NULL == stk_ptr) {

// .......Memory could not be allocated ..........

// ...........Take corrective action ..............
}

Suppose malloc is not able to allocate the requested amount of memory, then it will return a NULL pointer. If programmer doen't provide check for return type of malloc which is NULL and tries to use NULL pointer assuming that it points to allocated memory, then program may crash.

So it is a good practice to check the return type of standard library function for failure and take corrective action to handle the situation.

5. Going beyond the array limit

There are some conditions where one initializes an array elements or copy a series of data from one location to an array. Consider the following code snippet:

unsinged char input_buffer[10];
unsigned char i;

for (i = 0; i <=10; i++) {
input_buffer[i] = in_port(); // Error: Read 11 bytes of data instead of 10
}

Above code is expected to read 10 data byte from input port and assign it to input_buffer from 0 to 9 location of it. But due to error in comparision statement in 'for' loop, code will try to assign 11th byte at input_buffer[10] location which may be used by another variable. So this type of mistake will lead to crash the system and it may take considerable amount of time to debug the error.

There are some tools exist which will help in reducing this kind of mistake. PC-Lint is one the tool which can help in avoiding such errors.

Valgrind is another tool that can help in detecting common memory management errors. Valgrind is closely tied to the architecture of the operating system. Currently, it's only supported on Linux x86 machines with kernels from the 2.2.x and 2.4.x series and glibc 2.1.x or 2.2.x.

6. Bool type

Generally the bool data type can take one of the two possible values. It may be either TRUE or FALSE, SET or CLEAR, HIGH or LOW, ENABLE or DISABLE, ON or OFF, etc... While defining a macro for these values make sure that one macro is simple value defintion while other is inverse of first one rather then defining it directly.

Consider the following example:

#define DISABLE 0
#define ENABLE (!DISABLE)

This will help when one will change the DISBALE value to 1, ENABLE will automatically change to 0. But if above definition is written as:

#define DISABLE 0
#define ENABLE 1

If one will change the DISBALE value to 1 and forgets to change the value of ENABLE, imagine the disaster it would create.

7. Always use braces with if, for and while

Some programmers do not use the braces with if statement or/and with 'for' or 'while' loop when they conatins a single statement as below:

if (SET == timer_isr_flag)
timer_isr_flag = CLEAR;

or

for (i = 0; i < BUFF_SIZE; i++)
buff[i] =i;

or

while (timer_isr_flag)
wait(100);

There is nothing wrong in the above sample code statements. It will execute properly. But when you want to add one or few more statments and forget to use brace to enclose the set of statements under if, for or while then what happens ? Coe will not work as per expectation. Only first statment will the part of if, for or while. Other statments will always be executed irrespective of the conditions. So as a general practice always use braces with 'if', 'for' and 'while'.

if (SET == timer_isr_flag) {
timer_isr_flag = CLEAR;
}

or

for (i = 0; i < BUFF_SIZE; i++) {
buff[i] =i;
}

or

while (timer_isr_flag) {
wait (100);
}

8. Sharing of variable

There may the cases when you want to access some of the variables from Interrupt Service Routines (ISR) i.e. foreground routines, as well as from normal i.e. background routines. These kind of possibilities are more common while writting firmware for embedded system where back ground routine has to execute some task based on some event or interrupt. In such case, programers share a variable between foreground and background. When event or interrupt happens, variable will be set. Variable will be checked periodically in back ground and if variable is set then perform the related task.

In such design, if variable is declared as a normal unsigned char type then there may be the chances that when background routine read the variable, it may not get the actual value. To understand this, consider the below example:

unsigned char timer_flag = CLEAR;

void timer_isr (void ) {
timer_flag = SET;
// .......... Do other activity ........
}

void bg_timer_check (void) {
if (timer_flag) {
// ........ Perform necessary job .........
}
}

Suppose when bg_timer_check( ) check for the timer_flag (which is CLEAR), timer_flag value goes to CPU register and timer event happens, so CPU will push the system registers, prgram counter and necessary stuff on STACK, suspend the execution of bg_timer_check( ) and start the execution if ISR for timer event i.e. timer_isr( ). In timer_isr routine, timer_flag will be SET. When CPU resumes the execution of interrupoted routine i.e. bg_timer_check( ), it will first POP the program counter, system registers and other stuff pushed previously. This time even if timer_isr is SET, CPU will not get this reflected value and it will uses the value stored before interruption which is CLEAR in this example.

This kind of scenarios can be avoided by defining the timer_flag as a 'volatile' varible. When variable is defined as voltaile, then it tells the compiler not to do any optimization for that variable. It makes sure that CPU will always get fresh value of variable. Generally the variable used for hardware ports, ADC data ect ... must be declared as a volatile. Also the variables shared between processes or threholds muct be declared as a volatile. Variables declared as a volatile will not be optimized by compiler as their value can be changed at any time even if code doesn't contain any direct statement that changes the variable value. Consider the following example where

UART_SDR is used to read serial uart data byte from 0x1010 address :

unsinged int *UART_SDR = (unsinged int *) 0x1010;

*UART_SDR = 0;

void check_serial_data (void) {
while (STP != *UART_SDR) {
// ..... Wait for Start of Packet byte.......
}
}

Compiler will see that UART_SDR is never modify in the code, so it will optimize the above code as:

void check_serial_data (void) {
while (1) {
// ..... Wait for Start of Packet byte.......
}
}

Declaration for UART_SDR as below, the problem of optimization can be avoided in above case:

volatile unsinged int *UART_SDR = (volatile unsinged int *) 0x1010;

Home