MH-CRC IPL C Style Guide

April 3, 2001
 
 
 
 

C Style and Coding Standards

for the Iowa MC-CRC Image Processing Lab

IPL Programming Projects



Purpose
 
 

The purpose of this project is to define a uniform style for the all programming projects within the Iowa MC-CRC image processing laboratory. There is no claim that this document is unique in any way. It was based primarily on the Indian Hills C Style and Coding Guide and the SDM C Coding Manual. The generation of a well defined coding style for the IPL imaging lab should benefit all programmers by defining a style that should help to eliminate common bugs, ease the reading of code, and improve the ability to maintain the code. By defining a common style for the IPL it is believed that this will help to facilitate large development projects, ease the burden of maintenance and increase the portability of the code. The goal of this guide is to define a style that is:

1) Consistent across programmers

2) Easy to read and understand upon inspection

3) Free of common error types

4) Maintainable by various programmers in the lab

3) Portable to various architectures

This guide covers code generation from file organization and naming conventions to the style of statements and operators. While this guide was initially developed for the BRAINS2 programming project, all IPL projects and programs should conform to the defined style. This should help facilitate the maintenance of all IPL programs.
 
 

File Organization
 
 

A file consists of several sections that should be separated by several blank lines. Although there is not set maximum length limit for a file it typically should not exceed 1000 lines. This however is not a hard limit, but it should be used as guideline.
 
 

File Naming Convention
 
 

File names are made up of a base name and an optional period and suffix. The first name should not exceed 32 characters and the suffix should not exceed 3 letters. These rules should apply to both program files and default file names produced by the program. The filename should reflect the main function in the file. For example if the function in correlateImages() is located in a file then the file should be called correlateImages.c. If the files holds the main function the filename can be called main.c or program.c where program reflects the executable file that is created when the program is compiled.

Files Names for the Program Files
C source files *.c
C header files *.h
X Pixmap files *.xpm
Bitmaps *.bpm
Makefile Makefile
Shared library *.so
Static library *.a
Yacc source files *.y
Lex source files *.l
Forms Internal files *.fd
Template files *.tpl
Validation Files *.val
Gentle Grammer Files *.g
Directory Summary README


Common IPL Files Names suffixes
Palletes *.pal
Images *.img, *.pic, *.ima, *.imf, *.raw
Headers *.hdr, *.aux, *.ipl
Picture Files *.tif, *.gif, *.rgb
Log Files *.log


 
 

Program Files
 
 

Standard: Program Prologue
 
 

A program file should contain a prologue that provides vital information about the function of the file. It should contain the MH-CRC Source file line, the copyright, the file name, the authors name, the date and time of the last revision, a synopsis of the files purpose, a list of all of the functions in the file, a list of global variables used in the file, and revision control information in the development and maintenance of the file. If a program file contains several functions, the functions should be related in some manner. The desired relation of functions is based on a "breadth-first" approach where functions with a similar level of abstraction are together. For example in the development of a graphical user interface (GUI), all of the callback functions for a particular dialog box probably would exist in the same file. The program file prologue shown below is located in BRAINS2 directory templates/cprologue.tpl.
 
 

/!\verbatim***************MH-CRC IPL********************************* 
* Iowa MH-CRC IPL C Source File 
* Copyright (C) 1998 Nancy C. Andreasen and Vincent A. Magnotta 
* \endverbatim 
* 
* \file filename 
* \author authors name 
* 
* \date ??/??/???? ??:?? 
* 
* \brief Tell general relationship of functions - GUI 
* Interface Callback functions 
* 
* \par Functions: 
* \arg Function Prototype-1 
* \arg Function Prototype-N 
* 
* \par Globals: 
* \arg Global Variable-1 - Short description 
* \arg Global Variable-N - Short description 
* 
* \par Revision History: 
* \verbatim 
* Name Date Comments 
* -------------------------------------------------------------------- 
* ??? ??/??/??? Fixed Bug 
* 
* 
* \endverbatim 
**********************************************************************/


 
 

Standard: Functional Prologue
 
 

Each function must also have a short prologue that tells what the function does, the arguments for the function, the type of output for the function, and any global variables used by the function. The function prologue shown below is located in the BRAINS2 directory called templates/funcprologue.tpl.
 
 

/*!\verbatim*********************************************************** 
* Function: function name() 
* 
* \endverbatim 
* 
* \author authors name 
* 
* \fn Function prototype () 
* \param param-1 - Description 
* \param param-N - Description 
* \return Return values 
* 
* \brief Tell general purpose of the function call 
* add any Refs that accompany the routine 
* 
* 
* \par Globals Used: 
* \arg Global_Variable1 - Description 
* 
**********************************************************************/

The general guideline is that any function should not exceed 2-3 pages. If the function is longer than this, then the design of the function should be questioned. This is especially true if portions of the code could be broken into sub-functions that are separate entities.
 
 

Guideline: Line Length
 
 

