You are on page 1of 36

Software Development

Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 1 of 36

Software Development Standard

File name:

Software Development Standard 1.0 Draft A.doc

Author:
Reviewed by:
Approved by:

Stefano Braidi

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 2 of 36

Table of Contents
1.

Document Evolution................................................................................................................................. 3
1.1
Document History Log ......................................................................................................................... 3
1.2
Document Change Log ....................................................................................................................... 3
2. Introduction ............................................................................................................................................... 4
2.1
Purpose ............................................................................................................................................... 4
2.2
Scope .................................................................................................................................................. 4
2.3
Reference Documents ........................................................................................................................ 4
2.4
Acronyms ............................................................................................................................................ 4
2.5
Glossary .............................................................................................................................................. 4
3. Software Coding Overview ................................................................................................................... 6
4. Software Projects ..................................................................................................................................... 7
4.1
Introduction to Software Projects ........................................................................................................ 7
4.2
Files Naming Convention .................................................................................................................... 7
4.3
Default Library ..................................................................................................................................... 8
5. Program Files............................................................................................................................................ 9
5.1
C Source Files ..................................................................................................................................... 9
5.2
Include Header Files ........................................................................................................................... 9
5.3
Program File Prologue ...................................................................................................................... 11
5.4
Program File Versioning .................................................................................................................... 12
6. Style Guidelines...................................................................................................................................... 14
6.1
Comments ......................................................................................................................................... 14
6.1.1
Data Structures Comments........................................................................................................ 15
6.1.2
Global Data Comments.............................................................................................................. 16
6.1.3
Function Prologue ...................................................................................................................... 16
6.2
Whitespaces, Indentation and Layout ............................................................................................... 17
6.3
Naming Conventions ......................................................................................................................... 18
7. Programming Guidelines ....................................................................................................................... 21
7.1
Declarations ...................................................................................................................................... 21
7.1.1
Declarations Scope .................................................................................................................... 23
7.1.2
Declaration vs. Definition ........................................................................................................... 23
7.2
Functions ........................................................................................................................................... 24
7.3
Expressions and Statements ............................................................................................................ 25
7.3.1
Compound Statements .............................................................................................................. 26
7.3.2
Assignment ................................................................................................................................ 26
7.3.3
Control Flow ............................................................................................................................... 27
7.4
Operators .......................................................................................................................................... 29
7.5
Constants .......................................................................................................................................... 30
7.6
Macros ............................................................................................................................................... 31
8. Other Topics ........................................................................................................................................... 32
8.1
Conditional Compilation .................................................................................................................... 32
8.2
Debugging ......................................................................................................................................... 32
8.3
Special Considerations ..................................................................................................................... 33
8.3.1
Suggestions for Portability ......................................................................................................... 33

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard
1. Document Evolution
1.1

Document History Log

Version

Release date

Author

1.0 Draft A

19/10/2012

S. Braidi

1.2

First draft version for internal circulation.

Document Change Log

Reason
-

Description

Paragraph
-

Author:

Description
All the document is new.

Reviewed by:
Stefano Braidi

Approved by:

Version: 1.0 Draft A


Date: 19/10/2012
Page: 3 of 36

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 4 of 36

2. Introduction
2.1

Purpose

The purpose of this Development Standard document is to provide guidance to the Software Development
process.
In details, the purpose of this document is to ensure that all the software projects developed by AdelSystem
have a clear and consistent coding style.
Therefore, this document provides indications and recommendations so that Software Coding follows a
disciplined process.

2.2

Scope

The indications provided in these document are not strictly prescriptive and are not formulated as formal
requirements, but rather as informative material, examples, and notes.
This Coding Standard applies to all software projects that are developed in C language by AdelSystem.
So, unless explicitly noted, the contents of this document it is intended for software developed in C language.
Please take note that this Coding Standard is not a tutorial course on C language programming: C language
basic concepts and knowledge are prerequisites.

2.3

Reference Documents
Code

Document
B.W. Kernighan and D.M. Ritchie, The C Programming Language, Prentice Hall
1978, Second Ed. 1988, ISBN 0-13-110362-8.
MISRA-C:2004 Guidelines for the Use of the C Language in Critical Systems
ISO 9899 Programming Languages C

[R1]
[R2]
[R3]

2.4

Acronyms

Acronym

2.5

Definition

Glossary
Term

Definition

Software Library
All caps
Capitalized

Collection of functions and data that can be used to develop a software.


Short for all capitals. Text or font in which all letters are capital letters.
Text or font in the first letter only is capital letter.
Text in which the elements are joined without spaces and are capitalized within the
compound.

CamelCase

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 5 of 36

Term

Definition

1TBS

The One True Brace Style is a typical indenting style convention for structuring
source files.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 6 of 36

3. Software Coding Overview


As already mentioned, the purpose of this Development Standard is to ensure that all the software projects
developed by AdelSystem have a clear and consistent coding style.
This document will define the organization of a software project, some standards to document the source
code and the code headers, and also the language restrictions to be followed.
The use of this document is intended to provide the following benefits:

reduction in the number of software errors

encouragement of consistent layout

increasing clarity of the source code

ease of maintenance

increasing portability of code.

Many of the style choices described in this document are somewhat arbitrary.
Whatever standards might be adopted, be consistent with it.
However, a mixed coding style is harder to maintain than bad coding style: when changing existing code it is
better to conform to the style of the existing code (indentation, spacing, commenting, naming conventions)
than to blindly follow this document.
Of course, these standards cannot cover all situations.
Experience and informed judgment count for much.
Programmers who encounter unusual situations should consult either experienced programmers or code
written by experienced programmers (preferably following these rules).
The most important things to remember are here summarized.

Encourage the proper use of white space and comments, so that the structure of the software can be
evident from the layout of the code. Encourage also the use of simple expressions, statements, and
functions so that they may be easily understood by other developers.

Please keep in mind that you (or someone else) could be asked to modify the code or port it on a
different target machine sometime in the future. Craft the code so that it is portable to obscure
machines. Implement optimizations only when necessary. Localize and comment all optimizations,
since they are often confusing and may be "devasting on other target machines.

Many style choices are arbitrary. Having a style that is consistent (particularly with group standards)
is more important than following absolute style rules. Mixing styles is worse than using any single
bad style.

As with any standard, it must be followed to make it useful.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 7 of 36

4. Software Projects
4.1

Introduction to Software Projects

A generic (complex) software project is usually composed of different kind of files, whose organization and
naming rules can depend on the target platform or the development tools.
The files involved in a software project can be summarized in the following types:

Program Files

Project Files

Output Files

