Here are some safe C guidelines that I have found useful over the years. I also use a specific form of organizing header files that I have detailed here.
Make your code obvious
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." --Brian Kernighan
Enough said. This is probably the most important safe C guideline.
I like writing code so that it reads like English (or any other written language). This means writing long and descriptive variable and function names, making sure that the spacing between code blocks is such that they look like paragraphs. Each paragraph should capture a key concept. Topic for another article?
Use the form (constant == variable) in comparison expressions
C allows assignment to be done in comparison expression so
if(x = 5)
is a legal expression in C. Now, at least for me, I often mistype a single "=" instead of "==" while writing code. This used to be a source of errors in my code till I discovered the following technique (in "Deep C Secrets" book ).
Always write comparisons in the form
if(5 == x)
instead of
if(x == 5)
now if I make a mistake and write
if(5 = x)
the compiler declares an error and stops.
Modern C compilers may issue warning messages if they see a statement like
if(x =5)
but I picked up my habit before compilers became modern :-)
Use #ifdefs instead of commenting out source code
Sometimes you may not want to compile certain portions of source code but still leave it in the source file. It is much better to use #ifdefs to achieve this purpose than to simply comment out the code. The reason is that as you are playing with code, commenting and uncommenting code you may introduce errors.
Do not write integers with leading zeroes, do not use octal notation
C treats integers starting with a leading zero as an octal number. So if you want to write 32
int x = 32; //is 32
int x = 032 ; //is 3 *8 +2 = 26
This is yet another safe C guideline that saves hours of looking at code and asking ourselves what is going on here.
Allocate memory statically
If you can allocate memory statically, and in many embedded systems you can, you have avoided a common source of errors - memory management. I always try to allocate memory statically where I can, and I have been able to sleep at night, while my code has kept functioning for years and years without crashing.
Do not use recursion
Stack space in most embedded systems is limited. Recursion takes up more stack space, also making sure recursion depth stays within acceptable limits requires extra attention. Why have one more thing that can go wrong? Avoid recursion.
Use an ANSI C compiler to produce source code, although you may use C++ to do static checks
C++ is strictly speaking NOT a superset of C.
char Usage
For numbers use typedefs that explicitly specify the size of the number
stdint.h which is available in C99 standard defines these types for integers. If your C compiler is not C99 compliant, you can easily create these entries yourself
For example
Specifier Equivalent Sign Bits Bytes
int8_t signed char Signed 8 1
uint8_t unsigned char Unsigned 8 1
int16_t short Signed 16 2
uint16_t unsigned short Unsigned 16 2
int32_t int Signed 32 4
uint32_t unsigned int Unsigned 32 4
int64_t long long Signed 64 8
uint64_t unsigned long long Unsigned 64 8
I have found similar definitions very useful for float and double types also.
typedef float float32_t;
typedef double float64_t;
typedef long double float128_t;
If you are unsure about your compile you can always use the sizeof() operator to find the storage for example
int i;
i = sizeof(long double);
This is a safe C guideline that reduces the thought required while writing code.
Turn on maximum warnings in the compiler
Let compiler do the work, turn on maximum warnings and error messages. Make sure your code does not produce any warnings. For gcc you can do this using the C flag -Wall .
Turn warnings into errors
For gcc you can use# -Werror : Make all warnings into errors.CFLAGS += -Werror
I find this an indispensable safe C guideline. It prevents me from being lazy and ignoring warnings.
Initialize static variables
Always initialize variables to 0. Some embedded C run times may skip this step.
Use parenthesis in complex expressions
If you have a complex expression use parenthesis instead of just depending on C operator precedence rules. This makes the code much easier to read and there is no run time penalty.
Another safe C guideline that I have been happy with. \
Use bit operators only on unsigned types
Do not compare floating point valuesFloating point numbers are imprecise due to the way they are stored. Small errors resulting from this storage may cause your code to go awry
For example
float32_t x = 3.0;
float32_t y = 12.0;
float32_t result = y/x;
if(4.0 == result){
}
result may never equal 4.0 exactly.
If you need to use floating point comparisons use the library supplied for this purpose with your environment or write a small library that takes into account floating point number representation inaccuracies.
gcc has a warnings flag that will produce a warning if you use floating point number to test for equality
#-Wfloat-equal
#Warn if floating point values are used in equality comparisons.
this is automatically turned on by -Wall directive
Avoid using bitfields
While bitfields pack maximum information into a short space, they are compiler dependent and not portable So if you have the luxury of a small amount of extra memory, do not use bit fields.
A safe C guideline that I have been happy with. I will pack data into bitfields on itty, bitty processors but that is a last resort.
Use braces with if and else
C allows a single statement to be there after if and else without braces. In practice I have found that I make more errors when I use that facility. Now I always put braces after if and else statements.
Do you need more help to solve your problem? Would you like to ask the author a question about your specific problem? Do you have a great idea about this?
We will post an answer within 2 business days. If you need more immediate assistance or you would like to discuss your issue privately, please use our contact us form or call us at 1-888-215-8557. We love solving technical issues and there is no charge if we solve your problem over email or over a short phone call.