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:
- 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.
- 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
- 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.
- 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.
- 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.
- 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.
- Constants and variables should be commented normally, but
should not be commented when their purposes are obvious.
- All function arguments should be commented with purpose,
valid range and any exceptions.
- Functions should be preceeded by a comment stating its
purpose, parameters and return value.
- The main source file should contain a comment header stating
project, author(s), creation date, synopsis, exit value, purpose
and if necessary, copyright statement.
- 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.
- Long code blocks (including functions) should be ended with
an end comment, to make orientation easier.
- Comments shall be written in English.
- Comments shall be spell checked.
- 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!
- 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.
- 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.
- Don't use page formatting codes in commants (such as line-feed).
- 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.
- 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).
- 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).
- Comment blocks should not be longer than 79 characters, in order
to fit any display. Maximum 70 characters are even better.
- Never explain the obvious, such as things a professional
C programmer is supposed to know anyway.
- 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.
- Don't waste too much space by using blank lines in the header
comment block.
- Declare important compiler switches if necessary, in the
header comment block.
- Skip comments you won't be able to maintain anyway. Wrong comments
are even worse than no comments!
- Never copy a comment block from other source code files. Use a
template instead!
/**********************************************************************
* 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
- Decide how to indent the source code for the entire project and make
sure every developer follow the rules you have decided
- 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
- It is tedious to press the space bar many times. Just hitting
tab once is easier.
- It is difficult to keep count on the exact number of spaces.
Let the editor do the work for you!
- 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 }
- { should be put on the same line as corresponding
built-in keyword (
if, else, while, for) and
beginning of struct or typedef declations.
- { should be on a line by itself at the beginning
of function definitions.
- If a separate block is created to allow temporary
automatic variables to be declared in a limited scope,
{ should exist on a separate line.
- } 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.
- Exception to above rule is in
typedef:s, where the
defined word should follow the }, and before
else.
- 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.
- 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
- 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.
- Put the break statement on the same level as the
preceeding action lines.
- Insert an empty line before each
case and
default statement, except the very first.
- If one action falls through to the next
case
or default (doesn't contain a break,
clearly comment this with // FALLTHROUGH or
similar.
- 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.
- List
case:s in logical order.
- Avoid further indentations inside the action blocks. Use
helper functions if needed.
- 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
- Use one space before and one space after binary and trinary operators.
- Never use space between unary operator and its operand.
- Always use a space between a cast and its operand.
- Don't use space right of left parenthesis, and don't use space left of
right parenthesis.
- 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.
- 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.
- Use at least one space before { if it doesn't begin the line.
- Use at least one space after } if it doesn't end the line.
- Don't use space before ending ;.
- Always follow a comma by a space.
- Always follow a semi-colon in a
for-loop argument
by a space.
- Use space between built-in statements and the following left
parenthesis.
- Don't use space between ordinary function names and its following parameter
block.
- Don't use C++ style pointer declaration! Put the
*
directly in front of the declared symbol, not to the right of the type!
- 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:
- Header comment block
#include of standard library header files.
#include of local/own header files.
- Local pre-processor
#define:s.
typedef:s.
- Module global variable declarations (limited to current
source file by being declared
static).
- 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.
- 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.
- 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
- 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.
- The stated order of inclusions and declarations ensures
there's no forward declarations that can't be handled.
Section: Naming
- Use names that makes a clear connection to the purpose.
- Make names which are easy to find on a search. Thus, don't use
i as loop counter, use ii instead.
- 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!
- 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?
- 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.
- Don't use special prefixes to indicate data type
(such as letting all pointers start with p, and zero terminated strings
start with
sz).
- 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.
- 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.
- 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.
- 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.
- 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.
- 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:
- Use extra ( when the evaluation order isn't fully clear.
- Use extra ( when the type of arithmetics is changed, even if
the compiler does it automatically. For example, if a mathematical
expression is treated as logical.
- In case nested trinary operator
? : if used, always
use ( around each of them, and write only one level per line.
- Always use { in
if, while and for
statements, even if the action is only one line. Doing so means you
can simply adding a debug call at the same level without need to
break the code structure. Also any indentation errors shouldn't
make the program fail.
- It could be a good idea to surround pre-processor definitions
with extra parenthesis. For example,
#define SUMAB a + b
would give wrong result in the operation
c = 2 * SUMAB; and correct result if
the definition was changed to
#define SUMAB (a + b).
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
- 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.
- Use NULL, not 0, to check a pointer for invalidity.
- Use the symbol
'\0' to denote end of string, not 0.
- 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.
- Avoid
gets(), strcpy() and strtok() (risk
of buffer overflow and overwrite).
- 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).
- Immediately after freeing memory, set the memory pointer
variable to NULL to avoid reusing the freed memory. Getting
an immediate crash is better!
- 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.
- 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 profile