C Style Guide

Hello World,

This is a programming style guide for the C programming language.  The guide is meant to be concise and usable for everyday C programmers.  It is also meant to guide novice C programmers in writing code in a style that is easy for other C programmers to understand.

This guide does not apply to other languages, including C++.  C is its own language with its own syntax, and hence it own style for writing “good” code.

“Good” style is subjective.  This guide does not represent the “best” C style for all situations.  The contents of this guide are an amalgamation of many other guides and styles.  Nothing in this guide is unique or new, however its goal is consistency and readability.

What’s good?

Writing code that others can understand, and use, is more valuable that just “working code”.  I believe a programmer’s philosophy on crafting software should be:

  1. Write code for coders.
  2. Write code for users.
  3. Write code for computers.

For novices, the list above is often reversed.  They first write code that runs, period.  Then will try to transform that into something that a user may want.  Finally, if there is time, they might make it coder-friendly.  This last effort is usually just some sparse commenting about why “this part is done such and such way because of some weird compiler quirk”.  Unfortunately, code that is written this way is expensive or impossible to maintain.  Software that cannot be maintained usually is abandoned and has a short life span.

Give a man to fish and you feed him for a day.  Teach a man to fish and you feed him for a lifetime.  Give 1,000,000,000 fishermen a better tool for fishing and you feed the world.

Good software is easy for other programmers to use and change.  The “principle of least surprise” is a good metric for qualifying good software.  When writing new code, keep in mind the lucky stiff who will be inheriting and maintaining the code days, months or years from now.  This is not purely an altruistic philosophy because more often than not, that lucky stiff is you!  It is quite humbling to be analyzing a section of code, or library, thinking to yourself, “What the @#$% were they thinking?”… only to realize it was code you had written two years earlier.

Files and Directories

C code is written in plain text, it can be viewed and edited with a text editor.  The C files should be stored in an organized file structure, so each file is easy to find.

File structures vary from project to project, so flexibility is good here.  A couple of general rules I use are:

  • Separate source code files and compiler specific files.  Also, allow for multiple tools to be used to create the final product.
  • Setup folders early in the project life cycle, and use them to stay organized.

A fairly general top-level directory structure is:

  • bin -final binaries
  • build – compiler specific files
  • doc – documentation
  • ext – external library files
  • include – project include files
  • lib – library files built with project
  • rc – project resource files (e.g. images, sound files, other data)
  • src – project source code
  • test – project testing code
  • tools – any other tools needed to create the final product

When programming in C, each [.h, .c] file pair should represent a functional unit.  In an object-oriented language, each object can have members (data) and methods (functions).  C allows grouping of data with structures (struct), but no built-in mechanism to associate methods to a structure.  There is also no object-level distinction of public, protected, private variables.  All of the data in a struct is available to anyone that wants the data.

I prefer to use a style where a [.h, .c] represent an “object”, or group of related objects.  The .h file defines the public interface, and the .c file defines the implementation and private data and functions.  For instance, the files foo.h and foo.c will define a set of struct and functions to do all things foo-ey.  When creating libraries, the library interface .h files are placed in the /include/ directory.  Otherwise they are located in the /src/ directory (or sub-directory) in the same folder as the .c file.

Names

“What’s in a name? That which we call a rose. By any other name would smell as sweet.”

- Romeo and Juliet (II, ii, 1-2)

If Romeo were a programmer, he may have replied,

“Yes, but if I know not it is a rose, how shall I know to smell?

Simply put, names help convey meaning about what you can do with something.  In a C program, everything does something (otherwise it shouldn’t be there).  Names should describe what a particular object is and what it does, although they should not necessarily describe how a particular object does what it does.

Complete words should be used in names, unless the abbreviation is more common than the full name (e.g. radar).  Abbreviated names were often used in legacy code, and embedded code.  Unfortunately, abbreviations can lead to misunderstandings.  For instance:

int temp;

The above code looks harmless enough.  Sure, it is a variable for reading/writing temporary data.  Oh wait, that was the temperature of the nuclear reactor core that wasn’t supposed to be changed?  Uh-oh.