Program files are usually called source files because they are the core of the software project and they
originate directly the software product.
They are also referred to as modules, and will be described in detail below in Program Files chapter.
Project files are all the other files involved into the goal of obtaining the final software product, i.e.
compiler/linker options files, makefiles, debugger files, environment setup files,
Output files are the final products of a software project. They are also usually called binary files.
It is a good practice to keep source files physically separated form projects files and from software products.
It is also a good practice to add a specific container for the software documentation.
Even if it is not possible to define a strict rule to organize a software project, the following container should
always be created from the project root directory:
<prj root>\BIN\
to collect the output files
<prj root>\PROJECTS\
to organize the project files
<prj root>\SOURCES\
to organize the source files
<prj root>\DOCS\
to collect the software documentation files
Of course, each container can be also organized in subdirectories or can have different naming.

4.2

Files Naming Convention

File names are made up of a base name, and an (optional) extension, that describes the type of file itself.
The first character of the name should be a letter and all other characters (except the period) can be letters,
numbers, and special characters like underscore _ or minus -.
Some particular system file can begin with underscore character, in order to emphasize their nature.
The following extensions (in lower case) are commonly used to identify the program files:

C source file names must end in .c

Include header file names end in .h

while assembly source files have different platform-dependent extensions.


Project files and output files have different platform-dependent extensions too.
Software documentation files have no predefined extensions.
It can be useful to include directly in the name of a program file a suffix that may explain the purpose of the
file itself (i.e. comm_drv.c, comm_h.c, ).
A mixed case (CamelCase) file naming can be accepted, especially if meaningful to describe the file content
(i.e. LcdManager.c).

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard
4.3

Version: 1.0 Draft A


Date: 19/10/2012
Page: 8 of 36

Default Library

AdelSystem should develop its own default software library, with the purpose to standardize software
development and encourage software reuse.
At the moment, the use of the following definition files is strongly recommended in order to pursue a
consistent coding style in AdelSystem software projects:

defines.h for standard definitions

typedefs.h for basic types definitions.

These header files are included into a generic header file (together with specific project dependent files, like
compiler dependencies include or HW layout include), named std_types.h.
The std_types.h file shall then be included in all the software modules.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 9 of 36

5. Program Files
5.1

C Source Files

A C source file consists of various sections that should be separated by several blank lines.
Although there is no maximum length limit for source files, files with more than about 1000 lines are
cumbersome to deal with.
Lines longer than 140 columns should be avoided, if possible. Excessively long lines, which result from deep
indenting, are often a symptom of poorly-organized code.
The suggested order of sections for a C source file is as follows.

First there is a prologue (file header) that explains the content of the file. A description of the purpose
of the objects in the file (whether they be functions, external data declarations or definitions, or
something else) is more useful than a list of the object names. The prologue should contain
author(s), references, etc. (see below Program File Prologue paragraph).

Next, there should be all header files included. If the include is for a non-obvious reason, it should be
commented.
Standard library (system) header files (like stddef.h, math.h or string.h) should be included at the top
of the include list, immediately followed by project default header files (like the std_types.h described
above), and all other user header files.

Next are any defines and macro that apply to the file as a whole. A normal order is to have
"constant macros first, then "function macros.

Next are defined typedefs and enums whose scope cover the whole file.

Next, all (static) local function should be prototyped.

Next come the global data declarations, usually in the order: externs, non-static globals (exported),
static globals.
Alternative to this classical structure, it is strongly encouraged to move the definition of the non-static
globals directly into the header file corresponding to the same C source file, and use an automatic
mechanism of exporting/defining (see below Declaration vs. Definition paragraph).
If a set of defines applies to a particular piece of global data (such as a flags word), the defines
should be immediately after the data declaration.

The functions come last. They should be in some sort of meaningful order. Similar functions should
appear together. Even if it is not mandatory to part local functions from exported ones, this practice
is strongly recommended. Also, another kind of approach is to group functions belonging to the
same functionality area.

It is useful to define a private symbol to identify every source module in an unique way.
This can be easily done defining a symbol linked to the file name itself.
#define _FILENAME_MODULE
This symbol can also be used in the related header file to distinguish between what it is specific/local for this
very module and what can be included by other C source files.

5.2

Include Header Files

Header files are files that are included in other files prior to compilation by the C preprocessor.
They are used to contain data declarations and defines that are needed by more than one C source file.
Similarly to what defined above for C source files, even header files consist of various sections separated
by blank lines. The suggested order of sections for a header file is as follows:
file prologue
Author:
Stefano Braidi

Reviewed by:

Approved by:

Software Development
Standard

all header files included

defines and macro that can be exported

typedefs and enums that can be exported

all exported global variables declarations

all prototypes for exported functions.

Version: 1.0 Draft A


Date: 19/10/2012
Page: 10 of 36

Header files should be functionally organized, i.e., declarations for separate subsystems should be in
separate header files.
Also, if a set of declarations is likely to change when code is ported from a machine to another, those
declarations should be in a separate header file.
Some header files, like stddef.h, math.h or string.h, are defined at the system-level and must be included
by any module that uses these specific library functions.
Please remember that MISRA Guidelines have some restriction on Standard Libraries usage (i.e. stdio.h):
pay attention to it if you want to be compliant with MISRA Guidelines.
Please distinguish between including standard library and private project header files: use angular brackets
1
for standard library and double quotes for private headers, as in the following example .
#include <math.h>
#include "common.h"
#include "comm_h.h"

/* math library header file */


/* default project include */
/* comm. handler definitions */

<- standard library


<- private header
<- private header

Avoid private header filenames that are the same as library header filenames.
The statement
#include "math.h"
will include the standard library math header file if an header file with the same name is not found in the
current directory: if this is what you want to happen, comment this fact.
In extreme cases, where a (same) large number of header files are to be included in several different source
files, it is suggested to put all common #includes in one header file and just include this later one (like the
std_types.h described above).
Do not use absolute pathnames for header files.
As requested by MISRA Guidelines, use instead the <name> construction for getting them from a standard
place, or define them relative to the current directory. The "include-path option of the C compilers is the best
way to handle extensive private libraries of header files; this allows to reorganize the directory structure
without having to alter source files.
Header files should not be nested.
The prologue for an include header file should, therefore, describe what other headers are necessary.

Please also note that some static code analyzing tools use this very kind of syntax in order to exclude library header
files and suppress their related missing symbol warnings.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 11 of 36

To avoid undesired nesting of header files, it is a good practice to define a private symbol for each header
file and to add an automatic checking of this symbol inside the header file itself, as showed in the following
example:
#ifndef _FILENAME_INCLUDED
#define _FILENAME_INCLUDED
header file body here
#endif /* _FILENAME_INCLUDED */
As mentioned before, it is encouraged to move the definition of exported variables and functions prototypes
from C source file to the relative header file, in order to have an automatic mechanism of defining/exporting.
This is described below in the Declaration vs. Definition paragraph.
Defining variables in an header file is often a poor idea. Frequently it is a symptom of poor partitioning of
code between files. Also, some objects like typedefs and initialized data definitions cannot be seen twice by
the compiler in the same compilation. On some systems, repeating uninitialized declarations without the
extern keyword also causes problems. Repeated declarations can happen if header files are nested and will
cause the compilation to fail.