The length of any line should not exceed 255 characters. In general the length of a line should not exceed 132 characters which will eliminate problems when the program is printed. If a line is too long then it should be broken into separate pieces. The line length includes any white space used for indentation and any comments that may follow the code.
 
 

Standard: Program File Layout
 
 

Sections within the C-code should appear in this order:

Prologue 
/* 
* Include Files 
*/ 
Include Files 
/* 
* Defines 
*/ 
Defines in order of constant macros, function macros, typedfefs , and enums 
/* 
* Global Definitions 
*/ 
Global Data declarations in the order extern, non-static, and static 
Function Prologue - 1 
function - 1 
. 
. 
. 
Function Prologue - N 
function - N

This program layout template is laid out for the user in templates/c.tpl. This file should be used as the template for all programs.

Standard: Header Files
 
 

The header file must begin with a prologue that is similar to the program prologue with a couple of modifications. The header file prologue is shown below. This template is laid out in the file templates/h.tpl or templates/hprologue.tpl. This file should be used as the template for all header files.

/*!\verbatim******************MH-CRC IPL******************************* 
* Iowa MH-CRC IPL C Header File 
* Copyright (C) 1998 Nancy C. Andreasen and Vincent A. Magnotta 
* 
* \endverbatim 
* 
* \file filename 
* \author authors name 
* 
* \date ??/??/???? ??:?? 
* 
* \brief Tell general purpose of header variables and 
* macros 
* 
* \par Required Headers: 
* \arg hdr_name.h - comment about why header is needed 
* 
* \par Revision History: 
* \verbatim 
* Name Date Comments 
* -------------------------------------------------------------------- 
* ??? ??/??/??? Fixed Bug 
* 
* 
* \endverbatim 
**********************************************************************/

 
 

Guideline: Header File Organization
 
 

a. All functions in a given header file should be related to the same general function, i.e. declarations for separate sub-systems should be in separate header files. For example, all functions in the header file stdio.h either perform or assist in the performance of input and output.

b. Within a header file, functions that perform related tasks should be grouped in the same section. For example within the file stdio.h, all functions in the scanf family are placed together.

c. A set of declarations that is likely to change when the code is ported from one machine to another should be in a separate file.
 
 

Guideline: Header File-Naming
 
 

a. Avoid private header filenames that are the same as public header filenames.

b. Don't use absolute pathnames for header files. Use the <name> construction for getting them from a standard place.

c. The "include-path" option of the C compiler (-I on many systems) used in the Makefile is the best way to handle extensive private libraries of header files; it permits reorganizing the directory structure without having to alter source files.
 
 

Standard: Header File Inclusion in the File That Defines the Function
 
 

a. Header files that declare functions or external variables must be included in the file that defines the function or variable. That way, the compiler can do type checking and the external declaration will always agree with the definition.

b. Put code like the following into each .h file to prevent accidental double-inclusion.

#ifndef EXAMPLE_H 
#define EXAMPLE_H 
... /* body of example.h file */ 
#endif /* EXAMPLE_H */ 

 
 

Guideline: Do Not Nest Header Files
 
 

Header files should not be nested. The prologue for a header file should, therefore, describe what other headers need to be included for the header to be functional. In extreme cases, where a large number of header files are to be included in several different source files, it is acceptable to put all common include statements in one include file.
 
 

Guideline: README Files
 
 

It is conventional to have a file called README to document both the big picture

and issues for the program as whole. For this it is common to include a list of all files in the directory with a list of functions contained in the files with the proper prototype. All compilation flags with definitions should be documented along with a list of all machine dependent files.
 
 

Comments
 
 

The comments should describe what is happening, how it is being done, and what parameters mean. Avoid comments that are clear from the code. Comments that disagree with the code are of negative value.

Comments should justify any offensive code.

Comments that describe data structures, algorithms, etc. should be in block comment form with the opening /* in columns 1-2 a * in column 2 before each line of comment text, and the closing */ in columns 2-3. An alternative form is to have ** in columns 1-2 and the closing */ in columns 1-2.

/* 
* Here is a comment block 
* The comment text should be tabbed or spaced uniformly 
* The open and closing are alone on a line 
*/ 
/* 
** Alternative form for comment Block 
*/ 

Note that grep '^.\e*' will catch all of the block comments in the file.

Block comments inside the code are appropriate and they should be tabbed over to the same setting as the code they describe. One-line comments alone on a line should be indented to the tab setting of the code that follows:

if (argc > 1) { 
/* Get the input file from the command line */ 
if (freopen(argv[1],"r",stdin) == NULL) { 
perror(argv[1]); 
} 
} 

Very short comments may appear on the same line of the code they describe, and should be tabbed over to separate them from the statements. If more than one comment appears in a block of code they should all be tabbed to the same tab setting.
 
 

Variable Declarations
 
 

Standards: Variable Naming
 
 

a. All variable names must begin with a lowercase letter.

b. The words of a compound variable should be separated by capitalizing the first letter of every word except the first word, e.g. pixelSize. In some cases where it is clearer, variables may be named by separating words with underscores, e.g. pixel_size.

