Search This Blog

Thursday, March 18, 2010

C Coding Standards : Part 3 (The Last Part)

//--------------------------------------------
// Creation Date -- 18th March, 2010
// Last Revision -- 14th April, 2010
//-------------------------------------------

5. Comments:

5.1 File Headers:
Every file--both header and source files--should begin with this standard banner comment:
//-----------------------------------------------------

// File: foo.cpp (or .h)

//

// Desc: This is the description of the file.

// Long descriptions should be

// justified/indented like this.
//

// Any notes or multiple paragraphs are

// formatted like this.

//

// 11/2/2000 - subhab – Started this file

// 12/8/2000 – subhab – Fixed bug 12345

// in GetName() method

//

// Copyright (c) 2009-2010, SUBHA Pvt. Ltd. All rights

// reserved.

//------------------------------------------------------

5.2 Function and Method Headers:
Any function with non-obvious functionality, complex behavior, or a large list of input parameters should use a comment like the one below. It is optional for all other methods.

//--------------------------------------------------
// Name: foo()
// Desc: This method does nothing but return TRUE.
// It's just here to show what
// a function header should look like.
// Parameters:
// Parameter1 String used as input
// Parameter2 String used as output
//--------------------------------------------------

5.3 Structure Headers:
It is strongly recommended that all non-trivial structures have a header such as the following:

//--------------------------------------------
// Name: structure stNothing
// Desc: This structure does nothing.
// It's just here to show what
// a structure header should look like.
//--------------------------------------------

5.4 Section Dividers:
Each major section of code should be divided by a header such as:

//-------------------------------------------
// Global data
//-------------------------------------------

5.5 Source Comments:
The use of the // comment format is preferred over the /*…*/ format.

5.5.1 Inline Code Comments:
Inline comments should be included on their own line and should be indented at the same level as the code they are commenting on.

For example:

if (NULL == pFoo)
{
// The pointer is null, we cannot continue.
return E_POINTER;
}

Inline comments are permissible on the same line as the actual code only when giving a brief description of a structure member variable, or parameter. In this case it is a good idea to align the comments for all variables.

For example:

struct stFoo
{
int m_iCount;
// used to store the reference count
DWORD m_dwSeed; // store the random seed value
};

5.5.2 Commenting Out Code:

It is preferable to use #if 0 over /*…*/ to comment out code you don’t want compiled. The first style can be safely nested whereas the second cannot. Test against ‘0’ instead of an undefined value because someone might just define that value.

#if 0
// correct, always works

#ifdef NOTDEF
// bad – might fail


6. Programming Considerations:

6.1 Performance Considerations:
The following are merely recommendations but ones that will serve you well if you follow them.

6.1.1 Branch Prediction:
Code your if statements so that the most likely case is the true evaluation. The Pentium processor reads the instructions ahead and must flush a lot of work to branch around the true case. Intel’s documentation can provide more information on this subject.

6.1.2 Postpone Local Variable Definitions:
Define a variable right before you use it, not at the top of the block. If you happen to return before getting to where the variable is used, you’ll save a constructor and a destructor call. It may also ease stack use, lessen register pressure, etc.
The exception is when you have an object with a constructor inside a loop. In this case, it may be advantageous to declare the object right before the loop so that it does not have to be constructed and destructed each loop iteration.

6.1.3 Avoid Header File Bloat:
The unnecessary inclusion of header files can cause compilation times to increase, sometimes by a factor of 2 or 3. You can avoid header bloat by forward declaring classes and structs rather than including the header file where they are defined.
It can also be beneficial to use precompiled headers if you have a lot of source files and they are all including the same headers (like stdio.h).

6.1.4 Data Locality and Alignment:
Define the order of structure/class members with the following principles in mind:
largest types first – a fetch of N bytes is fastest by far if the memory is at 0 == (addr % N). If you define some odd BYTE or short members first, the compiler must either add padding to these small members to get your later double members aligned, or it must generate multiple reads to get the doubles. Adhering to this principle, coincidentally, also gets you the smallest struct. Interestingly, same is true of local variables and parameters.

void MyFunc(double dbl, char ch) // Misaligned dbl!
{
if( ch > g_ch ) {
g_ch = ch;
}

if( dbl > g_dbl ) {
g_dbl = dbl;
}
}



// Same function executes 4-5% faster when dbl is aligned.
// _stdcall, _cdecl, _fastcall are all right-to-left ordered

void MyFunc( char ch, double dbl )
{ … }