5.3

Program File Prologue

The following information shall be provided as commentary in the prologue of both source (.c) files and
header (.h) files:

module identity and issue

description

target platform references

copyright statement

authors references

module history (see below Program File Versioning paragraph)

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 12 of 36

Below there is an example of file prologue.


/*
*********************
******************************* C SOURCE FILE ********************************
**
*********************
**
**
**
** Filename
: COMM_H.C
**
** Description : communication interface handler module
**
** Project
: IR Sensor
**
**
**
** Version
: 1.0.0
**
** Date
: October 2012
**
**
**
** Target
: Freescale HC(S)08
**
** Compiler
: F2MCCodeWarrior Development Studio for Microcontrollers
**
**
**
******************************************************************************
**
**
** Copyright (c) 2012, AdelSystem - All rights reserved.
**
**
**
******************************************************************************
**
**
** Author(s)
: John Smith [JoSm]
**
**
**
******************************************************************************
**
**
** VERSION HISTORY
**
**
**
******************************************************************************
Date
Ver
Author Description
-------- ----- ------ ----------------------------------------------------01/10/12
*/

5.4

1.0.0

JoSm

First release

Program File Versioning

Software modules should have a consistent version identification criteria.


The criteria adopted is the same versioning method introduced by AUTOSAR organization, which is
summarized in the following rules.

The version number is provided in a clear way in the header file.

The version tag is composed by 3 numbers and it is formatted as M.m.p, with


o

M = major version

m = minor version

p = patch version.

Each number is represented as 8 bit unsigned integer.

The enumeration of version numbers shall follow this criteria:


o

the patch version is incremented if the module is still upwards and downwards compatible,
i.e. for bug fixing;

the minor version is incremented if the module is still downwards compatible, i.e. when new
feature/interface is added without changing the existing interfaces;

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard
o

Version: 1.0 Draft A


Date: 19/10/2012
Page: 13 of 36

the major version is incremented if the module is no more compatible, i.e. interfaces
changes or fundamental changes.

Increasing a digit in version number automatically resets all less significant digits (i.e. 1.9.2 -> 2.0.0).

As the version number is accessible in every module, it is easily possible to add version checking either in
compile time or in run time.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 14 of 36

6. Style Guidelines
The contents of this chapter will focus deeply on program files just described in the previous chapter.

6.1

Comments

The comments should describe:

what is happening;

how it is being done.

Comments can increase the readability of a software, but if used heavily or poorly placed they can render a
good code completely incomprehensible.
Avoid comments for situations that are clear from the code, as such information rapidly gets out of date.
Comments that disagree with the code are of negative value: an inaccurate or misleading comment leads to
more damages than benefits of a good comment.
Short comments should be what comments, such as "compute mean value, rather than how comments
such as "sum of values divided by n.
Comments should justify offensive code.
The justification should be that something bad will happen if inoffensive code is used.
Just making code run faster (more performing in terms of execution time) is not enough to justify a hack; its
necessary to show that performance is unacceptable without the hack. The comment should explain the
unacceptable behavior and describe why the hack is a "good fix.
Comments that describe data structures, algorithms, etc., should be in block comment, as in the following
examples.
Remember to be consistent with whatever style you might choose.
/*
*
*
*
*
*/

Here is a block comment.


The comment text should be indented uniformly.
The opening slash- asterisk and closing asterisk -slash sequences
are alone on a line and on lines by themselves.

/*****************************************************************************
*
* Here is an alternative block comment.
* The comment text should be indented uniformly.
*
****************************************************************************/
/*---------------------------------------------------------------------------Another alternative format for block comments
*--------------------------------------------------------------------------*/
/*
One more alternative format for block comments
*/

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 15 of 36

One-line comments alone on a line should be indented, preferably using spaces, to match the code that
follows.
if (argc > 1) {
/* Get input file from command line. */
if (freopen(argv[1], "r", stdin) == NULL) {
perror(argv[1]);
}
}
Very short comments may appear on the same line as the code they describe, and should be spaced to
separate them from the statements.
If more than one short comment appears in a block of code they should all be aligned to the same column.
if (a == EXCEPTION) {
b = TRUE;
}
else {
b = isprime(a);
}

/* special case */
/* works only for odd a */

How to comment the prologue of a program file has been already described in the Program File Prologue
paragraph. In the following paragraphs more important commenting topics will be defined.

6.1.1 Data Structures Comments


Data structure declarations should be accompanied by a descriptive comment since even the simplest
structure can be misinterpreted by other users.
This kind of comment should describe both the structure as a whole and its contents similarly to the following
example:
/*---------------------------------------------------------------------------* new_command
* New command ready to be sent.
* The transmission options are set in the required_opts field.
*
* sender_id:
identify the sender of the command
* required_opts: transmission options
* buffer_size:
max number of char in the transmission buffer
*--------------------------------------------------------------------------*/
typedef struct
{
unsigned char sender_id;
t_Options required_opts;
unsigned char buffer_size;
} new_command;

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 16 of 36

Alternatively, in order to have a more readable code, it is possible to comment the content of a data structure
directly in the definition, as showed in the following example:
/* New command ready to be sent
typedef struct
{
unsigned char sender_id;
t_Options required_opts;
unsigned char buffer_size;
} new_command;

*/
/* idenfificate the sender of the command */
/* transmission options */
/* max number of char in transm. buffer */

6.1.2 Global Data Comments


The purpose of each global data should be described in a comment similar to the following example:
/* Reception buffer */
#define RX_BUF_SIZE
48 /* Receive buffer size */
unsigned char RxBuffer[RX_BUF_SIZE];
Use a more verbose way of commenting if some other important information have to be remarked.
/*---------------------------------------------------------------------------in_transfer_name
A unique name which will be used to transfer data from the host.
The length of the name must be kept fairly short to avoid name length
limitations in the transfer system used.
*--------------------------------------------------------------------------*/
#define IN_MAX_TRANSFER_NAME 16
static char in_transfer_name[IN_MAX_TRANSFER_NAME];

6.1.3 Function Prologue


Each function definition in a C source file (.c) shall be preceded by a description prologue.
Since users of the module may only have visibility of the related header (.h) file, the header file shall repeat
the description prologue for each prototype of exported function.
The function prologue shall contain the following information:

function name

brief function description

function interface

what parameters mean