c. The names of pointers should begin with the prefix 'ptr' or 'ptr_', e.g. ptrMyObject or ptr_myObject.

d. Global variables, if used, should have names that begin with the prefix 'gbl' or 'gbl_', e.g. gblPixelScale or gbl_pixelScale.

e. Variable names should be meaningful and should avoid abbreviations except where extraordinarily long file names would occur. This help to improve readability of the code a later date or by another programmer who does not understand your abbreviations.

f. Defined names must be all upper case separated by underscores, e.g.

#define WEEK_DAYS 7

g. In the BRAINS2 project no assumption about the size of variables will be made. This should increase the portability of the code possibly in the future. A header file called b2Types.h has been created which contains the following data types that will be guaranteed to be the desired size for the compilation platform:

Type Size Min Max 
S8 8 bit S8_MIN S8_MAX 
U8 8 bit U8_MIN U8_MAX 
S16 16 bit S16_MIN S16_MAX 
U16 16 bit U16_MIN U16_MAX 
S32 32 bit S32_MIN S32_MAX 
U32 32 bit U32_MIN U32_MAX 
S64 64 bit S64_MIN S64_MAX 
U64 64 bit U64_MIN U64_MAX 
F32 32 bit float F32_MIN F32_MAX 
F64 64 bit float F64_MIN F64_MAX 
C32 2x32 bit float F32_MIN F32_MAX 
C64 2x64 bit float F64_MIN F64_MAX

 
 

Guideline: Do Not Use Global Variables
 
 

The use of global variables is strongly discouraged. It is conceivable, however, that there will be cases where the use of global variables can actually make a program more readable by not cluttering function calls. Any use of global variables by a function should be documented in the prologue. Note: often the use of global variable may make a function much less flexible in the long run.
 
 

Standard: Global Variables Declaration at Top of File
 
 

Any global variables must be declared at the top of a file, before any function declarations. Variables which are global to only the functions in a single file should be declared as "static". The ordering of global variables in the file should be extern, non-static, and static. This is shown in section 2.2.4.

Global declarations should begin in column 1. All external data declaration should be preceded by the extern keyword. If an external variable is an array that is defined with an explicit size, then the array bounds must be repeated in the extern declaration unless the size is always encoded in the array (e.g., a read-only character array that is always null-terminated). Repeated size declarations are particularly beneficial to someone picking up code written by another.

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 sufficient documentation. The names, values, and comments are usually tabbed so that they line up underneath each other. Use the tab character rather than blanks (spaces). For structure and union template declarations, each element should be alone on a line with a comment describing it. The opening brace ({) should be on the same line as the structure tag, and the closing brace (}) should be in column 1.

struct BOAT { 
int wllength; /* water line length in meters*/ 
int type; /* see below */ 
long sailarea; /* sail area in square mm */ 
}; 
/* defines for boat.type */ 
# define KETCH (1) 
# define YAWL (2) 
# define SLOOP (3) 
# define SQRIG (4) 
# define MOTOR (5)

These defines are sometimes put right after the declaration of type, within the struct declaration, with enough tabs after the # to indent define one level more than the structure member declarations. 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'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. Use capital letters; for example, "2l" (two long) looks a lot like "21" (the number twenty-one):

int x = 1; 
char *msg = "message"; 
struct BOAT winner[] = { 
{ 40, YAWL, 6000000L }, 
{ 28, MOTOR, 0L }, 
{ 0 }, 
};

 
 

Standard: Local Variable Declaration at Beginning of Function
 
 

a. Variables within a function must be declared at the beginning of the function.

b. C does permit the declaration of variables within any block, e.g., within a "for" block, but this practice will not be allowed in IPL programs

c. 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.

Standards: Use #DEFINE for Symbolic Constants
 
 

a. All quantities that must remain unchanged throughout a program must be named using the "#define" capability. The defined name must be in upper case letters. For example:

#define MAX_CHANNELS 4096 

b. Symbolic constants that are used only in a single compiland should be defined at the beginning of that compiland. Symbolic constants that have a more general scope should be defined and documented in header files of appropriate scope.
 
 

Guideline: Variable Initialization in Declaration
 
 

a. Static variables in a function that are initialized in the declaration are set once before the beginning of execution and maintain their assigned value whenever the function is called. If not initialized in the declaration, static variables are defined to be zero initially.

b. Automatic variables in a function that are initialized in the declaration are set to the same value whenever the function is called. If not initialized in the declaration, automatic variables are undefined and probably garbage.
 
 

Standard: Explicit +1 in String Length Declaration for \n
 
 

Character arrays used as strings, i.e., to hold ASCII text and terminated by a null character) should have a defined length that explicitly includes the "+ 1" character for the null string terminator.

#define NAME_LEN 20 + 1 
char name[NAME_LEN]; 

 

Standards: Structure Declaration
 
 

a. Each field in a structure must be declared on a separate line.

b. The structure must have a tag, in upper case.

