Standard - C Coding Convention
Standard - C Coding Convention
Code 09ae-HD/PM/HDCV/FSOFT
Version 1/3
RECORD OF CHANGE
*A - Added M - Modified D - Deleted
10/12/2005 1/2
TABLE OF CONTENTS
Record of change ..................................................................................................................... 2
Table of Contents..................................................................................................................... 3
1. Introduction ....................................................................................................................... 5
2. Program Structure............................................................................................................. 6
6. Portability........................................................................................................................ 27
7.2.Use #define and #ifdef keywords. With this method, in the main source or include file, use
#define keyword to define a symbol to turn on the debugging and use #ifdef keyword to check
this symbol is defined or not before writing log to file. ............................................................. 29
7.3.Use flag which is settable by user. Inside the Log function, developers will check the input
flag against the setting flag before writing to file. ..................................................................... 29
1. INTRODUCTION
1.1. Purpose
This document presents a compilation of the C/C++ coding standards. The aim is to provide
the programmer with program templates that highlight the layout and structure of C and
C++ source programs.
An attempt has been made to outline a style that is easily readable, amenable to
documentation and acceptable to most programmers. These standards were adapted
where necessary to meet the project reality.
Most of the details given here apply equally to C and C++ programs. Where details only
apply to C++, this will be stated explicitly.
It is assumed that the reader has knowledge of both the C and C++ programming
languages. Whatever programming style you use, be consistent, and use an ANSI C or
C++ compiler.
Fsoft projects
1 04e-QT/PM/HDCV/FSOFT Process_Coding
2. PROGRAM STRUCTURE
Example:
- Source/module A/inc : contains header file of which are only used in module A
The first part of a C/C++ program contains the source file header. The source file header
details the creation/modification date, author, program name, and a description of the
program. The header also contains a AMS copyright notice.
Use AMS as the author for all programs which are submitted to the customer, and use your
personal name as the author only during development. This prevents clients that have our
source code from contacting you directly for problems with code.
/******************************************************************
********
* ++
* Author: American Management Systems
* Module Name : xxxxxxxx.cpp
*
* Description :
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*
* Mod. History : DD.MMM.YY - Full Name
* File first created
* CR# PROJECT
* Description
*
* --
*******************************************************************
*****/
The extract marks („* ++‟ & „* --‟) must be present in every header so that an utility can
extract the headers for documentation purposes. Be sure to use the correct date format
(DD.MM.YY) and the developers full name in all file headers.
This section describes how the header and source file should be organized. The first
section outlines the header file (.h) layout. The second section outlines the souce file (.cpp)
layout.
File header
Include files
Global variables
Class definition 1
....
Class definition n
#endif
The #ifndef at the top of the file prevents multiple definitions. The <file name descriptor>
should be the name of the file with an underscore substituted for the dot ('.'). For example,
if the file is named PAGE_AP7.H, the file descriptor should be call PAGE_AP7_H.
File header
Include files
Message map
Method 1
....
Method n
Note that the use of global variables is discouraged. They should only be used where they
appear in external libraries (e.g. errno).
Each file should begin with an include file list. System include files should come first,
followed by include files from other packages, followed by developer-defined include files.
Developer-defined include files should be enclosed by quotes, all other include files
enclosed by < and >. Relative path names should be employed for the include file names,
using compiler options (e.g. -I) to provide include file search paths where necessary.
After the include files, global definitions and variables (if any) should be placed in the
following order:
global variables which are exported (hopefully there will be none of these)
Preceding each method there should be a header. This header should be filled out at the
time the method is written. The method header describes the name of the method, the
function class, the method purpose, each parameter passed to the method, the date of the
method, the author, the project the method is written for, and a brief description of the
purpose. If a parameter is passed by reference, note whether the method modifies the
parameter. If a value is returned, possible return values are documented. Any global
variables accessed and/or modified should be noted. In general, these should be kept to a
minimum. There is also a section for logging modifications, with the date of modification,
corresponding author, the modification number, the project the modification is being made
for, and a description of the modification.
When making modifications within the body of a function, it is important that the changes be
documented within the code. This is done with the use of modification flagging. Before
changes within the code, there is a beginning modification flag that contains the type of
modification (PN/CR/SR) and the number associated with the modification.
example.
//++CR 10132
At the end of the changes, there is an ending modification flag that contains the same
information as the beginning modification flag with "--" replacing "++".
example.
//--CR 10132
All functions have prototypes. Functions that have no return status should declare a void
return type in the function definition and prototype declaration.
For example,
/******************************************************************
************
* ++
* Method name : Class::Method()
* Description :
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
*
* Parameters :
*
* Global Variables
* Accessed : none
* Modified : none
*
* Return values :
*
* Modifications : DD.MMM.YY - Full Name
* Function first created
* CR/PN# - Project
* Description
*
* --
*******************************************************************
**********/
Note the extract marks („* ++‟) at the beginning and end of the header. Don‟t use the extract
marks anywhere else. Be sure to use the correct date format (DD.MMM.YY) and full name
in the modification log.
The first page of the include file is the include file header, which has the same format as the
source file header. In general there are two types of information that may be listed in an
include file: public information and private information. All public information must go into an
include file, where it is accessible to the user. Information is considered public if it is
required by the user for effective use of the functions with the C source code file. Typical
public information is represented by return codes, exported function types, public structure
and other type definitions etc.
Private information is represented by any structures, functions or constants that the user
does not require to use the functions in the C source file. Whereas public information
defines the interface, private information defines the implementation. The developer should
feel free to alter the private implementation as long as it does not affect the public interface.
Thus, private information should be kept in a separate include file or incorporated in the
source file to which it refers.
System include files come first, followed by include files from other software packages,
followed by user-defined include files. Nesting of include files is permitted.
Include files are organized similarly to source files. If any of the sections contained in the
source files are contained in an include file they will follow the same layout as the C/C++
source file:
Include files
Function prototypes
An include file should not include global variables or C/C++ function definitions (except for
C++ inline functions). These should be placed in source modules.
Where C functions have been compiled with a compatible C compiler, and the include files
have not been explicitly prepared for C++, this must be made known to the C++ compiler.
C++ uses different names inside the compiler for its identifiers than C does. To tell the
compiler that a C library is being used with C names, an alternate-linkage specification is
required. Thus to use the memory functions (memcp() etc.), one must specify:
3. PROGRAMMING STYLE
This next series of sections will cover various programming topics. Most of the topics are
focused on taking many different application programming styles and creating a standard
that is to be used for the PROXIGATE GUI application.
This section will cover the naming schemes for variables, classes, and macros to create
naming conventions standards for the application.
Source and include file names should be lower case, with the use of an underline ("_") as a
separator character. C/C++ source code files should end with one of the strings: .c, .C,
.CPP, depending on the operating system and the compiler used. Include files have the
extension ".h". If an include file is used principally or only with one source file, it should
have the same base name.
These should be mixed upper and lower case, and begin with a capital letter. Individual
words within the name begin with a capital letter, for example DoSomethingLikeThis().
Embedded acronyms should be left as capitals, e.g. DoThisASAP(). Underscores in these
names should be used very sparingly, if at all.
Ensure that all C functions which are only local to a source module are made static, so as
to reduce global name space pollution.
Each function has to do one task, so naming function should obey the following rules (as
Microsoft often use):
-Function do a task should have names as: Do[Something] or Make[Something] (the first is
a verb and then a noun).
The majority of Windows applications are written using the popular "Hungarian" variable-
naming convention. This method of variable-naming reduces coding errors due to
mismatched types, and will eliminate the need to repeatedly check the data type of a
variable when analyzing source code.
3.1.3.1. C Convention
a type
example:
Base Types
sz null-terminated string
ch character
l long
f boolean (flag)
x structure
c C++ class
Examples:
All variables and constants are simply named after their type, followed by a qualifier to
make the name unique in its context. Variables may also have type prefixes which help
identify how the variable is used.
Type Prefixes
G A global variable
P A pointer
Examples:
For boolean variables (f), the qualifier is the condition under which the variable is true.
Example:
Standard Qualifiers
Nil A special and illegal value, that is distinguishable from all legal
values
Mac Current maximum. The top of a stack, current upper limit for
legal indexes. Also the number of elements in an array when
Min is 0.
-The first(the type of variable) is prefix to indicate it is a global variable, static variable, class
member variable or temporary variable.
g_ A global variable
s_ A static variable
-The second indicates the data type. It can obeyed the recommendation in the following
table:
Base Types
w WORD
l long
b BOOL/bool
p pointer
c char/TCHAR
dw DWORD
n integer/short/long
u unsigned int/short/long/char
arr Array
map Map
lst List
h HANDLE
rc RECT/CRect
class X
private:
TCHAR m_lpszName[MAX_PATH];
Class names follow the same rules as function names, whereas instance names follow
those of variable names. It is recommended that instances which occur only once in a
program should be prefixed with the string "the", e.g. "theApplication".
Should:
enum OBJECTTYPE
OTCAR = 1,
OTPLANE = 2
};
Should not:
#define OTCAR 1
#define OTPLANE 2
In ANSI C programs, all comments should be marked with the delimiters /* and */. The
double slash comment, //, is non-ANSI and should only be used in C++ programs.
Comment blocks should be used to separate and describe following sections of code.
These comments should be indented to the level of the code and use normal punctuation
rules.
Comment lines should be used on the same line as the statement or declaration to describe
its use or operation. They should start with a small letter, as they are generally not complete
sentences. In C++, a one line comment is preceded with "//".
example:
#define ACT_FILE "ACT_FILE" // ini file section header
Code that is no longer in use should be removed from the source. Occasionally, the
programmer may wish to retain old during testing. This old commented code must be
removed prior to the migration from the developers area. Sections of old code that is
removed must be marked with a modification flag as described in section 2.1.3.
The following sections describe the formatting of statements within the PROXIGATE GUI
application.
3.3.1. Indentation
Indentation across the screen should use a width of 4 spaces. This can be achieved using
TAB characters of 4 spaces width or 4 separate spaces. Within the Visual C++editor, the
tab setting should be set and confirmed by the developer prior to code modification. Select
the Editor dialog from the Options menu.
Blank lines should be used to separate unrelated sections of code and where it will aid in
clarity and make the code more readable. They should not be inserted too liberally.
The following should start at the first column in a source file or include file:
Macros
global typedefs
class definitions
function definitions (unless as inline within a class definition in C++) - only source file
For example:
#include <iostream.h>
MyClass::MyClass ()
{
wMe = 1;
wMyself = 2;
w = 3;
}
3.3.2. Spaces
Spaces should be used to separate all operators from their operands. Conditional
keywords, such as if, while, switch, and for should be separated from their succeeding
bracket by a space. There should not be a space in a function call between the function
name and its parameter list, enclosed in round brackets. Spaces should be used after a
semicolon and a comma, as is used in normal writing. Spaces should not in general be
used to separate function arguments from their enclosing round brackets or in other
expressions enclosed by round brackets, unless very complicated expressions are used,
where they might be needed for clarity.
Lines should be split up if they are longer than 80 characters. This aids the use of editors
and for printing purposes. This is best done using an indented structure.
With source code line has lots of expression, they should be divided to each expression in
one line. Example:
if..else
if (condition)
{
statement1;
statement2
}
else
{
statement3;
statement4;
}
In the next example, it should be noted that the second variant takes up considerably more
space on paper. The else in the first variant must be started on a new line. If nested
conditions are used, insert extra curly brackets to ensure clarity. With an if and a single line
of code, both these constructions are allowed:
Version 1 Version 2
If (condition) if (condition)
statement; {
statement;
}
Version 1 Version 2
The first variant has the advantage of occupying one less indent, yet there is a slight
disadvantage in the readability. Where a case condition contains a large number of
statements, it is advisable to use curly brackets to contain them.
For example:
switch (condition)
{
case 1:
{
statement1;
..
statementn;
}
case 2:
{
statement1;
...
}
}
This allows checking of brackets for indentation purposes etc. In general there should be at
most one statement per line.
iFirstVar = 1;
return(iFirstVar);
}
Space permitting, the function definition argument list should be written on a single line.
When programming in C, there should be a blank line separating the last variable
declaration from the first statement in the function. There should be a space following the
function base name to help to differentiate the function definition from the function
declaration (function prototype), especially when they are both in the same file.
There is a significant difference between C and C++ for functions with empty argument lists.
In C, the function declaration
int Func2();
means "a function with any number and type of argument." This prevents type-checking, so
in C++ it means "a function with no arguments."
Only 1 return statement should be used in a function. Do not return at the middle, but
always make it the last statement of the function.
In general, use of goto is considered to be a bad habit, but proper use of it can make the
code more robust, as well as easier for error handling/maintenance. It should be used in
combination with rule 3.3.6 above.
- Use only 1 label in a function, this is the place for handling errors (display message,
trace logging, free allocated memory).
- In case of error, do not return immediately but goto the label instead.
Example:
if (NULL == param)
// error handling
goto End_of_function;
if (NULL == pPixels)
// error handling
goto End_of_function;
// other processing
End_of_function:
if (pPixels)
free(pPixels);
In the example above, whether error has occurred or not, allocated memory always freed
properly.
Should place constant on the LEFT when writing comparison with if statement. In case only
1 „=‟ is written, the compiler generate the error immediately.
For example:
The ANSI C library variant should always be used here (see include file varargs.h).
Normally, a C++ member function contains a hidden additional initial parameter, which is
not seen by the developer. This parameter points to the object instance, thus allowing this
instance to be accessed easily with the function itself. For example, when such a member
function is used as a C callback function which has specific arguments, there will be one
argument too many. This problem can be alleviated by using a static member function as
the callback function.
example:
class CSalesman;
A class definition on the other hand should have the following format:
class CSalesman
{
private:
char *pszName;
char *pszDepartment;
long lSalary;
protected:
void SetSalary(long lSalary);
public:
Salesman(char *pszName, char *pszDepartment);
char chGetName();
char chGetDept();
long lGetSalary();
}
The private keyword should always be stated explicitly.
6. PORTABILITY
Attempt to avoid the use of language constructs particular to the compiler, as these can
compromise the potential portability of the program. Always use the ANSI compiler switch
where the use of an ANSI compiler is optional.
#ifdef _UNICODE||UNICODE
#else
#endif
Or the sample below to make the code running on Linux without changing the code.
#ifdef _WIN32||WIN32
#else
#endif
The size of the data type int depends on the machine word length and is therefore different
for different machines. Do not use int unless you must in order to interface with a library or
third party package function. Should there be any doubts as to the result of an integer
operation then use the data type long.
It is not in general guaranteed that int occupies a complete machine word. C demands only
that the value of short is contained in the value of int and that this is contained in long.
example:
#define MASK 0177770 // incorrect
int iX;
iX &= MASK;
The 3 furthermost right bits of x are only correctly cleared if int is 16 bits long. If int has
more than 16 bits then the leftmost bits of x will also be cleared. One can avoid problems
by using the following #define statement:
example:
#define MASK (~07)
int iX;
iX&= MASK;
Keep all references to external variables in a special source file, to improve code portability
and maintainability. Ensure that all modules have access to this file (using #include).
As far as possible, do not store data such as data dictionaries in binary form, although it
may be used as such in the program. Use conversion routines to store it in a human-
readable form, which is easily accessible through a text editor. This simplifies program
testing.
6.4. Errors
Every tested condition or system call should have an error trap in case of failure. Each
function should set and return a status flag to its calling program so any errors can be
handled correctly or the program terminated cleanly.
6.5. Compiling
Final versions of programs should compile without any warnings with the compiler warning
level turned up high.
7. LOGGING/DEBUGGING CONVENTION
This section will describe some guidelines to help developers in debugging their
applications.
Logging is one of simple but effective way to debug the application. Define a framework to
log information about current point of execution will help developers to trace the flow and
isolate problems easily. Of course, this technique also has a major drawback is it will impact
the performance of the application so it should be excluded when performance test is
required.
To implement logging, simply define a global function to write logging information to file or
system debugger as following sample:
Log(long lFlag, char* lpszModule, char* lpszFormat, ...);
In this function, developers are freely to format the content to be sent to debugger.
Following is a sample:
2002/09/17 17:30:18 [DEBUG] (service) Start service
2002/09/17 17:30:18 [DEBUG] (service) Open from file [C:\FOO.TXT]
2002/09/17 17:30:18 [ERROR] (service) Read from file failed
[C:\FOO.TXT]
In order to filter log for developers or users, there are two simple methods:
7.2. Use #define and #ifdef keywords. With this method, in the main source or include
file, use #define keyword to define a symbol to turn on the debugging and use #ifdef
keyword to check this symbol is defined or not before writing log to file.
7.3. Use flag which is settable by user. Inside the Log function, developers will check the
input flag against the setting flag before writing to file.
Both methods can be used simultaneously to provide full features for the application.
Note:
- For Linux or U*X platform, function vsyslog() can be used to send to system log.
Consult vsyslog man page for more detail of this function.
In order to easily debug and troubleshoot the system in runtime, log writing must ensure:
provide enough but not excessive information. Following guidelines should be taken care:
- For complicate or very important functions, should write log at the beginning and the
end of the functions, and all parameters should be written at the beginning of function,
for example:
- For a simple function, one log should be written with a filter flag so that for deployment,
we can omit this log by using configuration.
- Before switch statement, should have a log to provide the first value for evaluation.
Inside each case, if necessary, a log can be written.
- For important evaluation by using if-else, do-while, for a log should be used.
- In a complex function, there are some checkpoints should be defined and for each
checkpoint, a log can be used for this notice. For example:
- Log writing can be used to monitor time for a certain task or process (in real-time
application), to monitor usage memory (in heavy load system), to evaluate very
important variables (in application/system which is very difficult to debug).
Le Hong Viet
Nguyen Quang Hoa Dang Van Thanh
Do Trung Hieu