C Coding Standard
C Coding Standard
C Coding Standard
C Coding Standard
Adapted from http://www.possibility.com/Cpp/CppCodingStandard.html and NetBSD's style
guidelines
Contents
1. Names
❍ Structure Names
❍ C File Extensions
❍ Pointer Variables
❍ Global Constants
❍ Enum Names
2. Formatting
❍ Brace {} Policy
❍ switch Formatting
3. Documentation
❍ Document Decisions
❍ Use Headers
4. Complexity Management
❍ Layering
5. Miscellaneous
❍ Be Const Correct
❍ Short Functions
❍ No Magic Numbers
❍ Macros
❍ Miscellaneous
Names
A name is the result of a long deep thought process about the ecology it lives in. Only a
programmer who understands the system as a whole can create a name that "fits" with the
system. If the name is appropriate everything fits together naturally, relationships are clear,
meaning is derivable, and reasoning from common human expectations works as
expected.
If you find all your names could be Thing and DoIt then you should probably revisit your
design.
Function Names
● Usually every function performs an action, so the name should make clear what it
does: check_for_errors() instead of error_check(), dump_data_to_file() instead of
data_file(). This will also make functions and data objects more distinguishable.
Structs are often nouns. By making function names verbs and following other
naming conventions programs can be read more naturally.
For example: retry_max to mean the maximum number of retries, retry_cnt to mean
the current retry count.
uint32 timeout_msecs;
uint32 my_weight_lbs;
Structure Names
● Use underbars ('_') to separate name components
Example
struct foo {
struct foo *next; /* List of active foo */
struct mumble amumble; /* Comment for mumble */
int bar;
Justification
● With this approach the scope of the variable is clear in the code.
● Now all variables look different and are identifiable in the code.
Example
Pointer Variables
● place the * close to the variable name not pointer type
Example
Global Variables
● Global variables should be prepended with a 'g_'.
Justification
Example
Logger g_log;
Logger* g_plog;
Global Constants
● Global constants should be all caps with '_' separators.
Justification
It's tradition for global constants to named this way. You must be careful to not conflict
with other global #defines and enum labels.
Example
file. If they are an inline expansion of a function, the function is defined all in
lowercase, the macro has the same name all in uppercase. If the macro is an
expression, wrap the expression in parenthesis. If the macro is more than a single
statement, use ``do { ... } while (0)'', so that a trailing semicolon works. Right-justify
the backslashes; it makes it easier to read.
Justification
This makes it very clear that the value is not alterable and in the case of macros, makes it
clear that you are using a construct that requires care.
Some subtle errors can occur when macro names and enum labels use the same name.
Example
Enum Names
This is the standard rule for enum labels. No comma on the last element.
Example
enum PinStateType {
PIN_OFF,
PIN_ON
};
It's often useful to be able to say an enum is not in any of its valid states. Make a label for
an uninitialized or error state. Make it the first label if possible.
Example
Formatting
Brace Placement
Of the three major brace placement strategies one is recommended:
All if, while and do statements require braces even if there is only a single statement within
the braces. For example:
if (1 == somevalue) {
somevalue = 2;
}
Justification
It ensures that when someone adds a line of code later there are already braces and they
don't forget. It provides a more consistent look. This doesn't affect execution speed. It's
easy to do.
if (1 == somevalue) somevalue = 2;
Justification
It provides safety when adding new lines while maintainng a compact readable form.
while(1) {
if (valid) {
} /* if valid */
else {
} /* not valid */
} /* end forever */
Justification
● Keywords are not functions. By putting parens next to keywords keywords and
Example
if (condition) {
}
while (condition) {
}
strcpy(s, s1);
return 1;
Justification
● Even though with big monitors we stretch windows wide our printers can only print
so wide. And we still need to print code.
● The wider the window the fewer windows we can have on a screen. More windows is
better than wider windows.
● We even view and print diff output correctly on all terminals and printers.
Layout
It's up to the programmer. Different bracing styles will yield slightly different looks. One
common approach is:
if (condition) {
} else if (condition) {
} else {
}
If you have else if statements then it is usually a good idea to always have an else block for
finding unhandled cases. Maybe put a log message in the else even if there is no corrective
action taken.
Condition Format
Always put the constant on the left hand side of an equality/inequality comparison. For
example:
if ( 6 == errorNum ) ...
One reason is that if you leave out one of the = signs, the compiler will find the error for
you. A second reason is that it puts the value you are looking for right up front where you
can find it instead of buried at the end of your expression. It takes a little time to get used
to this format, but then it really gets useful.
switch Formatting
● Falling through a case statement into the next case statement shall be permitted as
long as a comment is included.
● The default case should always be present and trigger an error if it should not be
reached, yet is reached.
Example
switch (...)
{
case 1:
...
/* comments */
case 2:
{
int v;
...
}
break;
default:
}
Goto
Goto statements should be used sparingly, as in any well-structured code. The goto
debates are boring so we won't go into them here. The main place where they can be
usefully employed is to break out of several levels of switch, for, and while nesting,
although the need to do such a thing may indicate that the inner constructs should be
broken out into a separate function, with a success/failure return code.
for (...) {
while (...) {
...
if (disaster) {
goto error;
}
}
}
...
error:
clean up the mess
When a goto is necessary the accompanying label should be alone on a line and to the left
of the code that follows. The goto should be commented (possibly in the block header) as
to its utility and purpose.
Continue and break are really disguised gotos so they are covered here.
Continue and break like goto should be used sparingly as they are magic in code. With a
simple spell the reader is beamed to god knows where for some usually undocumented
reason.
while (TRUE) {
...
/* A lot of code */
...
if (/* some condition */) {
continue;
}
...
/* A lot of code */
...
if ( i++ > STOP_VALUE) break;
}
Note: "A lot of code" is necessary in order that the problem cannot be caught easily by the
programmer.
From the above example, a further rule may be given: Mixing continue with break in the
same loop is a sure way to disaster.
?:
The trouble is people usually try and stuff too much code in between the ? and :. Here are a
couple of clarity rules to follow:
● Put the action for the then and else statement on a separate line unless it can be
clearly put on one line.
Example
or
(condition)
? long statement
: another long statement;
1. The code is easier to read. Use some white space too. Nothing better than to read
code that is one line after another with no white space or comments.
Not:
char **a, *x;
Do:
char **a = 0; /* add doc */
char *x = 0; /* add doc */
3. Declarations are clear which reduces the probablity of declaring a pointer when you
meant to declare just a char.
C allows constant variables, which should deprecate the use of enums as constants.
Unfortunately, in most compilers constants take space. Some compilers will remove
constants, but not all. Constants taking space precludes them from being used in tight
memory environments like embedded systems. Workstation users should use constants
and ignore the rest of this discussion.
In general enums are preferred to #define as enums are understood by the debugger.
Be aware enums are not of a guaranteed size. So if you have a type that can take a known
range of values and it is transported in a message you can't use an enum as the type. Use
the correct integer size and use constants or #define. Casting between integers and enums
is very error prone as you could cast a value not in the enum.
#ifndef sys_socket_h
#define sys_socket_h /* NOT _sys_socket_h_ */
#endif
Macros
Don't Turn C into Pascal
Don't change syntax via macro substitution. It makes the program unintelligible to all but
the perpetrator.
Example
The macro above can be replaced for integers with the following inline function with no
loss of efficiency:
inline int
max(int x, int y) {
return (x > y ? x : y);
}
Example
MAX(f(x),z++);
Example
#define ADD(x,y) x + y
must be written as
Justification
● More problems than you can believe are eventually traced back to a pointer or
variable left uninitialized.
Short Functions
● Functions should limit themselves to a single page of code.
Justification
● The idea is that the each method represents a technique for achieving a single
objective.
● True function calls are slower than not, but there needs to a thought out decision
(see premature optimization).
if (FAIL != f())
is better than
if (f())
even though FAIL may have the value 0 which C considers to be false. An explicit test will
help you out later when somebody decides that a failure return should be -1 instead of 0.
Explicit comparison should be used even if the comparison value will never change; e.g., if
(!(bufsize % sizeof(int))) should be written instead as if ((bufsize % sizeof(int)) == 0) to
reflect the numeric (not boolean) nature of the test. A frequent trouble spot is using strcmp
to test for string equality, where the result should never ever be defaulted. The preferred
approach is to define a macro STREQ.
inline bool
string_equal(char* a, char* b)
{
(strcmp(a, b) == 0) ? return true : return false;
Or more compactly:
return (strcmp(a, b) == 0);
}
Note, this is just an example, you should really use the standard library string type for
doing the comparison.
The non-zero test is often defaulted for predicates and other functions or expressions
which meet the following restrictions:
● Is named so that the meaning of (say) a true return is absolutely obvious. Call a
predicate is_valid(), not check_valid().
The ++ and -- operators count as assignment statements. So, for many purposes, do
functions with side effects. Using embedded assignment statements to improve run-time
performance is also possible. However, one should consider the tradeoff between
increased speed and decreased maintainability that results when embedded assignments
are used in artificial places. For example,
a = b + c;
d = a + r;
d = (a = b + c) + r;
even though the latter may save one cycle. In the long run the time difference between the
two will decrease as the optimizer gains maturity, while the difference in ease of
maintenance will increase as the human memory of what's going on in the latter piece of
code begins to fade.
Documentation
Document Decisions
Comments should document decisions. At every point where you had a choice of what to
do place a comment describing which choice you made and why. Archeologists will find
this the most useful information.
Use Headers
Use a document extraction system like Doxygen.
These headers are structured in such a way as they can be parsed and extracted. They are
not useless like normal headers. So take time to fill them out. If you do it right once no
more documentation may be necessary.
Comment Layout
Each part of the project has a specific comment layout. Doxygen has the recommended
format for the comment layouts.
Gotcha Keywords
● @author:
specifies the author of the module
● @version:
specifies the version of the module
● @param:
specifies a parameter into a function
● @return:
specifies what a function returns
● @deprecated:
says that a function is not to be used anymore
● @see:
creates a link in the documentation to the file/function/variable to consult to get a
better understanding on what the current block of code does.
● @todo:
what remains to be done
● @bug:
report a bug found in the piece of code
Gotcha Formatting
● Comments may consist of multiple lines, but the first line should be a self-
containing, meaningful summary.
● The writer's name and the date of the remark should be part of the comment. This
information is in the source repository, but it can take a quite a while to find out
when and by whom it was added. Often gotchas stick around longer than they
should. Embedding date information allows other programmer to make this decision.
Embedding who information lets us know who to ask.
explicit prototype declaration in the .h file, should have a header in the .c file.
Layering
Layering is the primary technique for reducing complexity in a system. A system should be
divided into layers. Layers should communicate between adjacent layers using well
defined interfaces. When a layer uses a non-adjacent layer then a layering violation has
occurred.
A layering violation simply means we have dependency between layers that is not
controlled by a well defined interface. When one of the layers changes code could break.
We don't want code to break so we want layers to work only with other adjacent layers.
Sometimes we need to jump layers for performance reasons. This is fine, but we should
know we are doing it and document appropriately.
Miscellaneous
General advice
This section contains some miscellaneous do's and don'ts.
● 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 !=).
● Compilers have bugs. Common trouble spots include structure assignment and bit
fields. 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.
● 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. Finally, since beautifiers are non-trivial
programs that must parse the source, a sophisticated beautifier is not worth the
benefits gained by such a program. Beautifiers are best for gross formatting of
machine-generated code.
● Accidental omission of the second ``='' of the logical compare is a problem. The
following is confusing and prone to error.
Does the programmer really mean assignment here? Often yes, but usually no. The
solution is to just not do it, an inverse Nike philosophy. Instead use explicit tests and
avoid assignment with an implicit test. The recommended form is to do the
assignment before doing the test:
abool= bbool;
if (abool) { ... }
● 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.
Be Const Correct
C provides the const key word to allow passing as parameters objects that cannot change
to indicate when a method doesn't modify its object. Using const in all the right places is
called "const correctness." It's hard at first, but using const really tightens up your coding
style. Const correctness grows on you.
#ifdef DEBUG
temporary_debugger_break();
#endif
Someone else might compile the code with turned-of debug info like:
cc -c lurker.cc -DDEBUG=0
Alway use #if, if you have to use the preprocessor. This works fine, and does the right
thing, even if DEBUG is not defined at all (!)
#if DEBUG
temporary_debugger_break();
#endif
If you really need to test whether a symbol is defined or not, test it with the defined()
construct, which allows you to add more things later to the conditional without editing text
that's already in the program:
#if !defined(USER_NAME)
Using #if 0
void
example()
{
great looking code
#if 0
lots of code
#endif
more code
}
You can't use /**/ style comments because comments can't contain comments and surely a
large block of your code will contain a comment, won't it?
Don't use #ifdef as someone can unknowingly trigger ifdefs from the compiler command
line. #if 0is that even day later you or anyone else has know idea why this code is
commented out. Is it because a feature has been dropped? Is it because it was buggy? It
didn't compile? Can it be added back? It's a mystery.
#if NOT_YET_IMPLEMENTED
#if OBSOLETE
#if TEMP_DISABLED
File Extensions
In short: Use the .h extension for header files and .c for source files.
/*
* aheader.h
*/
int x = 0;
1. It's bad magic to have space consuming code silently inserted through the innocent
use of header files.
2. It's not common practice to define variables in the header file so it will not occur to
developers to look for this when there are problems.
3. Consider defining the variable once in a .c file and use an extern statement to
reference it.
in a C library from C++ you must wrap in the above syntax. Here are some examples:
If you have code that must compile in a C and C++ environment then you must use the
__cplusplus preprocessor directive. For example:
#ifdef __cplusplus
#else
extern some_function();
#endif
No Magic Numbers
A magic number is a bare naked number used in source code. It's magic because no-one
has a clue what it means including the author inside 3 months. For example:
In the above example what do 22 and 19 mean? If there was a number change or the
numbers were just plain wrong how would you know? Instead of magic numbers use a real
name that means something. You can use #define or constants or enums as names. Which
one is a design choice. For example:
Now isn't that better? The const and enum options are preferable because when debugging
the debugger has enough information to display both the value and the label. The #define
option just shows up as a number in the debugger which is very inconvenient. The const
option has the downside of allocating memory. Only you know if this matters for your
application.
● Include the system error text for every system error message.
● Check every call to malloc or realloc unless you know your versions of these calls do
the right thing. You might want to have your own wrapper for these calls, including
new, so you can do the right thing always and developers don't have to make
memory checks everywhere.