c. The actual assignment of a structure to a variable must be done in a separate statement.

struct BOOK { 
char name[NAME_LEN]; 
char author[AUTHOR_LEN]; 
long number; 
}; 
struct BOOK goneWithTheWind; 

 
 

Standard: Use Conventional Prefixes for Variables
 
 

Variable Function Variable Prefix Abbreviation
average avg
database db
length len
message msg
number num
pointer ptr
position pos
string str
object obj
form fd
summation sum

 
 

Guideline: Brains2 Function naming convention
 
 

For the BRAINS2 project standard function call naming convention exists to allow easy inspection of what the function operates on. This should help to improve the readability and maintainability of the code. Shown below is a table of the function prefixes that are preferred for the BRAINS2 project

Function Type Function Prefix
BRAINS2 Object Functions ob
Queue Function Calls que
BRAINS Kernel Calls bk
Standard Image Lab Routines ipl
Private Object Functions pv

Guideline: Macros
 
 

a. Macro names should be all upper case characters.

b. Include a comment on the same line as macro declarations.

c. Place all macro definitions at the beginning of the file after the prologue, or in a header file.

d. Place all shared macros in a header file.

e. Place parentheses around the parameters in the replacement text and around the entire text whenever possible. For example:

#define NEXT(p) ((p)->_next)

Complex expressions can be used as macro parameters, and operator-precedence problems can arise unless all occurrences of parameters have parentheses around them. There is little that can be done about the problems caused by side effects in parameters except to avoid side effects in expressions (a good idea anyway) and, when possible, to write macros that evaluate their parameters exactly once. There are times when it is impossible to write macros that act exactly like functions.

Some macros also exist as functions (e.g., getc and fgetc). The macro should be used in implementing the function so that changes to the macro will be automatically reflected in the function. Care is needed when interchanging macros and functions since function parameters are passed by value, while macro parameters are passed by name substitution. Carefree use of macros requires that they be declared carefully.

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 at) or may be used as the left-hand 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:

#define OFF_A() (glb_a+OFFSET) 
#define BORK() (zork()) 
#define SP3() if (b) { int x; av = f (&x); bv += x; }

Macros save function call/return overhead, but when a macro gets long, the effect of the call/return becomes negligible, so a function should be used instead.

In some cases it is appropriate to make the compiler insure that a macro is terminated with a semicolon.

if (x==3) 
SP3(); 
else 
BORK();

If the semicolon is omitted after the call to SP3, then the else will (silently!) become associated with the if in the SP3 macro. With the semicolon, the else doesn't match any if ! The macro SP3 can be written safely as

#define SP3() \\\\ 
do { if (b) { int x; av = f (&x); bv += x; }} while (0)

Writing out the enclosing do-while by hand is awkward and some compilers and tools may complain that there is a constant in the while conditional. A macro for declaring statements may make programming easier:

#ifdef lint 
static int ZERO; 
#else 
# define ZERO 0 
#endif 
#define STMT( stuff ) do { stuff } while (ZERO)

Declare SP3 with

#define SP3() \\\\ 
STMT( if (b) { int x; av = f (&x); bv += x; } )

Using STMT will help prevent small typos from silently changing programs.

Except for type casts, sizeof, and hacks such as the above, macros should contain keywords only if the entire macro is surrounded by braces.
 
 

Functions
 
 

Function Declarations
 
 

Standards: Return Values Must Be Explicitly Declared
 
 

a. Return values must be explicitly declared.

b. The function declaration should return void if an actual value is not being returned, and there is no possible way for the function to fail.
 
 

Standards: Parameter List
 
 

a. If the function and its parameter list is longer than one line, lines after the first one will be indented from the left margin so that the second line of the parameter list starts directly below where the parameter list begins on the first line.

b. The list of function parameters should have a definite order. The standard for the C library functions is opposite from most other languages and from most programmer's intuition. For example, the string copy function, strcpy(out, in), takes the output parameter first and then the input parameter. This makes sense if you think of the ordering like you think of the ordering of an assignment statement. We can't change the C library standard but for routines which we write, we will use the more conventional parameter ordering of input, modified (input and output), and finally output parameters.

c. A function that returns information via one or more of its parameters may return only status information in its name.

d. For the BRAINS2 project if a function is returning a value giving only the status of the call then it should be of type STATUS with the two following return values SUCCESS and FAILURE.

e. Be careful when you use or declare functions that take a variable number of arguments ("varargs"). There is no truly portable way to do varargs in C. Better to design an interface that uses a fixed number of arguments. If you must have varargs, use the library macros for declaring functions with variant argument lists.

f. If the function uses any external variables (or functions) that are not declared globally in the file, these should have their own declarations in the function body using the extern keyword.

g. Avoid local declarations that override declarations at higher levels. In particular, local variables should not be redeclared in nested blocks. Although this is valid C, it is potentially very confusing in debugging.
 
 

Standard: Static Function
 
 

Any function which is only called from other functions in the same file should be declared "static".
 
 

Template for Function Declarations
 
 