external variables (used or modified), if used

any restrictions.

As a memo, after the closing brace }, each function shall repeat the function name.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 17 of 36

Below is an example function prologue comment.


/*---------------------------------------------------------------------------* SendDataToPeripheral()
*
Send a generic data to the output peripheral
*
* Input parameters -> data: data value to be sent
* Return value
-> 0 = everything ok ; <> 0 = error detected
* Ext variables
-> LastErr
*--------------------------------------------------------------------------*/
unsigned char SendDataToPeripheral (unsigned char data)
{
...
function body
...
} /** SendDataToPeripheral */

6.2

Whitespaces, Indentation and Layout

Use vertical and horizontal white spaces generously.


Indentation and spacing should reflect the block structure of the code: a visually well-structured code is
easier to understand.
The indentation of blocks of code should follow a consistent style, like the One True Brace Style (1TBS).
This style keeps the first opening brace on the same line as the control statement, indents the statements
within the braces, and puts the closing brace on the same indentation level as the control statement on a line
of its own.
Examples of this style are reported later in this paragraph and in Compound Statements paragraph.
Functions, however, are braced distinctly from statements as the opening brace is placed on the line
following the declaration, at the same indentation level as the declaration.
The default indentation size is fixed to 4 blank spaces.
Below, are summarized some other general rules.
All keywords (except for the sizeof operator) that are followed by expressions in parentheses should be
separated from the left parenthesis by a blank.
if (...)
while (...)
Assignment statements should have the operator preceded and followed by a blank space, as in the
following examples.
a = b;
c = c + 5;
Avoid multiple statements in a single line:
a = b; c += 5;
Please also make reference to Expressions and Statements paragraph below in this document.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 18 of 36

Array declaration should avoid blank spaces between the name and the opening square bracket.
int tmp[10];
Relational operators should be preceded and followed by a blank space:
if ((a >= 5) && (b == c)) {
...
}
Please also make reference to Operators paragraph below in this document.
There should be at least 2 blank lines between the end of a function and the comment prologue of the next
one.
Function name should be

followed by a blank space before the opening left parenthesis, in function definitions and prototypes/
declarations;

directly followed by the opening left parenthesis without any blank space, in function calling (it is
considered like an unary operator);

as showed in the following examples.


unsigned char SendDataToPeripheral (unsigned char data, unsigned char error)
{
...
} /** SendDataToPeripheral */
...
if (SendDataToPeripheral(MyData, MyError) != FALSE) {
...
}
Functions arguments should be separated by a blank spaces after commas.
Please also check the Function paragraph below in this document.
Macro definitions with arguments must not have a blank between the name and the left parenthesis,
otherwise some C preprocessors could not recognize the argument list.
#define GET_BYTE(buf, i)

(...)

Avoid too long lines: break a long line in meaningful place (better near to a binary operator).
A long string of conditional operators should be split onto separate lines, as showed in the following
examples.
if ((foo->next == NULL)
&& (totalcount < needed) && (needed <= MAX_ALLOT)
&& (server_active(current_input))) {
...
As final rule, please avoid useless and confusing blank spaces, as in the following declarations:
unsigned char myvar1 ;
unsigned char
myvar2;

6.3

Naming Conventions

Individual software projects will surely have their own naming conventions.
Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 19 of 36

There are however some general rules.

Names with leading and trailing underscores are reserved for system purposes and should not be
used for any user-created names. Most systems use them for names that the user should not to
know.

#define constants should be in ALL CAPS.

enum constants should be in ALL CAPS.

Function, typedef, and variable name, as well as struct, union, and enum tag names should be in
Capitalized style. union and typdef tag names often have _t appended to their name.

As each structure or union has a separate name space for its members, it is not necessary to add
them a distinguishing prefix/suffix. Members names can be either in lower-case either in Capitalized
style.

Macros should be in ALL CAPS. If a macro behaves like a function (it evaluates its parameters
exactly once and does not assign values to named parameters) you can follow the conventions for
functions and use Capitalized style. Some macros (such as getchar and putchar) are in lower-case
since they may also exist as functions. Lower-case macro names are only acceptable for functionlike macros in order to avoid ambiguity.

Remember that C language has the following namesapces:

label names

tags (structs, enums and unions)

member names (each struct or union has its own namespace)

everything else.

Names should be chosen to make sense when the software is read.


Thus, all names should be part of speech which will make sense when used with the language's syntactic
keywords:

variables should be noun clauses

boolean variables should be named for the meaning of their "true" value

function names should reflect what they return (should be named for what they do, not how they do
it)

boolean-valued functions of an object should be named for the property their true value implies
about the object.

Avoid "Hungarian notation" naming convention: its maintenance could be very difficult and confusing.
Avoid names that differ only in case, like foo and Foo.
Similarly, avoid foobar and foo_bar, as the potential for confusion is considerable.
Similarly, avoid names that look like each other, and keep in mind that on many terminals and printers, l', 1'
and I' look quite similar.
As general rule, avoid single-character names (except for very low scope counters like i, j, ).
Remember that C language allows only two scope levels: module level and public level.
Remember also that a local declaration might hide a global declaration with the same name (i.e. a library
defined function).
So, avoid names that might conflict with various standard library names, and assign to each library a short
prefix to be used in all global identifiers.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 20 of 36

Moreover, as standard data types (char, int, long, ) may vary from machine to machine, typedef explicitly
in each library all exported data types by beginning them with a letter or two identifying the package to which
they belong, i.e.
typedef unsigned char
typedef unsigned int

Author:

osU8;
osU16;

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 21 of 36

7. Programming Guidelines
7.1

Declarations

All identifiers shall have meaningful names, and may have any length.
In the following part of this paragraph there will be summarized some general rules that will make your code
easier to understand.
All declarations should begin in column 1, except for declaration of local variables inside a function.
All external data declaration should be preceded by the extern keyword.
All module-local declaration should be preceded by the static keyword.
Unrelated declarations, even of the same type, should be on separate lines. A comment describing the role
of the object being declared should be included, with the exception that a list of #defined constants do not
need comments if the constant names are self-explaining.
The names, values, and comments are usually indented so that they line up underneath each other.
Do not rely on C language implicit default int typing, but use explicit int declaration:
static int MyData;
instead of
static MyData;
If an external variable is an array that is defined with an explicit size, then the array bounds must be
expressed with a #defined macro and repeated in the extern declaration unless the size is encoded in the
array (i.e., a read-only character array that is always null-terminated).
Repeated size declarations are particularly beneficial to someone picking up code written by another.
#define ABC_ARRAY_SIZE;
extern unsigned char abc[ABC_ARRAY_SIZE];
The "pointer qualifier, *, should be with the variable name rather than with the type:
char

*s, *t, *u;

instead of
char*

s, t, u;

which is wrong, since t and u do not get declared as pointers.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 22 of 36

For structure and union template declarations, each element should be alone on a line with a comment
describing it, and the closing brace } should be in column 1.
struct boat {
int wllength;
int type;
long sailarea;
};

