Style Guide

Introduction

This is my recommended C source code style guide. I use to refer to it simply as Rein's Styleguide. But don't follow it blindly. If there's any other style guide at your company to be used, I recommend you to do so as long as:

  1. Those who selected the style guide at your company really are competent to do so, and didn't just pick any style guide more or less by random.
  2. Your style guide is really motivated. I mean, there should really be motivations to any of the recommendations it contains, so you can argue for or against each of its statements.

Guide

Section: Project Name

Before doing anything, make sure you know the name of the project! Not just because you'll have to enter it in the source code header, you also need to know how to refer to the project in emails, technical documentation and so on. And it must be possible to search for the project using free text search!

Section: Comments

  1. Each and every source file should begin with a comment block. This should make sure the programmer always knows which file he/she edits, and where it belongs.
  2. Sections of code that handles a specific tasks should be preceeded by two blanks, a comment about the following section and then another blank line.
  3. C++ style comments // should be used to comment variables, constants etc., when the declaration and the comment is on the same line. This is to save space.
  4. No attempt should be made to draw boxes with right edges because it is a waste of time trying to maintain these boxes, or if they are not maintained, it will look ugly.
  5. Constants and variables should be commented normally, but should not be commented when their purposes are obvious.
  6. All function arguments should be commented with purpose, valid range and any exceptions.
  7. Functions should be preceeded by a comment stating its purpose, parameters and return value.
  8. The main source file should contain a comment header stating project, author(s), creation date, synopsis, exit value, purpose and if necessary, copyright statement.
  9. The purpose-comment for a file or function should be limited to maximum 100 characters. If this isn't enough to explain the purpose, you haven't structured the code properly, you try to solve too many different problems at once! Go back to the planning state and split up the code in more logical seqments, each having a well defined purpose.
  10. Long code blocks (including functions) should be ended with an end comment, to make orientation easier.
  11. Comments shall be written in English.
  12. Comments shall be spell checked.
  13. Just repeating a variable name with a comment which is the same name as the variable is completely stupid and should be punished! The purpose of the comment is to explain in other words what the variable does!
  14. If a revision control system is used, the leading comment block in each source file should contain keywords from the revision control system, not be edited manually.
  15. Optionally, a source file header block could declare version numbers of tools (compiler, linker, OS etc.), source machine, target machine and compiler directions. But if this information is available in the technical documentation, makefile etc., it should not be repeated in the source, since nobody cares to maintain duplicate information.
  16. Don't use page formatting codes in commants (such as line-feed).
  17. When listing names of developers, list both real name, company name (for consultants), abbreviation (which is a personal, unique tag, preferrable the same as used in the revision control system). If the name contains letters outside the English alphabeth, list both the native name and a normalized name.
  18. Dates and times shall be given with decreasing significance of values when read from left to right, and only expressed with numbers, not text. (for example, 2011-12-30 is correct, but Dec 30, 2011 or 30-12-2011 is bad).
  19. Comments shall be written as the code is written, not later! There is no budget allowing anyone to sit down and comment the code. And by commenting, it's also easier to detect thought failures (if you can't comment, you probably don't know what you're doing).
  20. Comment blocks should not be longer than 79 characters, in order to fit any display. Maximum 70 characters are even better.
  21. Never explain the obvious, such as things a professional C programmer is supposed to know anyway.
  22. In headers, use spaces all the way, not tabs, since many of the labels will be of different length, making the column alignment look sloppy.
  23. Don't waste too much space by using blank lines in the header comment block.
  24. Declare important compiler switches if necessary, in the header comment block.
  25. Skip comments you won't be able to maintain anyway. Wrong comments are even worse than no comments!
  26. Never copy a comment block from other source code files. Use a template instead!

Source file comment header example