void func_name (int firstParam,int secondParam, int thirdParam) 
{ 
int i; /* Loop variable */ 
int sum; /* Loop sum */ 
sum = 0; 
for (i=0;i < firstParameter;i++) 
{ 
sum += secondParameter; 
} 
printf("%d %d %d %d\n", firstParam, secondParam, 
thirdParam, sum); 
return; 
}

 
 

Standards: Function Body
 
 

a. The function body, until the closing brace will be indented one step (three or four spaces - do not use tabs). A beginning brace, "{", opening the body of the function must be on a line by itself and left justified or at the end of the line which introduces the block. course, any control statements will cause further indentation from this basic indentation.

b. A return statement must always be present with a parameter if the function is not of type void. Even if the function has no return value and, therefore the C language does not require a return, it is good practice to use one. This can be useful for setting a break point during debugging.

c. A closing brace, "}", closing the body of the function must be on a line by itself and left justified.
 
 

Standards: Function Prototype
 
 

a. Function prototypes must be generated for all functions generated.

b. If the function prototypes reside in a separate file, that file should have the extension of ".h".
 
 

Statements
 
 

Guideline: One Statement Per Line
 
 

There should be only one statement per line unless the statements are very closely related. For example,

if (fail == 1) {print_error(); return FAILURE}

 
 

Guideline: Do Not Use Goto and Continue Statements
 
 

"Goto" and "continue" statements are highly discouraged.
 
 

Guideline: Exit Statement Discouraged
 
 

Explicit use of the "exit();" statement is not allowed, except for explicitly predefined functions whose purpose are program termination.
 
 

Guideline: Maximum 4 Levels of Control Structure Nesting
 
 

Nesting of statements, "if", "for", "while", etc., should go no more than 4 levels. If more levels appear to be needed, consider use of a function at one of the higher levels. This guideline should be followed for most functions, however there may be several cases, especially with images, where more nesting is required. In these cases, nesting of the control structure is allowed to be greater than 4 levels.
 
 

Standards: Null Statements
 
 

a. Null statements must include a comment line. For example, if the "default:" case in a switch statement does nothing, put in a "default:" label followed by a comment to the effect: /* no action*/ followed by the break statement.

b. 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 (*dest++ = *src++) 
; /* VOID */

 
 

Standard: Comment for Missing "break" in switch
 
 

If a particular case in a switch statement is meant to drop through to the next case (i.e., it has the same effect), the fact that the earlier case has no "break" statement should be explicitly noted with a comment:

switch(whichIsotope) { 
case PU239 : /* same action as for PU240; no break */ 
case PU240 : 
processPlutoniumIsotope(); 
break; 
default : 
break; /* if not PU 239 or 240, no action taken */ 
}

 
 

Standards: Statement Blocks
 
 

a. Statements that affect a block of code (i.e., more than one statement) must either have the opening brace "{" at the end of the line containing the control statement, or the opening brace must be on the line immediately below and lined up with the first letter of the control statement.

b. The body of the block must be indented one step from the control statement.

c. The ending brace, "}", must be on a line by itself and at the same indentation level as the control statement.

d. Examples:

if (first < last) { 
resultOne = first / 2; 
resultTwo = last / 2; 
} 
else { 
resultOne = last / 2; 
resultTwo = first / 2; 
} 
do 
{ 
status = doSomething(); 
} while (status == SUCCESS);

 
 

Guideline: "block" with Single Statement
 
 

The programmer is encouraged to block off even a single statement following a "while", "if", "else", etc. Using the curly braces "{}" is required only when there is a block of more than one statement. However, putting in the braces makes the scope of the control statement very clear and helps to protect the code in the event that a second line is added to the block if the single line contains a macro which translates into more than one line of code.

if (someCondition == TRUE) { 
thisVariable = thatVariable; 
}

 
 

Standard : Increment and Decrement Operator Only as Postfix
 
 

The increment and decrement operators, "++", and "--" are permitted only in post-fix notation, e.g. "i++", and not as part of any statement. For example, the code block

while (string[i++] != NULL) { 
doSomething(); 
}

is not allowed; the preferred style is

while (string[i] != NULL) { 
doSomething(); 
i++ 
} 


Standard: Branching on Function Call
 
 

Often a program will branch based on the success or failure of a function call. It is clearer to break out the function call onto a separate line followed by a new line containing the conditional statement:

ptr_FileHandle = fopen("some_file", READ_ONLY); 
if (ptr_FileHandle == NULL) { 
printf("Could not open file; program terminating."); 
terminateApplication(); 
} 
else { 
doSomething(); 
} 
is easier to understand than: 
if ((fileHandle = open("some_file", READ_ONLY))== NULL) { 
printf("Could not open file; program terminating."); 
terminateApplication(); 
} 
else { 
doSomething(); 
}

The "fopen" example above demonstrates a style which helps to avoid internal side effects. "Side effect" as used in this example refers to the fact that the reader may focus on the "if(xxx == NULL)" aspect of the statement and not fully realize that there is a call to "fopen()". The other danger of this type of compound statement is the potential for completely changing the meaning if the parentheses around the "fileHandle = open()" part are left off.
 
 