/* water line length in meters */


/* see below */
/* sail area in square mm */

/* defines for boat.type */


#define BOAT_KETCH (1)
#define BOAT_YAWL (2)
#define BOAT_SLOOP (3)
#define BOAT_SQRIG (4)
#define BOAT_MOTOR (5)
It is strongly discouraged to put these defines within the struct declaration, right after the declaration of
type, as in the following example.
struct boat {
int wllength;
int type;
#
define KETCH
#
define YAWL
#
define SLOOP
#
define SQRIG
#
define MOTOR
long sailarea;
};

/* water line length in meters */


/* see below */
(1)
(2)
(3)
(4)
(5)
/* sail area in square mm */

When the actual values are unimportant, the enum facility is better.
enum bt { KETCH=1, YAWL, SLOOP, SQRIG, MOTOR };
struct boat {
int wllength;
/* water line length in meters */
enum bt type;
/* what kind of boat */
long sailarea;
/* sail area in square mm */
};
Any variable whose initial value is important should be explicitly initialized, or at the very least should be
commented to indicate that C language's default initialization to zero is being relied upon.
The empty initializer "{ } should never be used.
Structure initializations should be fully parenthesized with braces. Constants used to initialize longs should
be explicitly long.
int
x = 1;
char
*msg = "message";
struct boat winner[] = {
{ 40, YAWL, 6000000L },
{ 28, MOTOR, 0L },
{ 0 },
};
The most important types should be highlighted by typedeffing them, even if they are only integers, as the
unique name makes the software easier to read (as long as there are only a few things typedeffed to
integers!).
In the same way, provide typedefs for most of enum constants and for struct and union types: this
eliminates the clutter of extra struct and union keywords every time a variable is declared.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 23 of 36

It is possible to typedef them directly when they are declared


typedef struct boat {
int wllength;
enum bt type;
long sailarea;
};

/* water line length in meters */


/* what kind of boat */
/* sail area in square mm */

or alternatively to create the typedefs before the type declarations


typedef struct boat boat;
typedef struct Foo Foo;
struct boat {
int wllength;
enum bt type;
long sailarea;
};

/* water line length in meters */


/* what kind of boat */
/* sail area in square mm */

struct Foo {
...
}

7.1.1 Declarations Scope


As previously already mentioned, C language allows only two scope levels: module level and public level.
A local identifier that overloads another identifier in an outer scope shall not be used: the potential for
confusion and error is too great.
Avoid exporting names outside of individual C source files: maximum usage should be made of the static
keyword in order to make functions and variables local to single modules.
Remember that by default in C language all declarations are global, so explicitly declare as static every
function and global variable that you possibly can.
Variables in particular should be accessible from other files only when there is a clear need that cannot be
filled in another way. Such usage should be commented.
So, in order to have a more readable code, it is encouraged to keep a variable declared as static and let
other modules access it with specific get and set functions.
If global variables are necessary, it is strongly encouraged to move their declarations from C source file to
the relative header file, and use an explicit extern.
The same thing applies to functions prototypes too.
An automatic mechanism for declaring external object is described below in the Declaration vs. Definition
paragraph.
Do not declare external objects inside functions.

7.1.2 Declaration vs. Definition


Keep in mind the main difference between declaring a variable and defining it.
A definition actually allocates and initializes storage for an object, while a declaration just informs the
compiler that an object exists.
You can declare an object any number of times, but there can only be one definition among all the source
files.
Header files should never contain object definitions, but only type definitions and object declarations: the real
object definitions have to be done in C source files.
Thats why it is required extern keyword to appear everywhere except on the real definition.
Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 24 of 36

This can be easily done in the header files with the following automatic mechanism of defining/exporting:
#ifdef _FILENAME_MODULE
#define _EXT_MYMOD
#else
#define _EXT_MYMOD extern
#endif
_EXT_MYMOD unsigned long int Counter;
_EXT_MYMOD unsigned char Status;
Of course, header files that declare functions or external variables must absolutely be included in the C
source file that defines the function or variable itself.
In this way, the compiler can do type checking and the external declarations will always agree with the
definitions.

7.2

Functions

Each function should be preceded by a block comment prologue that gives a short description of what the
function does and (if not clear) how to use it, as mentioned above in the Function Prologue paragraph.
Discussion of non-trivial design decisions and side-effects is also appropriate. Avoid duplicating information
already obvious from the code.
The return type of functions should always be declared: do not default to int, if the function does not return a
value then it should be given return type void.
The opening and closing brace of the function body should be alone on a line beginning in column 1.
unsigned char SendDataToPeripheral (unsigned char data)
{
...
function body
...
} /** SendDataToPeripheral */
Each parameter should be declared (do not default to int).
In general the role of each local variable in the function should be described.
This may be done in its own line.
Loop counters called "i, string pointers called "s, and integral types called "c and used for characters are
typically excluded.
If a group of functions all have a similar parameter or local variable, it helps to call the repeated variable by
the same name in all functions. (On the contrary, avoid using the same name for different purposes in
related functions.) Similar parameters should also appear in the same place in the various argument lists.
If the function uses any external variables (or functions), declare them globally in the file using the extern
keyword and not inside the function itself: extern declarations inside function body are absolutely
unacceptable.
Avoid local declarations that override declarations at higher levels. In particular, local variables should not be
re-declared in nested blocks.
Local variable declarations should be separated from the function's statements by a blank line.
Also feel free to include other blank lines, particularly to separate major blocks of code.
Functions should be short. Do not be afraid to break functions down into smaller helper functions.
Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 25 of 36

If they are static to the module an optimizing compiler can inline them again, if necessary.
Helper functions can also be reused by other functions.
Prototype each function, either the static functions either the exported ones.
A common mistake is to omit the declaration of external math functions that return double. The compiler
then assumes that the return value is an integer and the bits are dutifully converted into a (meaningless)
floating point value.

7.3

Expressions and Statements

There should only be one statement per line unless the statements are very closely related.
The null body of a for or while loop should be alone on a line and commented so that it is clear that the null
body is intentional and not missing code.
while (MyTimeout > 0)
;
/* DO NOTHING */
Infinite loops should be done with for statement, and not with while statement:
for (;;) {
...
}
Do not default the test for non-zero but explicitate it as a negative test for zero, i.e.
if (funct() != FALSE)
is better than
if (funct())
or
if (funct() == TRUE)
even though FALSE may have the value 0 which C language considers to be false.
An explicit test will help you out later when somebody decides that a failure return should be --1 instead of 0.
Explicit comparison should be used even if the comparison value will never change; i.e., in the following
code it is not recommended to write
if (!(bufsize % sizeof(int)))
but it should be written instead as
if ((bufsize % sizeof(int)) == 0)
to reflect the numeric (not boolean) nature of the test.
It is common practice to declare a boolean type boolean in a global header file. The special names improve
readability immensely.
typedef int
#define FALSE
#define TRUE