CAPITALIZATION

Capitalization is used to help convey what something is.  The capitalization scheme I prefer is:

  • UPPER_CASE with underscores for macros and constant data.
  • TitleCase without underscores for structure, enumeration and function names.
  • lower_case with underscores for structure variables, local variables, function parameters and macro parameters.

For example:

#define MACRO(a) (a = 1)

#define DEFINED_CONSTANT 1

const int CONST_INT_VARIABLE = 2;

struct ExampleStruct
{
  int int_value;
};

Foo(ExampleStruct a_struct);

FUNCTION NAMES

C does not have struct methods, or namespaces, like other object-oriented languages such as C++.  Therefore, one must be mindful of name collisions, where two sections of code use the same name for different things.  Hopefully your compiler warns you if this situations arises, otherwise the chance of the code executing correctly are very slim.

In C++, name mangling is used by the compiler to make a unique name for each function from each class method.  C only has struct types, which don’t have methods.  To associate a function with a structure, I use the following convention:

struct ExampleStruct;

ExampleStruct_Foo(struct ExampleStruct* self);

The name of the function begins with the structure name, followed by an underscore.  A pointer to the structure instance is passed as the first parameter, usually called ‘self’ which was borrowed from Python.

I prefer the NounVerb convention for naming functions.  For instance compare the following two groups of related functions that do not have a common parameter (perhaps operate on global or private data):

void InitializeGlobalData();
void FormatGlobalData();
void GetGlobalData();
void RecalibrateGlobalData();

void GlobalData_Initialize();
void GlobalData_Format();
void GlobalData_Get();
void GlobalData_Recalibrate();

Both of the function name groups are equivalent.  There is no advantage to the NounVerb convention, If each of the function name pairs (i.e. VerbNoun versus NounVerb) is considered individually.  Arguably, the VerbNoun convention is more readable, especially for native English speakers.  However, I believe the NounVerb syntax conveys more meaning when considered as a group.  It conveys what the function does, and that the function is member of a group of associated functions.  This is also useful when sorting alphabetically.  For instance, compare the following two alphabetical lists of functions:

void InitializeGlobalData();
void InitializePrivateData();
void FormatGlobalData();
void GetGlobalData();
void RecalibrateGlobalData();
void TwiddlePrivateData();

void GlobalData_Initialize();
void GlobalData_Format();
void GlobalData_Get();
void GlobalData_Recalibrate();
void PrivateData_Initialize();
void PrivateData_Twiddle();

The second list is easier (IMHO) to scan quickly.  Notice, it also shows that GlobalData has more functionality than PrivateData (after initialization, PrivateData can only be twiddled).

STATIC VARIABLE NAMES

When defining a static variable name inside a module .c file, I use the following convention:

static int ObjectName_variable_name;

In the example above “ObjectName” should be replaced by the “object” name that uses the variable.  For example:

static int GlobalData_a_global_int;

void GlobalData_Initialize()
{
  GlobalData_a_global_int = 0;
}

TYPE DEFINITIONS

Type definitions can be useful for readability.  I use the following convention for typedefs:

typedef struct Counter
{
  int count;

} Counter_t;

The _t is useful shorthand for indicating type, and is a common convention for the C standard library (e.g. size_t).

ENUMERATIONS

For enumerations, I prefer to use ‘Enum’ at the end of the name to denote is an enumerated type.  For example:

typedef enum FooStateEnum
{
  FOO_STATE_A,
  FOO_STATE_B

} FooStateEnum_t;

I’ve seen ‘Type’ at the end of an enumeration names when the enumeration value denotes a “object type”.  For example:

/** Please don't name enum's with Type. */
typedef enum ObjectType
{
  OBJECT_A,
  OBJECT_B

} ObjectType_t;

As somewhat of a code purist, I don’t like this approach because an ObjectType is really an object type, it is an enumeration (which is an integer).  This opinion is primarily a carryover from C++, so from a C perspective it isn’t 100% relevant or necessary.  However, I think it is more clear to the reader.  For example, consider the following function:

Foo(ObjectType_t object);