Standard: Avoid Internal Side Effects
 
 

Expressions may not produce any internal side effects. For example the expression

while(string[i++]) != 0)

should not be used because C's order of evaluation is explicitly undefined.

Standard: Do Not Use Default Truth Value Test
 
 

Do not default the test for non-zero. For example, this

check = f(); 
if (check != FAIL) 

is better than this

check = f(); 
if (check)

 
 

Operators
 
 

Standard: Use Single Spaces Around Binary Operator
 
 

All operators which take two parameters must have a single space on either side of the operator. This makes it very handy to use an editor to search for a variable assignment; you need only search for "a =" rather than both "a =" and "a=". It also makes the code more readable.
 
 

Standard: No Space Following Unary Operator
 
 

In contrast to binary operators, all unary operators (e.g., a minus sign or the address operator, "&") should not have a space between the operator and the object.
 
 

Guideline: Conditional Operator
 
 

The use of the ternary conditional operator, "?:" should be used in functions only in cases where it makes the code more readable. If it makes the meaning less clear, then its use is discouraged. Use of the ternary conditional operator is acceptable in macros.
 
 

Standard: Use Parentheses to Remove Precedence Ambiguity
 
 

Where operator precedence must be known to determine the meaning of an expression, it is required that you use parentheses to eliminate any ambiguity which might arise from lack of knowledge of operator precedence. For example, to increment the variable pointed to by the pointer "ptr_NumTimes", use "(*ptr_NumTimes)++". This use of parentheses makes it clear that the contents of location "ptr_NumTimes" is being incremented and not the address itself.
 
 

White Space
 
 

Guideline: Use of Whitespace for Readability
 
 

Use vertical and horizontal whitespace judiciously to make the program more readable. Indentation and spacing should reflect the block structure of the code.
 
 

Standard: Vertical Spacing of Conditional Operators on Separate Lines
 
 

A long string of conditional operators should be split onto separate lines.