boolean;
0
1

or
typedef enum { NO = 0, YES } boolean;
Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 26 of 36

Even with these declarations, do not check a boolean value for equality with 1 (TRUE, YES, etc.); instead
test for inequality with 0 (FALSE, NO, etc.).
Most functions are guaranteed to return 0 if false, but only non-zero if true.
Thus,
if (func() == TRUE)
must be written
if (func() != FALSE)
An exception is made for pointers, since 0 is the only language-level representation for the null pointer.
It is even better (where possible) to rename the function/variable or rewrite the expression so that the
meaning is obvious without a comparison to true or false (i.e., rename to isvalid()).

7.3.1 Compound Statements


A compound statement is a list of statements enclosed by braces.
There are many common ways of formatting the braces.
As mentioned above in the Whitespaces, Indentation and Layout paragraph, the adopted style is the One
True Brace Style (1TBS).
control {
statement;
statement;
}
With the previous style, the else part of an if-else statement and the while part of a do-while statement
should appear on the same line as the close brace.
However, it is also acceptable a style where the braces are always on a line of its own.
control
{
statement;
statement;
}

7.3.2 Assignment
Assignment statements should have the operator preceded and followed by a blank space, as in the
following examples.
a = b;
c += 5;
Embedded assignment statements (assignment of a value to a variable within a statement, as showed in the
following example) should be avoided, if possible:
while ((c = getchar()) != EOF) {
...
}
Please keep in mind that ++ and -- operators count as assignment statements.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 27 of 36

Multiple assignment, as in the following example, is discouraged too:


a = b = c = 0;

7.3.3 Control Flow


All do-while loops should always have braces around the body.
do
{
statement;
}
while (expression);
Where possible, a switch statement shall be used in preference to a deeply nested series of if statements.
switch (expr) {
case ABC:
statement;
...
statement;
break;
case DEF:
statement;
break;
case GHI:
/* DO NOTHING */
break;
case UVW:
statement;
/* FALL-THROUGH */
case XYZ:
statement;
break;
default:
break;
} /* end switch */
In switch statements, be sure every case ends with either a break or /* FALL-THROUGH */ comment.
Avoid unconditional control transfer via continue or return.
When a block of code has more than one single label, the labels are placed on separate lines.
The fall-through feature of the C languages switch statement, (that is, when there is no break between a
code segment and the next case statement) must be explicitly commented for future maintenance.
In the previous example, the second-last break is unnecessary, but is required because it prevents a fallthrough error if another case is added later after it.
The default case, followed by a break, should always be added as switch terminator, even if no statement
is included.
Also, add an explicit /* DO NOTHING */ comment for the empty cases.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 28 of 36

For if statements, the following layout shall be used:


if (expression) {
statement;
}
else if (expression) {
statement;
}
else if (expression) {
statement;
}
else {
statement;
}
The statements of both the if and else sections should be enclosed in braces (called fully bracketed syntax).
if (expr) {
statement;
}
else {
statement;
statement;
}
Braces are absolutely essential in if-if-else sequences with no second else such as the following example: if
an inner nested block is braced, then the outer blocks should be too.
if (ex1) {
if (ex2) {
funca();
}
}
else {
funcb();
}
As a general rule, the use of braces in any kind of if statements is strongly recommended.
An if-else with else if should be written with the else conditions left-justified, as already showed.
if (STREQ(reply, "yes")) {
statements for yes
...
}
else if (STREQ(reply, "no")) {
...
}
else if (STREQ(reply, "maybe")) {
...
}
else {
statements for default
...
}
This layout looks like a generalized switch statement and the spacing reflects the switch between exactly
one of several alternatives rather than a nesting of statements.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 29 of 36

Remember to always add the conclusive else in all if-else if sequences, even if no statement is included: in
this case, the explicit comment sequence /*-*/ [slash, asterisk, minus, asterisk, slash] is required on the
conclusive else.
if (expr) {
statement;
}
else if (expr) {
statement;
statement;
}
else { /*-*/
}
If statements with unconditional control transfer via break, continue, goto or return should be avoided:
if (level > limit) {
return (OVERFLOW)
}
...
return (level);
Goto statements should never be used, as in any well-structured code.
The only place where they might be usefully employed is to break out of several levels of switch, for and
while nesting, although the need to do such a thing may indicate that the inner constructs should be broken
out into a separate function, with a success/failure return code.
for (...) {
while (...) {
...
if (disaster)
goto error;
...
}
}
...
error:
clean up the mess
When a goto is necessary, the accompanying label should be alone on a line and spaced one stop to the left
of the code that follows. The goto should be commented (possibly in the block header) as to its utility and
purpose.
In the same way, break and continue should be used rarely.
Functions should have a single point of exit: only one return statement per function is allowed.

7.4

Operators