/**********************************************************************
* File:     pink.c
* Project:  Scartrek
* Purpose:  Program for calculating different shades of pink.
* Authors:  Loke Skywalker/Löke Skywalker (LOS), loke@rymdpromenad.com
*           Yoda (YOD), yoda@youghurt.com
* Target:   Ubuntu Linux 10.04 kernel 2.6.42, 32-bit.
* Created:  2012-01-05
* Synopsis: pink [-rgb r.b.g] [-l]
*           r, b and g are decimal numbers in range 0 to 255 which
*           declares the pink shade for which a name shall be calcualted.
*           -l gives a list of all possible names.
*           Without option, a usage message is printed to stdout.
* Exit:     0 - no error
*           1 - usage error
*           2 - lost contact with server
* Switches: DEBUG     Enable debugging output.
*           SILLYDATE Use bad (American) date format (dd/mm/yyyy).
* Copyright (c) Dark Valium Enterprices
**********************************************************************/

Function header example


/**********************************************************************
* Function: dismiss
* Purpose:  Constructs a sentence, dissmissing whatever the argument
*           request says.
* Args:     1. request   A request written in English.
*           2. dest      Destination to copy the dismissal to. If NULL,
*                        nothing is copied and a locally allocated
*                        string is returned instead.
* Return:   On success, pointer to a string containing the dismissal is
*           returned. This will be the address of dest if it was given.
*           On failure, NULL is returned.
**********************************************************************/