if (foo-<next==NULL && totalCount < needed 
&& needed<=MAXLLOT && serverActive(currentInput)) { ...

will be better as

if (foo-<next == NULL 
&& totalCount<needed 
&& needed<= MAXLLOT 
&& serverActive(current -input)) 
{ 
...

Similarly, elaborate "for" loops should be split onto different lines.

for (curr = *listp, trail = pList; 
curr != NULL; 
trail = &(curr->next), curr = curr->next) { 
doSomething(); 
doAnotherSomething(); 
} 

 
 

Standard: Spacing for Parentheses
 
 

Keywords that are followed by expressions in parentheses should not be separated from the left parenthesis. Also put blanks after commas in argument lists to help separate the arguments visually.
 
 

Constants
 
 

Standard: Naming Symbolic Constants in Upper Case
 
 

Symbolic constants must be in upper case. e.g., "TRUE".
 
 

Guideline: Consistency of Constant Definitions
 
 

Constants should be defined consistently with their use; e.g. use 540.0 for a double instead of 540 with an implicit float cast.
 
 

Standard: Conventional Constants
 
 

A conventional set of symbolic constants will be used for constants common to C coding:
 
 

TRUE anything but zero, the value 1 will be used
FALSE zero
LF_CHAR line feed character (10)
CR_CHAR carriage return character (13)
EOS end of string character ('\0')
EOL end of line character ( )
EOF end of file
FAILURE error returned from function with type STATUS
SUCCESS successful completion of a function of type STATUS


 
 

Conditional Compilation
 
 

Guideline: Use of Conditional Compilation
 
 

Conditional compilation should only be used for controlling the compilation of machine-dependent code, setting optimizations at compile time, and debugging.
 
 

Standard: Default of Conditional Compilation
 
 

If a conditional compiland is used to control machine dependencies code, then the behavior when no machine is specified should result in an error and not a default machine. The default for a conditional compiland meant to allow for code optimization should be un-optimized code.
 
 

Guidelines: Conditional Compilation
 
 

a. Whenever possible, put conditionally compiled code in a header file instead of in the main program.

b. Conditional compilands should be bracketed on a feature-by-feature basis.

c. Debugging statements in the code that are left in for future debugging or addition of features should be set of by the conditional block

#ifdef DEBUG 
fprintf(stderr,"Debugging line\n"); 
#endif

This will allow for easy debugging in the future just by defining the DEBUG macro.
 
 

Portability
 
 

Standards: Machine-Dependent Code in Separate File
 
 

a. Place all machine-dependent code in a separate file from all machine-independent code.

b. Machine-dependent code must be "#ifdef'ed" so that an informative error message will result if the code is compiled on a machine other that which it is designed for.
 
 

Guideline: Machine-Dependent Code Use
 
 

a. Only write machine-dependent code when necessary. Even if, for example, a particular piece of hardware requires that a machine-dependent routine be written, try to write any routines that support the machine-dependent code machine-independently.

b. Recognize that some things are inherently non-portable. Examples are code to deal with particular hardware registers such as the program status word, and code that is designed to support a particular piece of hardware, such as an assembler or I/O driver. Even in these cases there are many routines and data organizations that can be made machine independent

c. Organize source files so that the machine-independent code and the machine-dependent code are in separate files. Then if the program is to be moved to a new machine, it is a much easier task to determine what needs to be changed. Comment the machine dependence in the headers of the appropriate files.
 
 

General Guidelines: Portability
 
 

a. Try not to assume ANSI. Even though ANSI-compliant code should be written, if you know that many non-ANSI compilers will not understand some code and you know of a more portable way of writing the code which is still ANSI-compatible, do so.

b. Be aware that the size of different data types may vary from platform to platform. Be especially careful to avoid making assumptions about integers and pointers.

c. Also be aware that the precision and storage format of floating point numbers may vary from platform to platform.

d. Do not assume that software will always be executed on the machine for which it is originally designed.

e. 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.

f. On ANSI compilers, when two pointers of the same type access the same storage, they will compare as equal. When non-zero integer constants are cast to pointer types, they may become identical to other pointers. On non-ANSI compilers, pointers that access the same storage may compare as different. The following two pointers, for instance, may or may not compare equal, and they may or may not access the same storage.

((int *) 2 ) 
((int *) 3 )

If you need `magic' pointers other than NULL, either allocate some storage or treat the pointer as a machine dependence.

extern int x_int_dummy; /* in x.c */ 
#define X_FAIL (NULL) 
#define X_BUSY (&x_int_dummy) 
#define X_FAIL (NULL) 
#define X_BUSY MD_PTR1 /* MD_PTR1 from "machdep.h" */ 

 
 

g. 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.

h. Data alignment is also important. For instance, on various machines a 4-byte integer may start at any address, start only at an even address, or start 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. Indeed, a structure of a 32-bit pointer and an 8-bit character may be 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 it very rash 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.

i. There may be unused holes in structures. Suspect unions used for type cheating. Specifically, a value should not be stored as one type and retrieved as another. An explicit tag field for unions may be useful.

j. 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.

k. Do not make assumptions about the parameter passing mechanism especially pointer sizes and parameter evaluation order, size, etc. The following code, for instance, is very nonportable.

c = foo (getchar(), getchar()); 
char 
foo (c1, c2, c3) 
char c1, c2, c3; 
{ 
char bar = *(&c1 + 1); 
return (bar); /* often won't return c2 */ 
} 

This example has lots of problems. The stack may grow up or down (indeed, there need 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. One compiler may use several (incompatible) calling conventions.

l. 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.

m. Do not modify string constants. One particularly notorious (bad) example is

s = "/dev/tty??"; 
strcpy (&s[8], ttychars); 

n. 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 program. If the address is used in a comparison, sometimes the program 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.

o. 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.

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

q. Side effects within expressions can result in code whose semantics are compiler-dependent, since C's order of evaluation is explicitly undefined in most places. Notorious examples include the following.

a[i] = b[i++]; 

In the above example, we know only that the subscript into b has not been incremented. The index into a could be the value of i either before or after the increment.

struct bar_t { struct bar_t *next; } bar; 
bar->next = bar = tmp; 

In the second example, the address of bar->next may be computed before the value is assigned to bar.

bar = bar->next = tmp; 

In the third example, bar can be assigned before bar->next. Although this appears to violate the rule that ``assignment proceeds right-to-left,'' it is a legal interpretation. Consider the following example:

long i; 
short a[N]; 
i = old 
i = a[i] = new; 

The value that i is assigned must be a value that is typed as if assignment proceeded right-to-left. However, i may be assigned the value ``(long)(short)new'' before a[i] is assigned to. Compilers do differ.

r. Avoid preprocessor tricks. Tricks such as using /**/ for token pasting and macros that rely on argument string expansion will break reliably.

#define FOO(string) (printf("string = %s",(string))) 
... 
FOO(filename); 

Will only sometimes be expanded to

(printf("filename = %s",(filename))) 

Be aware, however, that tricky preprocessors may cause macros to break accidentally on some machines. Consider the following two versions of a macro.

#define LOOKUP(chr) (a['c'+(chr)]) /* Works as intended. */ 

#define LOOKUP(c) (a['c'+(c)]) /* Sometimes breaks. */  

The second version of LOOKUP can be expanded in two different ways and will cause code to break mysteriously.

s. 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 unportable.) 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 whether you're doing something special in that reimplemented stuff to justify its existence. It also prevents your program from taking advantage of any microcode assists or other means of improving performance of system routines. Furthermore, it's a fruitful source of bugs. If possible, be aware of the differences between the common libraries (such as ANSI, POSIX, and so on).

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

ANSI C
 
 

Standard: ANSI-Compatible Code
 
 

All code should conform to the standard established by ANSI.
 
 

Guideline: Compiler
 
 

All C code should be compiled and tested with the both the cc and gcc compilers whenever possible.
 
 

Standard: POSIX
 
 

Make standard POSIX compatible functional calls whenever possible. Non-standard machine specific functional calls should be commented and stated why they are being used.
 
 

Miscellaneous
 
 

Standard: Always Check Function Return Value
 
 

Always check the return values of functions which return a special value when an error occurs. If there really were no chance of an error occurring, then the function would not have such a return value.
 
 

Guideline: Use Library Functions When Possible
 
 

Use functions in libraries whenever possible instead of re-inventing the wheel.
 
 

Guideline: Lint
 
 

Lint is a C program checker that examines C source files to detect and report type incompatibilities, inconsistencies between function definitions and calls, potential program bugs, etc. The use of lint on all programs is strongly recommended, and it is expected that most projects will require programs to use lint as part of the official acceptance procedure.

It should be noted that the best way to use lint is not as a barrier that must be overcome before official acceptance of a program, but rather as a tool to use during and after changes or additions to the code. Lint can find obscure bugs and insure portability before problems occur. Many messages from lint really do indicate something wrong. One fun story is about is about a program that was missing an argument to "fprintf:"

fprintf ("Usage: foo -bar <file>\n");

The author never had a problem. But the program dumped core every time an ordinary user made a mistake on the command line. Many versions of lint will catch this.

Most options are worth learning. Some options may complain about legitimate things, but they will also pick up many botches. Note that `--p' checks function-call type-consistency for only a subset of library routines, so programs should be linted both with and without --p for the best ``coverage''.

Lint also recognizes several special comments in the code. These comments both shut up lint when the code otherwise makes it complain, and also document special code.
 
 

Guideline: IPL module Certification Procedure
 
 

The IPL module certification procedure is explain in the "IPL Module Certification Procedure" document. The basis of the document explains what steps are nessecary to have your module certified by the IPL which will then allow it to become part of the BRAINS2 software in the future. It is strongly urged that any module which you feel could be used by another individual in the future be certified. This is especially true for any software which is or may be for standard workups.
 
 

Guideline: CVS
 
 

CVS is a GNU utility that facilitates the ability to track changes and revisions in source code. While the IPL does not require that the CVS be used during initial development and testing of a module especially in experimental code, any module that becomes certified through the IPL module certification procedure should then be added to the CVS archive to allow maintenance and tracking of edits on the certified module. While this manual will not go into the use of CVS a future document will be written to explain CVS and the IPL's use of the program.
 
 

Special Considerations
 
 

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

a. Don't change syntax via macro substitution. It makes the program unintelligible to all but the perpetrator.

b. Don't use floating-point variables where discrete values are needed. Using a float for a loop counter is a great way to shoot yourself in the foot. Always test floating-point numbers as <= or >=, never use an exact comparison (== or !=).

c. Compilers have bugs. Common trouble spots include structure assignment and bitfields. You cannot generally predict which bugs a compiler has. You could write a program that avoids all constructs that are known broken on all compilers. You won't 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.

d. Do not rely on automatic beautifiers. The main person who benefits from good program style is the programmer him/herself, and especially in the early design of handwritten algorithms or pseudo-code. Automatic beautifiers can only be applied to complete, syntactically correct programs and hence are not available when the need for attention to white space and indentation is greatest. 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.

e. Accidental omission of the second = of the logical compare is a problem. Use explicit tests. Avoid assignment with implicit test.

abool = bbool; 
if (abool) { ... 

When embedded assignment is used, make the test explicit so that it doesn't get ``fixed'' later.

while ((abool = bbool) != FALSE) { ... 
while (abool = bbool) { ... /* VALUSED */ 
while (abool = bbool, abool) { ...

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

g. Modern compilers will put variables in registers automatically. Use the register sparingly to indicate the variables that you think are most critical. In extreme cases, mark the 2-4 most critical values as register and mark the rest as REGISTER. The latter can be #defined to register on those machines with many registers.
 
 

Conclusion
 
 

The standards and guidelines laid forth in this document should be followed zealously and in good faith (do not, for example, ignore guidelines just because they do not say must). Remember above all that you are working on a team of programmers, and should therefore labor to make your code as easy for another to follow as possible in case another person has to modify your program. It is not unheard of for even the person who writes sloppy code to not be able to follow it after a significant amount of time has passed. Finally the style laid out in this document must be followed in order to certify the module into the BRAINS2 software (see BRAINS2 Module Certification Document).
 
 

References
 
 

1.Spencer, Keppel, and Brader, Recommended C Style and Coding Standards, Revision 6.0, June 25, 1990.

2.The Boulder Software Group, Example C Style Guidelines, 1990.

3. Cannon LL, Elliot RA, Kirchhoff LW, Miller JH, Milner JM, Mitze RW, Schan EP, Wittington NO, et al. Recommended C Style and Coding Standards: Update of the Indian Hills C Style and Coding Standards

4. Los Alamos National Labratory, SDM Style and Coding Standards for the SDM Project, 1996.