Unary operators should not be separated from their single operand, while all binary operators (except .' and
->') should be separated from their operands by blanks.
a++
a + b
(a <= b) && (c > 3)
boat.type
Since C language has some unexpected precedence rules, expressions involving mixed operators should
always be parenthesized.
Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 30 of 36

Of course, too many parentheses can make a line harder to read because humans are not good at
parenthesis-matching.
Binary comma operator generally should be avoided.
The comma operator is most useful to provide multiple initializations or operations, as in for statements.
Complex expressions, for instance those with nested ternary ? : operators, can be confusing and should be
avoided if possible.
There are some macros like getchar where both the ternary operator and comma operators are useful. The
logical expression operand before the ? : should be parenthesized and both return values must be the same
type.

7.5

Constants

Numerical constants should not be coded directly.


The #define feature of the C preprocessor should be used to give constants meaningful names.
Symbolic constants make the code easier to be read.
Defining the value in one place also makes it easier to administer large software projects since the constant
value can be changed uniformly by changing only the #define.
As mentioned above in Declarations paragraph, the enumeration data type is a better way to declare
variables that take on only a discrete set of values, since additional type checking is often available, and
defines are rarely visible in debuggers.
enum { Red = 0xF00, Blue = 0x0F0, Green = 0x00F };
At the very least, any directly-coded numerical constant must have a comment explaining the derivation of
the value.
Constants should be defined consistently with their use; i.e. use 540.0 for a float instead of 540 with an
implicit float cast.
There are some cases where the constants 0 and 1 may appear explicitly.
For example if a for loop indexes through an array, then
for (i = 0; i < ARYBOUND; i++)
is reasonable, while the code
door_t *front_door = opens(door[i], 7);
if (front_door == 0) {
...
}
is not.
In the last example front_door is a pointer.
When a value is a pointer it should be compared to null pointer instead of 0.
The AdelSystem Default Library defines the _NULL_PTR symbol as null pointer, in defines.h file.
Even simple values like 1 or 0 are often better expressed using defines like TRUE and FALSE (sometimes
YES and NO read better).
Simple character constants should be defined as character literals rather than numbers.
Non-text characters are discouraged as non-portable. If non-text characters are necessary, particularly if
they are used in strings, they should be written using a escape character of three octal digits rather than one
(i.e., '\007').
Even so, such usage should be considered machine-dependent and treated as such.
Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard
7.6

Version: 1.0 Draft A


Date: 19/10/2012
Page: 31 of 36

Macros

Macros should avoid side effects.


Complex expressions can be used as macro arguments, but operator-precedence problems can bring some
side effects.
To reduce them, write macros that evaluate each argument exactly once.
Also, fully parenthesize all arguments. When the macro is an expression, parenthesize the whole macro
body.
A macro behaves like a function if it evaluates its arguments exactly once and does not assign values to
named arguments.
However, keep always in mind that function parameters are passed by value, while macro arguments are
passed by name substitution, and pay attention in interchanging macros and functions.
Macros should avoid using globals, since the global name may be hidden by a local declaration.
Macros that change named parameters (rather than the storage they point to) or may be used as the lefthand side of an assignment should mention this in their comments.
Macros that take no parameters but reference variables, are long, or are aliases for function calls should be
given an empty parameter list, i.e.,
#define OFF_A()

(a + OFFSET)

Macros do save function call/return overhead, but when a macro gets long, the effect of the call/return
becomes minimum, so a function should be used instead.
Except for typecasts, macros should contain keywords only if the entire macro is surrounded by braces.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 32 of 36

8. Other Topics
8.1

Conditional Compilation

Conditional compilation is useful for things like machine dependencies, debugging, and for setting certain
options at compile-time.
Be careful when you use conditional compilation: various controls can easily combine in unforeseen ways.
Conditional compilation should generally be on a feature-by-feature basis: consider restricting a dependent
code into a single module (per feature).
I.e., if you need to have different code for the main application and for the bootloader, instead of having
#ifdef APP and #ifdef BOOT everywhere, try to have files app.c and boot.c with identical interfaces.
It is also a good practice to protect with an #error directive the critical code:
#if defined (ABC)
some code
#elif defined (DEF)
other code
#else
#error "Undefined symbol"
#endif
and to document the end of the conditional code:
#ifdef ABC
some code
#else
other code
#endif /* ABC */

8.2

Debugging

It could be very useful to define a special symbol for debug purpose, like
#define _DEBUG_
and use it to build some special code:
#ifdef _DEBUG_
...
#else
...
#endif /* _DEBUG_ */
As exception of what mentioned below in the Conditional Compilation paragraph, in order to have an
efficient and maintainable code, the following rules must be covered.

There must be only one debug symbol.

There must be only one option in the project where define (or not define) the debug symbol: the best
solution is to have it definable in the project environment area.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard
8.3

Version: 1.0 Draft A


Date: 19/10/2012
Page: 33 of 36

Special Considerations

This section contains some miscellaneous do's and don'ts.

Do not change syntax via macro substitution, unless you are 100% aware that there are not any side
effect. It could make the software unintelligible to all but the author.

Do not use floating-point variables where discrete values are needed. Using a float for a loop counter
is a hook for disaster. Always test floating-point numbers (and not only floating-point numbers) as <=
or >=, never use an exact comparison (== or !=).

It is also safe not to compare an integer variable with an exact value (a == 5) but instead try to
compare it with a range of values using <=, >=, or !=. For example, if the variable is a timer, it may
jump from 4 to 6 and miss the check.

Compilers have bugs. Common trouble spots include structure assignment and bitfields. You cannot
generally predict which bugs a compiler has. You could write a software that avoids all constructs
that are known broken on all compilers. You will not be able to write anything useful, you might still
encounter bugs, and the compiler might get fixed in the meanwhile. Thus, you should write "around
compiler bugs only when you are forced to use a particular buggy compiler.

Do not rely on automatic beautifiers. The main person who benefits from good software style is the
programmer him/herself, and especially in the early design of handwritten algorithms or pseudocode. Automatic beautifiers can only be applied to complete, syntactically correct software and
hence are not available at development stage. Programmers can do a better job of making clear the
complete visual layout of a function or file, with the normal attention to detail of a careful
programmer. (In other words, some of the visual layout is dictated by intent rather than syntax and
beautifiers cannot read minds.) Sloppy programmers should learn to be careful programmers instead
of relying on a beautifier to make their code readable.

Accidental omission of the second = of the logical compare is a problem. Use explicit tests. Avoid
assignment with implicit test.
if (abool = bbool) { ...

Explicitly comment variables that are changed outside of the normal control flow, or other code that
is likely to break during maintenance.

Modern compilers will put variables in registers automatically. Use the register keyword only to
indicate the variables that you think are most critical.

8.3.1 Suggestions for Portability


The advantages of portable code are well known: this paragraph gives some guidelines for writing portable
code.

Organize source files so that the machine-independent code and the machine-dependent code are
in separate files. Comment the machine dependence in the headers of the appropriate files.

Any behavior that is described as "implementation defined should be treated as a machine


(compiler) dependent. Assume that the compiler or hardware does it some completely screwed way.

Pay attention to word sizes. Objects may have non-intuitive size, pointers are not always the same
size as ints, the same size as each other, or freely interchangeable.

Some machines have more than one possible size for a given type. The size you get can depend
both on the compiler and on various compile-time flags.

The void* type is guaranteed to have enough bits of precision to hold a pointer to any data object.
The void(*)() type is guaranteed to be able to hold a pointer to any function. Use these types when
you need a generic pointer. (Use char* and char(*)(), respectively, in older compilers). Be sure to
cast pointers back to the correct type before using them.

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 34 of 36

Even when an int* and a char* are the same size, they may have different formats. For example, the
following code will rise a warning that must be deeply investigated:
int *p = (int *) malloc(sizeof(int));
free(p);

Note that the size of an object does not guarantee the precision of that object. The Cray-2 may use
64 bits to store an int, but a long cast into an int and back to a long may be truncated to 32 bits.

The integer constant zero may be cast to any pointer type. The resulting pointer is called a null
pointer for that type, and is different from any other pointer of that type. A null pointer always
compares equal to the constant zero. A null pointer might not compare equal with a variable that has
the value zero. Null pointers are not always stored with all bits zero. Null pointers for two different
types are sometimes different. A null pointer of one type cast in to a pointer of another type will be
cast in to the null pointer for that second type.
o

A null pointer might not compare equal with a variable that has the value zero. Null pointers
are not always stored with all bits zero.

Null pointers for two different types are sometimes different.

A null pointer of one type, cast in to a pointer of another type, will be cast in to the null
pointer for that second type.

Floating-point numbers have both a precision and a range. These are independent of the size of the
object. Thus, overflow (underflow) for a 32-bit floating-point number will happen at different values on
different machines. Also, 4.9 times 5.1 will yield two different numbers on two different machines.
Differences in rounding and truncation can give surprisingly different answers.

On some machines, a double may have less range or precision than a float.

On some machines the first half of a double may be a float with similar value. Do not depend on this.

Do not use floating-point numbers unless it is really necessary.

Watch out for signed characters. If you must assume signed or unsigned characters, comment them
as SIGNED or UNSIGNED. Unsigned behavior can be guaranteed with unsigned char.

Avoid assuming ASCII. If you must assume, document and localize. Remember that characters may
hold (much) more than one byte.

Code that takes advantage of the two's complement representation of numbers on most machines
should not be used. Optimizations that replace arithmetic operations with equivalent shifting
operations are particularly suspect. If absolutely necessary, machine-dependent code should be
#ifdeffed or operations should be performed by #ifdeffed macros. You should weigh the time
savings with the potential for obscure and difficult bugs when your code is moved.

In general, if the word size or value range is important, typedef "sized types. Large software
projects should have a default header file which supplies typedefs for commonly-used widthsensitive types, to make it easier to change them and to aid in finding width-sensitive code.
Unsigned types other than unsigned int are highly compiler-dependent. If a simple loop counter is
being used where either 16 or 32 bits will do, then use int, since it will get the most efficient (natural)
unit for the current machine.

Data alignment is also important. For instance, on various machines a 4-byte integer may start either
at any address, or only at an even address, or only at a multiple-of-four address. Thus, a particular
structure may have its elements at different offsets on different machines, even when given
elements are the same size on all machines. Remember that a structure, due to internal data
storage organization, might take more data bytes than the sum of its elements. Indeed, a structure of
a 32-bit pointer and an 8-bit character may have 3 sizes on 3 different machines. As a corollary,
pointers to objects may not be interchanged freely; saving an integer through a pointer to 4 bytes
starting at an odd address will sometimes work, sometimes cause a core dump, and sometimes fail
silently (clobbering other data in the process). Pointer-to-character is a particular trouble spot on
machines which do not address to the byte. Alignment considerations and loader peculiarities make

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 35 of 36

it very hard to assume that two consecutively-declared variables are together in memory, or that a
variable of one type is aligned appropriately to be used as another type.

The byte order to represent a word has 2 typical configurations:


o

place the least significant byte (LSB) at the lowest address and increasing significance with
increasing address (little-endian or Intel);

place the most significant byte (MSB) at the lowest address, decreasing significance with
increasing address (big-endian or Motorola).

The order of bytes in a word and of words in larger objects (say, a double word) might not be the
same. Hence any code that depends on the left-right orientation of bits in an object deserves special
scrutiny. Bit fields within structure members will only be portable so long as two separate fields are
never concatenated and treated as a unit. Actually concatenating any two variables is non-portable.

Different compilers use different conventions for returning structures. This causes a problem when
libraries return structure values to code compiled with a different compiler. Structure pointers are
not a problem.

Do not make assumptions about the parameter-passing mechanism. especially pointer sizes and
parameter evaluation order, size, etc. The stack may grow up or down (indeed, there may not even
be a stack!). Parameters may be widened when they are passed, so a char might be passed as an
int, for instance. Arguments may be pushed left-to-right, right-to-left, in arbitrary order, or passed in
registers (not pushed at all). The order of evaluation may differ from the order in which they are
pushed. A compiler may use several (incompatible) calling conventions.

On some machines, the null character pointer ((char *)0) is treated the same way as a pointer to a
null string. Do not depend on this.

Remember this convention: NUL (one L) = ASCII 0; NULL (two LL) = pointer to nothing.

Do not modify string constants.

The address space may have holes. Simply computing the address of an unallocated element in an
array (before or after the actual storage of the array) may crash the software. If the address is used
in a comparison, sometimes the software will run but clobber data, give wrong answers, or loop
forever. In ANSI C, a pointer into an array of objects may legally point to the first element after the
end of the array; this is usually safe in older implementations. This "outside pointer may not be dereferenced.

Only the == and != comparisons are defined for all pointers of a given type. It is only portable to use
<, <=, >, or >= to compare pointers when they both point in to (or to the first element after) the same
array. It is likewise only portable to use arithmetic operators on pointers that both point into the same
array or the first element afterwards.

Word size also affects shifts and masks. The code x &= 0177770 will clear only the three rightmost
bits of an int on some machines, while on other machines it will also clear the upper two bytes. Use
instead x &= ~07 which works properly on all machines. Bitfields do not have these problems.

Side effects within expressions can result in code whose semantics are compiler-dependent.
Compilers do differ, in fact C-language order of evaluation is explicitly undefined in most places.

Be suspicious of numeric values appearing in the code ("magic numbers).

Avoid preprocessor tricks.

Become familiar with existing library functions and defines. (But not too familiar. The internal details
of library facilities, as opposed to their external interfaces, are subject to change without warning.
They are also often quite un-portable.) You should not be writing your own string compare routine,
terminal control routines, or making your own defines for system structures. "Rolling your own
wastes your time and makes your code less readable, because another reader has to figure out if
you are doing something necessary or not in the new implementation. It also prevents your software
from taking advantage of any microcode assists or other means of improving performance of system

Author:

Reviewed by:
Stefano Braidi

Approved by:

Software Development
Standard

Version: 1.0 Draft A


Date: 19/10/2012
Page: 36 of 36

routines. Furthermore, it is a fruitful source of bugs. If possible, be aware of the differences between
the common libraries (such as ANSI, POSIX, and so on).

Use explicit casts when doing arithmetic that mixes signed and unsigned values.

The inter-procedural goto, longjmp, should not be used. Many implementations "forget to restore
values in registers. Declare critical values as volatile if you can or comment them as VOLATILE.

Some linkers convert names to lower-case and some only recognize the first six letters as unique.
Programs software may break quietly on these systems.

Beware of compiler extensions. If used, document and consider them as machine dependencies.

Except for bootloader procedures, a software cannot generally execute code in the data segment or
write into the code segment. Even when it can, there is no guarantee that it can do so reliably.

Author:

Reviewed by:
Stefano Braidi

Approved by:

You might also like