char	*dismiss(const char *request, char *dest)
{
	...

Bad


int	ii;	// Loop counter
char	ch;	/* Input character from delta stream */
int	distance;	// Distance

On the first line, the obvious is explained. No need to comment!
On the second line, space is wasted on the ending */.
On the last line, nothing is actually explained, just repeated.

Section: Indentation

  1. Decide how to indent the source code for the entire project and make sure every developer follow the rules you have decided
  2. Take the time to set your (an all other developers') editor to indent one step for each press on the TAB key, so you don't have to count the blanks each time.
I usually keep the tabs (ASCII 8) in the source code, and let a tab steps correspond to 8 characters, because that's usually the default in most editors.

Motivation

  1. It is tedious to press the space bar many times. Just hitting tab once is easier.
  2. It is difficult to keep count on the exact number of spaces. Let the editor do the work for you!
  3. Too narrow indenting makes the levels difficult to detect. It also allows you to use too deep indention levels, which makes the code blocks hard to understand and difficult to unit test.

Only increase indentation level between the curly braces, without including them on the new level!

Bad


if (something)
	{
		while (somethingelse)
			{
				dothis();
				dothat();
			}
	}
Above, space is wasted in both X and Y directions. We need to scroll more to see the full code, thus getting a bad overview. And we're actually indenting two steps for each level, which becomes confusing.

Good


if (something)
{
	while (somethingelse)
	{
		dothis();
		dothat();
	}
}
Some people like the above syntax. I don't because every { and } take a full line just for one character. This means more scrolling in Y direction, which in turn means worse overview of the code, or requirement for larger displays (I sometimes write code using my mobile phone, which has a quite limited display size).

Best


if (something) {
	while (somethingelse) {
		dothis();
		dothat();
	}
}
This is compact enough, requiring little scrolling. I don't recommend putting the ending } elsewhere, because that would make the indention ends harder to detect. Well, you could always consider switching to Python :), which is another of my favorite languages!

Section: Where to put { and }

  1. { should be put on the same line as corresponding built-in keyword (if, else, while, for) and beginning of struct or typedef declations.
  2. { should be on a line by itself at the beginning of function definitions.
  3. If a separate block is created to allow temporary automatic variables to be declared in a limited scope, { should exist on a separate line.
  4. } should always be on a line by itself, but may be followed by a comment which indicates what is ended. If so, there should be a tab before the comment.
  5. Exception to above rule is in typedef:s, where the defined word should follow the }, and before else.
  6. Initialized vectors should be aligned so that indentation levels are clearly visible. This can be enhanched by using blanks and tabs in a way so that values begin in the same positions.
  7. Indent only once! There are some silly style guides like GNU, Allman and Whitesmiths that recommends first an indention of two(!) spaces before the {, then again on the next code line. Main reason for not following this style is that it implements two symbolic indentations for one logic level (there are more stupid recommendations in the GNU guide though).

Motivation

All above rules are aimed to improve readability. Some people mean { should always be on separate lines. It might make indentations more visible, but at the same time less code fits in the visible part of the display, so more scrolling is required, which by itself reduces the overview. If tabs are used for indentation, the levels will be pretty clear anyway.

Good Bad
if (a > b) {
	x = y;
} else {
	x = 1;
}
if (a > b)
{
	x = y;
}
else
{
	x = 1;
}
typedef struct	nlist {
	char	*name;
	struct nlist	*pre,
			*nxt;
} namelist;
typedef struct	nlist
{
	char	*name;
	struct nlist	*pre,
			*nxt;
}
namelist;
static int	foo(int bar)
{
	return(bar * FOO);
}	/*>> foo() <<*/
static int	foo(int bar) {
	return(bar * FOO);
}
/*>> foo() <<*/
int	foo(char *bar)
{
	int	ii;

	{
		char *bap = bar;
		for (ii = 0; *bap; *bap++) {
			inv(bap);
		}
	}
}	/*>> foo() <<*/
int	foo(char *bar)
{
	int	ii;

	{ char *bap = bar;
		for (ii = 0; *bap; *bap++) {
			inv(bap);
	} }
} /*>> foo() <<*/
char	*entrefs[] = {
		"lt",     "gt",    "auml", "ouml",
		"eacute", "aring", "nbsp", "quot"
};
coords	route[] = {
		{ 1, 5}, { 4,  2}, { 7,  9}, {11, 5},
		{12, 8}, {15, 12}, {17, 10}, {14, 6}
};
char	*entrefs[] = {"lt", "gt", "auml", "ouml",
	"eacute", "aring", "nbsp", "quot"};
coords	route[] = {{1, 5}, {4, 2}, {7, 9}, {11, 5},
	{12, 8}, {15, 12}, {17, 10}, {14, 6}};
int	foo(char *bar, int count)
{
	int	ii;

	while (--count) {
		burp(bar, count);
		for (ii = 0; *bap; *bap++) {
			inv(bap);
		}
	}
}	/*>> foo() <<*/
int	foo(char *bar, int count)
{
  int ii;

  while (--count)
    {
      burp(bar, count);
      for (ii = 0; *bap; *bap++)
        {
          inv(bap);
        }
    }
}  /*>> foo() <<*/

Section: Switch-case

  1. Indent both the case line and the action that follows it only if there is space enough. Otherwise chose which one to indent, and be consequent.
  2. Put the break statement on the same level as the preceeding action lines.
  3. Insert an empty line before each case and default statement, except the very first.
  4. If one action falls through to the next case or default (doesn't contain a break, clearly comment this with // FALLTHROUGH or similar.
  5. If each action is very minimalistic, consider putting both the action and the break statement on the same line with a tab inbetween. But use this only for very limited actions.
  6. List case:s in logical order.
  7. Avoid further indentations inside the action blocks. Use helper functions if needed.
  8. Avoid hard coded numerical constants as case values, use mnemonics (#define) instead (the examples below break this rule for simplicity).
Good Bad
switch(result) {
	case(0):
		res = UNKNOWN;
		break;

	case(1):
		res = GOOD;
		break;

	default:
		res = BAD;
}
switch(result) {
	case(0):
		res = UNKNOWN;
	break;

	case(1):
		res = GOOD;
	break;

	default:
		res = BAD;
}
switch(result) {
	case(0):
	res = UNKNOWN;
	break;

	case(1):
	res = GOOD;
	// FALLTROUGH

	default:
	res = BAD;
}
switch(result) {
	case(0):
	res = UNKNOWN;
	break;

	case(1):
	res = GOOD;

	default:
	res = BAD;
}
switch(result) {
	case(0):
	res = UNKNOWN;	break;

	case(1):
	res = GOOD;	break;

	default:
	res = BAD;
}
switch(result) {
	case(0):
	res = UNKNOWN; break;

	case(1):
	res = GOOD; break;

	default:
	res = BAD;
}
switch(result) {
	case(0):
	res = calc_res(UNKNOWN);
	break;

	case(1):
	res = calc_res(GOOD);
	break;

	default:
	res = calc_res(BAD);
}
switch(result) {
	case(0):
	if (old != new) {
		if (x = readsomething(buff, result)) {
			dosomething(x);
		} else {
			dosomethingelse(x);
		}
		res = UGLY;
	} else {
		res = UNKNOWN;
	}
	break;

	case(1):
	...
	break;

	default:
	...
}

Section: Spaces

  1. Use one space before and one space after binary and trinary operators.
  2. Never use space between unary operator and its operand.
  3. Always use a space between a cast and its operand.
  4. Don't use space right of left parenthesis, and don't use space left of right parenthesis.
  5. Don't trail lines with spaces after the last visible character. This can make code printouts look wrong, especially on already long lines when they wrap.
  6. Avoid spaces in file names. This can confuse shell commands, FTP etc., and it's difficult to tell apart one and two aligned spaces. It is also more difficult to search such files if one part slips passed a line break.
  7. Use at least one space before { if it doesn't begin the line.
  8. Use at least one space after } if it doesn't end the line.
  9. Don't use space before ending ;.
  10. Always follow a comma by a space.
  11. Always follow a semi-colon in a for-loop argument by a space.
  12. Use space between built-in statements and the following left parenthesis.
  13. Don't use space between ordinary function names and its following parameter block.
  14. Don't use C++ style pointer declaration! Put the * directly in front of the declared symbol, not to the right of the type!
  15. Don't declare more than one veriable per line (using commas as separators). Instead declare one variable per line and repeat the type on each line. This makes it possible to add a comment after each variable and a free text seach with grep reveals the variable type directly.
Good Bad
res = LIMIT + END;
return (lx >= end ? FAIL : OK);
res=LIMIT+END;
return (lx>=end?FAIL:OK);
char *pos;
neg = !pass;
set = ~mask | ABITS;
char * pos;
neg = ! pass;
set = ~ mask | ABITS;
if ((flags & STOP) || (max_reached)) {
	pos = 15 * (scale - apos);
if ( ( flags & STOP ) || ( max_reached ) ) {
	pos = 15 * ( scale - apos );
for (ii = 0; ii < END; ii++) {
	while (!done) {
		doit();
	}
}
for (ii = 0; ii < END; ii++){
	while (!done){
		doit();
	}
 }
int  aa = 0;
char bb;
if (aa > 0) {
    aa++;
int  aa = 0 ;
char bb ;
if (aa > 0) {
    aa++ ;
for (ii = 0; ii < END; ii++) {
	if (a > b) {
		return (code + OPT);
for(ii = 0; ii < END; ii++) {
	if(a > b) {
		return(code + OPT);
static void	helper(int);
if (release(party) == wild) {
	callfor(ambulance);
static void	helper (int);
if (release (party) == wild) {
	callfor ( ambulance );
int	*foo;
char	*bar,
	*hopper;
This is not really good but used as an example only!
int*	foo;
char*	bar,
	hopper;
Note: hopper is not a pointer to a char here!
int	foo;	// Food object
int	boo;	// Book object
int	doo;	// Doodle object
char	*bar;	// Name of bar
char	*fhop;	// First hopper
int	foo, boo, doo;	// Some objects
char	*bar,	// Name of bar
	*fhop;	// First hopper

Section: Source file order

Use the following order in your C source files:

  1. Header comment block
  2. #include of standard library header files.
  3. #include of local/own header files.
  4. Local pre-processor #define:s.
  5. typedef:s.
  6. Module global variable declarations (limited to current source file by being declared static).
  7. Module global function declarations (limited to current source file by being declared static). If any of them use helper functions, the helper function's prototype should preceed the caller function.
  8. Function definitions: Keep the order so that if A calls B, B should be defined above A. If there's a main() function, it should appear last in the file.
  9. Discrete helper functions ought to be defined between the calling function's comment block and its definition, as long as the helper function is only called from one single function.

Motivation

  1. Putting the calling functions below called functions is natural as heavier things usually gravitates downwards in real life. This makes it easier to locate the the function one looks for.
  2. The stated order of inclusions and declarations ensures there's no forward declarations that can't be handled.

Section: Naming

  1. Use names that makes a clear connection to the purpose.
  2. Make names which are easy to find on a search. Thus, don't use i as loop counter, use ii instead.
  3. Avoid too long names. A variable name should not wreck the code layout and force you to buy an extra wide display, because it will come out weird on a paper printout anyway. And, if you can't understand that imgconv means imageconversion, you shouldn't do programming anyway. But don't expect anyone to understand what imcv means! Think of it this way: Friends to people with very long names will quickly start using shorter nicknames!
  4. Decide early in your project how to handle singular and plural naming of your entities. Then keep that strategy. Exemple: What would you call a typedef struct holding contact information, and what would you call a list of such records? contact or contacts?
  5. Never use the name of a company or an individual to construct names, since companies are taken over and people are replaced. Exemption: Known units named after persons such as Hertz, Pascal etc.
  6. Don't use special prefixes to indicate data type (such as letting all pointers start with p, and zero terminated strings start with sz).
  7. When grouping names for similarities, put the most significant part of the name first (to the left), since this is a general rule, used in numbers, dates etc.
  8. Use lower case names normally, but use all upper case for pre-processor defines (#define FOO). This is a well-established rule, helping you to look for the defines in header files.
  9. Avoid using _ at the beginning of names, since many of the compiler built-in names are constructed this way and may later collide with your own code.
  10. Avoid repeating the same character more than once since it can be difficult to read them, and don't repeat _ since it isn't easy at all to read on some screens or on paper printouts.
  11. Never use initial upper-case letters for variables and functions in C! You will regret it when convering the code to Java or C++, as this maming convention is reserved for classes.
  12. Never use diacritic or non-English symbols (characters outside the range 33-127 ASCII 8-bit, for example ä, 不汉语) anywhere in the source code to be compiled. Only acceptable exemptions would be restricted usage in comments and hard coded string values (but then, only for the values, not for the variable holding the value). Reasons: 1) The code may becom incompatible with other compilers 2) Other tools may protest even if your compiler accepts it 3) If you send your source via email to other developers, the mailing program might convert these characters to something void or odd 4) Probably, using these characters mean you base your code on a language (Swedish, Finnish, Polish, Chinese, Hebrew, Yiddish etc.) less known to others so your code will be difficult to maintain in the future. Use English!

Section: Nested if-statements

Avoid nested if-statements. Instead, build sequential tests where each failure leads to a break out from the test set. And make sure the quickest and most significant tests are made as early as possible.

Bad


if (!timeout(MAXTIM)) {
	if ((slices = waitforaccess(acctim)) {
		if (writesize <= freespace()) {
			if (slices > 0) {
				if (writesize > 0) {
					if (write(fd, buf, writesize) != writesize) {
						err = ERR_WRITE;
					}
					err = ERR_OK;
				} else {
					err = ERR_NODATA;
				}
			} else {
				err = ERR_NOSLICE;
			}
		} else {
			err = ERR_NOSPACE;
		}
	} else {
		err = ERR_NOACCESS;
	}
} else {
	err = ERR_TIMEOUT;
}
return(err);

Above, some code executes slowly and might not have to be executed anyway if any of the later tests fail. There are many lines between the test and the action to take if it fails, making overview difficult. The indentation depth also makes the code come too close to the right margin, a paper printout would wither cut the lines or make them wrap which looks odd.

Good


if (writesize <= 0) {
	return(ERR_NODATA);
}
if (!(slices = waitforaccess(acctim))) {
	return(ERR_NOACCESS);
}
if (slices <= 0) {
	return(ERR_NOSLICE);
}
if (writesize > freespace()) {
	return(ERR_NOSPACE);
}
if (timeout(MAXTIM)) {
	return(ERR_TIMEOUT);
}
if (write(fd, buf, writesize) != writesize) {
	return(ERR_WRITE);
}
return(ERR_OK);

Here, the fastes tests run first to save time. As soon as we find that we can't continue, we break out and don't waste more time. There are no deep nesting levels, test and error codes are next to each other. The code is easy to follow.

Section: Hard coded constants

Avoid hard coded constants! They can be very hard to find, making the program inconsequent and maintenance more time consuming.

Bad


printf("Please enter your age: ");
if (age < 18) {
	printf("Sorry, you must be at least 18 to enter!\n");

Good


#define MINAGE          18
#define AGEPROMPT       "Please enter your age"
#define DISMISS         "Sorry, you must be at least 18 to enter!"

printf("%s: ", AGEPROMPT);
if (age < MINAGE) {
	printf("%s\n", DISMISS);

The Good example isn't excellent though! To prepare for another language, we shouldn't even hard code the defines. And 18 appears twice anyway. If the program is exported to another country, it might have another minimum age (or none at all), so perhaps the values should be read from a configuration file instead?

Section: Extra ( and {

I recommend using extra parenthesis and curly braces in some situations:

Bad


if (score + CONST)
	show_score(score);
dirx = (PT + CONST * oldx - saved);
pass = fails < succs ? pos & PBIT ? run != lev ? YES : NO : NO : NO;

Good


if (score + CONST) {
	show_score(score);
}
dirx = ((PT + (CONST * oldx)) - saved);
pass = (fails < succs ?
		(pos & PBIT ?
			(run != lev ?
				YES
				: NO)
		: NO)
	: NO);

The last part of this example is actually bad in both situations, the whole construct should be avoided. See Nested if-statements above.

Section: Column alignment

Readability can sometimes be improved by aligning similar types of code to the same column, provided you don't go too far. I always use a tab in front of declared entities, and try to start comments in the same column on consecutive, similar lines. Example:


int	whatever(char *buff)
{
	int	ii;		// Loop counter
	char	*pos;		// Current position
	time_t	access_time;	// Timestamp of access attempt
	struct something	somethingstrange;	// Monsters
	struct ufo_charter	flyingsausage;		// Vehicle

Notice that since ii is much shorter than access_time, two tabs are used before the ii comment. Some types are too long so it's time to give up trying to align them with the others. It's better to begin a new group with new alignment positions.

Section: Misc. advices

  1. Don't compare logical results against any constant such as 1 and 0, or true and false. There's no guarantee that your computer represents true with 1, it might as well be 255 or -1.
  2. Use NULL, not 0, to check a pointer for invalidity.
  3. Use the symbol '\0' to denote end of string, not 0.
  4. To create short execution pauses use either sleep() or select(). Never rely on empty loops. The compiler might optimize them away, different computers have different clock rates and it steals a lot of CPU power.
  5. Avoid gets(), strcpy() and strtok() (risk of buffer overflow and overwrite).
  6. When writing debugging output, make sure it is followed by a fflush() for the debug file, else the message might get trapped in the display buffer and discarded if the program crashes immediately after. You may alternatively open the debug file in sync mode (O_SYNC).
  7. Immediately after freeing memory, set the memory pointer variable to NULL to avoid reusing the freed memory. Getting an immediate crash is better!
  8. Always define a specific exit value for your programs. Exit value 0 should indicate success, any value from 1 to 254 may indicate some failure. Usually 1 means bad usage. Avoid 255 since the shell uses it in some cases.
  9. Avoid using system() to invoke external programs, as the means of controlling the invoked process is very limited. Instead use popen() or any of the exec..() calls.

Rein's Howtos Project List
View Rein Ytterberg's LinkedIn profileView Rein Ytterberg's profile