Version: 0.1.0

Disclaimer: This post is a “living post”. That means it will be updated from time to time. The version will be increased with every new update and a changelog at the end of the post indicates what has changed.

General

Over the last years I wrote several programs in the C language. During that period I tried a lot of coding styles until I found the one which works best for me.

In this blog post I describe the most important constructs for programming clean C. There are more constructs that are used less frequent. I intend to describe those in future.

I use at least the C99 standard. It brings some possibilities that are neat to have. You will see later.

Lines

I prefer a line size of not more than 80 columns. This size is suitable to fit on all screen sizes and enables the reader to recognize the meaning of the source code more easily. The reader doesn’t have to move his eyes to much from left to right, which helps him to stay concentrated and tire more slowly. 80 characters also help you to think about your variable and function naming and helps you to reflect on your code complexity.

Indentation

I don’t use tabs to indent my code. Instead I use 4 spaces. This has the advantage that only one type of whitespace is used and again the movement of the eyes is reduced. You should also make sure that you remove all trailing whitespace.

A pattern I really often see is double empty lines. Instead of using double empty lines to delimit logical parts of the source I prefer one empty line and no empty line for related parts of the source.

One special case of indentation is the switch statement. Because I use 4 spaces to indent my code I also indent the cases inside of the switch. That leads to a better overview of what’s happening.

switch (c) {
    case 1:
        break;
    case 2:
        break;
    default:
        // anything
}

Braces

In my coding style there are two types of brace alignments. One for function definitions and one for control statements. For a function definition the opening brace will be placed in a new line after the function prototype.

int f(int i)
{
    return i;
}

For statements like if, switch, for, while and do the opening brace follows the statement in the same line.

if (a == b) {
    // something
}

for (int i = 0; i < 10; i++) {
    // something
}

while (42) {
    // anything
}

In most cases the closing brace is located on it’s own new line, except two exceptions. A do-while loop and multiple connected if-else statements.

do {
    // anything
} while (42);

if (a == b) {
    // something
} else {
    // some other thing
} else if (a == c) {
    // some third thing
}

I always use braces! Telling you from my experience, there is always a case where missing braces or broken indentation create unforeseen behavior. This cannot happen, if you always use braces for your loops and branch statements.

Spaces

Spaces are a very important part of my coding style. They have to be consistent. In the following paragraphs I will describe how spaces have to be used.

Use a space after if, switch, case, for, do, while. This delimits the type of the control structure from it’s definition and you can immediately recognize what’s going on.

Don’t use a space after sizeof, typeof. They represent something similar to a function with a return value and should be treated as such.

int *i = malloc(8 * sizeof(int));

Don’t use spaces inside of parenthesized expressions. The following is bad:

if ( a == b ) {
    // something
}

a = f( 3 );

For pointers there are many correct reasons why one should set the space at one specific position or another. Here is my interpretation.

If you create a pointer-variable you should write the asterisk (*) in front of the identifier of the variable, without a space. You should set a space between the type of the variable and the asterisk. This makes sense, because you can see from the identifier that you are dealing with a pointer variable which handles a specific type. By the way: Always initialize with NULL when defining a pointer variable.

int *a = NULL;

For casts yo do the same: place the asterisk as close as possible to the identifier.

char *c = (char *)a;

If you’re casting a pointer to a pointer, write both asterisks next to the identifier and also don’t use spaces around the ampersand &. This conserves the context for the variable in all situations.

char **ch = (char **)&a;

For defining arrays use the same method as written above.

int a[8];

int *a = malloc(8 * sizeof(int));

char *s[8];

char **s = malloc(8 * sizeof(int *));

Operators should be surrounded by spaces.

=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  : += -= *= /=

When using unary operators omit the space between the operator and the identifier.

&  *  +  -  ~  !

Also no space when using post- or pre-in/decrement.

++ --

Naming

Name you variables, constants and functions with underscores as delimiters for multiple words. Also start the names with a lower case.

int named_variable;

int this_is_a_function();

Typedefs

Don’t use! DON’T!

Functions

When defining functions in header files, all parameters should have a name.

void func(int count, char *buffer);

Comments

Only use C89 style comments.

/* comment */

/*
 * multi line
 * comment
 */

/** Doxygen comment */

/**
 * Doxygen multi line
 * comment
 */

/**< post comment for members */

Macros and Enumerations

One macro you should definitely should use is an include guard.

#ifndef FILE_H
#define FILE_H

// CODE

#endif

Don’t use #define to define constants, instead use a const variable in the global scope.

const int CONSTANT = 42;

From time to time you need to model states. Instead of using #defines to create literate states, use an enumeration.

enum state {
    OK = 1,
    ERROR
};

The enumeration provides the compiler with the ability of type checking, which leads to cleaner and safer code.

Return values

Last but not least, return values of functions should always be consistent. That is important, because you can always check the return values of a function in the same way, without thinking about what value defines success and what a failure.

For functions that return a number values you should return 0 for success and a value different from 0 for a failure.

If your function returns a pointer NULL should be used to indicate an error.

Style automation

To automatically apply parts of my coding style you can use the tool astyle with the following parameters:

$ astyle --style=kr --indent=spaces=4 --indent-switches --pad-oper --pad-header --align-pointer=name --add-brackets -n <file>

Or the short form:

$ astyle -A3s4SpHk3jn <file>