Hmm, I wonder how big that object is?  Should I worry about the pass-by-value call?  Compare this to the following function:

Foo(ObjectTypeEnum_t object);

The function above clearly takes an enumeration as its parameter.

SPLAT *

When using type names, the * indicates a change in type (i.e. from a thing, to a pointer to a thing).  Thus, when declaring variables, I prefer to keep the * with the type name.  For instance:

/* Keep the splat next to the data type */

int* i;
ExampleStruct_t* example;

void Foo(ExampleStruct_t* example);

/* Don't do any of the following */

int *i;
int * i;

ExampleStruct_t *example;
ExampleStruct_t * example;

void Foo(ExampleStruct_t *example);
void Foo(ExampleStruct_t * example);

When dereferencing a pointer, I prefer to keep the * with the variable.  For instance:

/* Keep the splat next to the variable when dereferencing */

void Foo(int* i)
{
  int j = *i;  /* splat is next to i */
}

White space

White space is the “code that isn’t code”.  Empty lines, indention and spaces make up the white space.  The most important rule with white space is be consistent.  This rule is most important for indention.  I prefer indention with 2 spaces.  Some of the fiercest, and somewhat pointless, style wars have been fought over indentation.  If you, or your coworkers, or the 10,000 lines of code you have inherited use a different indention style than your favorite, that is okay.  However, it should be consistent, at at least the file level. Four spaces, or tab indention, is also popular and acceptable.

Blank lines are very effective for indicating to the the reader that “something is different”.

I use the following convention for spacing:

/** A simple structure with a counter. */
typedef struct ExampleStruct
{
  int counter;

} ExampleStruct_t;

/** Two spaces between related structures and functions. */
void ExampleStruct_Foo();

/** Two spaces between related functions. */
void ExampleStruct_Count(ExampleStruct_t* self);

/** Four spaces between different structures and functions. */
struct AnotherStruct
{
  int size;
};

Comments

Comments should be included in source code when they improve readability, and/or are used to generate documentation.  A header file is typically commented more heavily than the .c implementation file.

Ideally, “the code is the documentation” philosophy is the best.  The book by Kent Beck entitled, “Refactoring: Improving the Design of Existing Code” has many excellent methods and ideas regarding this goal.

A comment is useful when providing more information, or explaining the use of a function or variable.  For example:

/** Main entry point for the global Frobinator.
 *  Should be called by main().
 *  It is recommended to call Defrobinate() after a call to
 *  Frobinate(), but is not required.
 */
Frobinate();

If the code is sufficient for describing the use, or the documentation does add value, then it should be omitted.  For example:

/** Foo.
 */
Foo();

int i; /**< an integer */

Documentation

Write “self-documenting” code. Ultimately, “the code is the documentation” works for those familiar with the code base for a project. So the code should be organized and readable. Comments should also be provided to help those that are new to the project.

A documentation generator can be worthwhile for finished project. Doxygen is an excellent documentation generator that works well with C. The markup syntax works well with the C language, and big improvements to documentation can be made with a little change in commenting. The Doxygen manual is the best reference for the markup syntax. I tend to use one markup the most often.

/* This is a C comment. */

/** This is a Doxygen comment for Foo(). */
void Foo();

int bar; /**< This is a Doxygen comment for bar. */

C89 vs C99

If C99 is available, and usable, for your project, then I prefer the C99 standard.

COMMENTS

For comments that are documentation, I prefer to always use the /* */ style.  For temporary comments, I will use the // style.

VARIABLE TYPE DECLARATION

When declaring a variable, I prefer to declare it when it is first used.  I feel this improves readability.  This is only possible when using C99.

Conclusion

I hope you have found this C style guide useful.  I’ve found the style presented here to be particularly effective with test-driven development.

This guide may be updated, enhanced, or changed at any time.  If you have any constructive suggestions or critiques, please leave a comment.  Who knows, if you are lucky, it might become part of the “official” guide ;-)

I’ll leave the comments on as long as spam doesn’t become a problem.

* UPDATE: The spam bots are feeding on this post, so I have disabled comments. :-(