Members that you access frequently or that you always access a short time from each other should be located near each other in your struct.. The cache lines are 32bytes wide and all memory is cached 32bytes at a time, so if your struct is larger than this, you want to avoid having to fill two cache lines if the members you need could have occupied one.
If you plan on having arrays of structs, consider adding deliberate padding at the end such that no element in the array will begin misaligned or will span a cache-line unnecessarily.

struct STWillMakeUpAnArray
{
double dblKey;
// bytes 0-3
float fltX; // 4-7
float fltY; // 8-11
short iZ; // bytes 12, 13 only!
BYTE pad[2]; // Without this, we misalign
// every odd element’s
// dwKey, fltX, and fltY members!
// Arr[3], and others, require 2
// cache-lines, hurting random access.
};

6.1.5 Temporaries and Copies:
Pass-by-Reference can be an easy solution to the problems with pass-by-value and return-by-value.

bool IsFontBold(int iCheck) // Don’t do this
{
return iCheck > gc_iThreshold;

}

inline bool IsFontBold(int &iCheck) // Good!
{
return iCheck > gc_iThreshold;
}


You can achieve the same with pass-by-pointer, the differences being pointers can be NULL, pointers can be reassigned (well, const BigFontClass * const pMyFont couldn’t…), pointers cause ambiguity about who owns the memory, while references have none of these problems. If you call IsFontBold( * pMyFont), you’d have better checked pMyFont for NULL before making the call!
Return-by-value does allow a clever compiler optimization for anonymous temporaries; the compiler has an excellent chance of constructing this object directly into the memory, whereas if a local variable were created first, there would be 2 copy-constructor(/operator=) invocations.


int MyFunc( int ia, int ib )
{
return (ia+ib);
}

int iSum = MyFunc( i1, i2 );


6.1.6 Size == Performance:
In the vast majority of code, the users’ perception of the performance of the code is determined by the binary size. Writing smaller code will improve load times, increase cache performance, etc.

6.2 Clean Coding Style:

6.2.1 Const and Volatile:

Use const liberally. If a variable will not be changed in a function, make it const. If a method will not be changing any member variables, make the function const. This will allow the compiler to catch potential errors which would otherwise be difficult to track down. Use const member functions if possible.


The purpose of volatile is to force an implementation to supress optimization that could otherwise occur.
For example, for a machine with memory-mapped input/output, a pointer to a device register might be declared as a pointer to volatile, in order to prevent the compiler from removing apparently redundant references through the pointer. Except that it should diagonose explicit attempts to change const objects, a compiler may ignore this qualifier.


6.2.2 Test for the Error Case, Not the Success Case:
Code is much more readable if blocks are kept to a minimum. One way to do this is to check for the error case up front rather than checking for the success case and entering a block if successful.


// Do Not Do This
bool MyFunc(int * pInt)
{
if (NULL != pInt)
{
CFoo pFoo = new CFoo();
if (NULL != pFoo)
{
//do something here
}
else
{
return false;
}
}
else
{
return false;
}


// instead, prefer this
bool MyFunc(int * pInt)
{
if (NULL == pInt)
{
return false;
}
CFoo pFoo = new CFoo();
if (NULL == pFoo)
{
return false;
}
// do something here
}

6.2.3 Limit Exit Points From Functions:
Having more than one exit point in a function or method which allocates memory or increments reference counts risks memory leaks. It is difficult to account for all of the various delete or release combinations required if the function exits early. Even if all of these cases are handled, maintenance of the code can be cumbersome at best and fraught with peril at worst. It is therefore recommended that you limit the number of exit points from a function.
Many people will use goto statements to accomplish this. This is not recommended. The use of goto wreaks havoc with the compiler’s ability to optimize code. It cannot be assured of the execution path and so foregoes much of its optimization


6.2.4 Functions Should Be Short:
As a general rule, functions should be able to fully fit onto the screen at one time. Longer functions are harder to maintain and harder to understand. Functions longer than approximately 30 lines should be examined to see if they can’t be broken up into a series of smaller functions. This isn’t a hard and fast rule, but will produce more maintainable code if it is followed.


4 comments:

  1. Cool one....though i've not been able to go through all of the articles thoroughly....but I am sure this trilogy will help the aspiring coders,as well as the veteran ones, to make their coding techniques even better.
    Awaiting for the next post eagerly...

    ReplyDelete
  2. Fatafati, অসাধারন. I appreciate your sense of belonging and love to the juniors and Department besides your busy life. Thanks.

    ReplyDelete
  3. as u mentioned about "const" i think u also need to tell the advantage of using "const volatile" in together.
    also the difference between using const and using volatile

    ReplyDelete
  4. Thanks for mentioning those points Anshu. I'll post on them ASAP.

    ReplyDelete