Recommended C code style and coding rules for standard C99 or later
Recommended C code style and coding rules for standard C99 or later
This document describes C code style used by Tilen MAJERLE in his projects and libraries.
Let's start with the quote from GNOME developer site.
The single most important rule when writing code is this: check the surrounding code and try to imitate it.
As a maintainer it is dismaying to receive a patch that is obviously in a different coding style to the surrounding code. This is disrespectful, like someone tromping into a spotlessly-clean house with muddy shoes.
So, whatever this document recommends, if there is already written code and you are patching it, keep its current style consistent even if it is not your favorite style.
Here are listed most obvious and important general rules. Please check them carefully before you continue with other chapters.
C99 standard4 spaces per indent level1 space between keyword and opening bracket/* OK */
if (condition)
while (condition)
for (init; condition; step)
do {} while (condition)
/* Wrong */
if(condition)
while(condition)
for(init;condition;step)
do {} while(condition)int32_t a = sum(4, 3); /* OK */
int32_t a = sum (4, 3); /* Wrong */__ or _ prefix for variables/functions/macros/types. This is reserved for C language itselfprv_ name prefix for strictly module-private functions_ charfor, while, do, switch, if, ...)size_t i;
for (i = 0; i
bool status = true;true, eg. if (check_func() == 1), use if (check_func()) { ... }NULL valuevoid* ptr;
/* ... */
/* OK, compare against NULL */
if (ptr == NULL || ptr != NULL) {
}
/* Wrong */
if (ptr || !ptr) {
}int32_t a = 0;
...
a++; /* Wrong */
++a; /* OK */
for (size_t j = 0; j
void
my_func(size_t size) {
int32_t* arr;
arr = malloc(sizeof(*arr) * n); /* OK, Allocate memory */
arr = malloc(sizeof *arr * n); /* Wrong, brackets for sizeof operator are missing */
if (arr == NULL) {
/* FAIL, no memory */
}
free(arr); /* Free memory after usage */
}
/* Wrong */
void
my_func(size_t size) {
int32_t arr[size]; /* Wrong, do not use VLA */
}boolean typeboolean-treated variables against zero or one. Use NOT (!) instead…/* comment */ for comments, even for single-line commentC++ with extern keyword in header filestaticforce_redraw. Do not use forceRedrawvoid *, eg. uint8_t* ptr = (uint8_t *)func_returning_void_ptr(); as void * is safely promoted to any other pointer typeuint8_t* ptr = func_returning_void_ptr(); instead#include "" for custom libraries, eg. #include "my_library.h"uint8_t* t = (uint8_t*)var_width_diff_type// are not allowed. Always use /* comment */, even for single-line comment//This is comment (wrong)
/* This is comment (ok) */space+asterisk for every line/*
* This is multi-line comments,
* written in 2 lines (ok)
*/
/**
* Wrong, use double-asterisk only for doxygen documentation
*/
/*
* Single line comment without space before asterisk (wrong)
*/
/*
* Single line comment in multi-line configuration (wrong)
*/
/* Single line comment (ok) */12 indents (12 * 4 spaces) offset when commenting. If statement is larger than 12 indents, make comment 4-spaces aligned (examples below) to next available indentvoid
my_func(void) {
char a, b;
a = call_func_returning_char_a(a); /* This is comment with 12*4 spaces indent from beginning of line */
b = call_func_returning_char_a_but_func_name_is_very_long(a); /* This is comment, aligned to 4-spaces indent */
}_ character/* OK */
void my_func(void);
void myfunc(void);
/* Wrong */
void MYFunc(void);
void myFunc();/* OK */
const char* my_func(void);
my_struct_t* my_func(int32_t a, int32_t b);
/* Wrong */
const char *my_func(void);
my_struct_t * my_func(void);/* OK, function names aligned */
void set(int32_t a);
my_type_t get(void);
my_ptr_t* get_ptr(void);
/* Wrong */
void set(int32_t a);
const char * get(void);/* OK */
int32_t
foo(void) {
return 0;
}
/* OK */
static const char*
get_string(void) {
return "Hello world!\r\n";
}
/* Wrong */
int32_t foo(void) {
return 0;
}_ character/* OK */
int32_t a;
int32_t my_var;
int32_t myvar;
/* Wrong */
int32_t A;
int32_t myVar;
int32_t MYVar;typevoid
foo(void) {
int32_t a, b; /* OK */
char a;
char b; /* Wrong, char type already exists */
}void
foo(void) {
int32_t a;
a = bar();
int32_t b; /* Wrong, there is already executable statement */
}int32_t a, b;
a = foo();
if (a) {
int32_t c, d; /* OK, c and d are in if-statement scope */
c = foo();
int32_t e; /* Wrong, there was already executable statement inside block */
}/* OK */
char* a;
/* Wrong */
char *a;
char * a;/* OK */
char *p, *n;_ character between wordstypedef keywordWhen structure is declared, it may use one of 3 different options:
_t suffix after its name.struct struct_name {
char* a;
char b;
};_t suffix after its name.typedef struct {
char* a;
char b;
} struct_name_t;_t for basic name and it has to contain _t suffix after its name for typedef part.typedef struct struct_name {
char* a;
char b;
char c;
} struct_name_t;Examples of bad declarations and their suggested corrections
/* a and b must be separated to 2 lines */
/* Name of structure with typedef must include _t suffix */
typedef struct {
int32_t a, b;
} a;
/* Corrected version */
typedef struct {
int32_t a;
int32_t b;
} a_t;
/* Wrong name, it must not include _t suffix */
struct name_t {
int32_t a;
int32_t b;
};
/* Wrong parameters, must be all uppercase */
typedef enum {
MY_ENUM_TESTA,
my_enum_testb,
} my_enum_t;C99 initialization style/* OK */
a_t a = {
.a = 4,
.b = 5,
};
/* Wrong */
a_t a = {1, 2};_fn suffix/* Function accepts 2 parameters and returns uint8_t */
/* Name of typedef has `_fn` suffix */
typedef uint8_t (*my_func_typedef_fn)(uint8_t p1, const char* p2);1 nested statement1 indent size for each nest/* OK */
if (c) {
do_a();
} else {
do_b();
}
/* Wrong */
if (c)
do_a();
else
do_b();
/* Wrong */
if (c) do_a();
else do_b();if or if-else-if statement, else must be in the same line as closing bracket of first statement/* OK */
if (a) {
} else if (b) {
} else {
}
/* Wrong */
if (a) {
}
else {
}
/* Wrong */
if (a) {
}
else
{
}do-while statement, while part must be in the same line as closing bracket of do part/* OK */
do {
int32_t a;
a = do_a();
do_b(a);
} while (check());
/* Wrong */
do
{
/* ... */
} while (check());
/* Wrong */
do {
/* ... */
}
while (check());if (a) {
do_a();
} else {
do_b();
if (c) {
do_c();
}
}if (a) do_b();
else do_c();
if (a) do_a(); else do_b();while, do-while or for loops must include brackets/* OK */
while (is_register_bit_set()) {}
/* Wrong */
while (is_register_bit_set());
while (is_register_bit_set()) { }
while (is_register_bit_set()) {
}while (or for, do-while, etc) is empty (it can be the case in embedded programming), use empty single-line brackets…#ifdef or #ifndef. Use defined() or !defined() instead#ifdef XYZ
/* do something */
#endif /* XYZ */if/elif/else/endif statements/* OK */
#if defined(XYZ)
/* Do if XYZ defined */
#else /* defined(XYZ) */
/* Do if XYZ not defined */
#endif /* !defined(XYZ) */
/* Wrong */
#if defined(XYZ)
/* Do if XYZ defined */
#else
/* Do if XYZ not defined */
#endif#if statement/* OK */
#if defined(XYZ)
#if defined(ABC)
/* do when ABC defined */
#endif /* defineNo open issues yet, or sync has not completed.