MCPcopy Index your code
hub / github.com/MaJerle/c-code-style

github.com/MaJerle/c-code-style @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
1 symbols 1 edges 2 files 1 documented · 100%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Recommended C style and coding rules

This document describes C code style used by Tilen MAJERLE in his projects and libraries.

Table of Contents

The single most important rule

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.

Integration with VSCode

VScode comes with pre-installed clang-format tool (part of LLVM package) that has been design to help developers with auto-format tool during code development.

As such, it allows users to format code on file change (and save). When file is saved, vscode will try to invoke the clang-format and format the code. Rules to use are in .clang-format file. If clang-format cannot find the rules in the path of current file, it will go all the way up to the root, until one is found. If still none is available, default rules are then being used.

This repository contains always up-to-date .clang-format file with rules matching explained ones. You can place the folder in the root or your project or even in the root of your software development projects -> use one file for all!

Some configurations shall be enabled: VSCode configuration

Conventions used

The keywords MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, and OPTIONAL in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174]

General rules

Here are listed most obvious and important general rules. Please check them carefully before you continue with other chapters.

  • clang-format SHOULD be used with formatting file attached to this repository (version 15.x is a minimum)
  • Use C11 standard
  • Do not use tabs, use spaces instead
  • Use 4 spaces per indent level
  • Use 1 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)
  • Do not use space between function name and opening bracket
int32_t a = sum(4, 3);              /* OK */
int32_t a = sum (4, 3);             /* Wrong */
  • Never use __ or _ prefix for variables/functions/macros/types. This is reserved for C language itself
    • Prefer prv_ name prefix for strictly module-private (static) functions
    • Prefer libname_int_ or libnamei_ prefix for library internal functions, that should not be used by the user application while they MUST be used across different library internal modules
  • Use only lowercase characters for variables/functions/types with optional underscore _ char
  • Opening curly bracket is always at the same line as keyword (for, while, do, switch, if, ...)
size_t i;
for (i = 0; i < 5; ++i) {           /* OK */
}
for (i = 0; i < 5; ++i){            /* Wrong */
}
for (i = 0; i < 5; ++i)             /* Wrong */
{
}
  • Use single space before and after comparison and assignment operators
int32_t a;
a = 3 + 4;              /* OK */
for (a = 0; a < 5; ++a) /* OK */
a=3+4;                  /* Wrong */
a = 3+4;                /* Wrong */
for (a=0;a<5;++a)       /* Wrong */
  • Use single space after every comma
func_name(5, 4);        /* OK */
func_name(4,3);         /* Wrong */
  • Do not initialize global variables to any default value (or NULL), implement it in the dedicated init function (if REQUIRED).
static int32_t a;       /* Wrong */
static int32_t b = 4;   /* Wrong */
static int32_t a = 0;   /* Wrong */

In embedded systems, it is very common that RAM memories are scattered across different memory locations in the system. It quickly becomes tricky to handle all the cases, especially when user declares custom RAM sections. Startup script is in-charge to set default values (.data and .bss) while other custom sections may not be filled with default values, which leads to variables with init value won't have any effect.

To be independent of such problem, create init function for each module and use it to set default values for all of your variables, like so:

static int32_t a;       /* OK */
static int32_t b = 4;   /* Wrong - this value may not be set at zero 
                            if linker script&startup files are not properly handled */

void
my_module_init(void) {
    a = 0;
    b = 4;
}
  • Declare all local variables of the same type in the same line
void
my_func(void) {
    /* 1 */
    char a;             /* OK */

    /* 2 */
    char a, b;          /* OK */

    /* 3 */
    char a;
    char b;             /* Wrong, variable with char type already exists */
}
  • Declare local variables in order
    1. Custom structures and enumerations
    2. Integer types, wider unsigned type first
    3. Single/Double floating point
int
my_func(void) {
    /* 1 */
    my_struct_t my;     /* First custom structures */
    my_struct_ptr_t* p; /* Pointers too */

    /* 2 */
    uint32_t a;
    int32_t b;
    uint16_t c;
    int16_t g;
    char h;
    /* ... */

    /* 3 */
    double d;
    float f;
}
  • Always declare local variables at the beginning of the block, before first executable statement
  • Always add trailing comma in the last element of structure (or its children) initialization (this helps clang-format to properly format structures). Unless structure is very simple and short
typedef struct {
    int a, b;
} str_t;

str_t s = {
    .a = 1,
    .b = 2,   /* Comma here */
}

/* Examples of "complex" structure, with or with missing several trailing commas, after clang-format runs the formatting */
static const my_struct_t my_var_1 = {
    .type = TYPE1,
    .type_data =
        {
            .type1 =
                {
                    .par1 = 0,
                    .par2 = 1, /* Trailing comma here */
                }, /* Trailing comma here */
        },  /* Trailing comma here */
};

static const my_struct_t my_var_2 = {.type = TYPE2,
                                     .type_data = {
                                         .type2 =
                                             {
                                                 .par1 = 0,
                                                 .par2 = 1,
                                             },
                                     }};    /* Missing comma here */
static const my_struct_t my_var_3 = {.type = TYPE3,
                                     .type_data = {.type3 = {
                                                       .par1 = 0,
                                                       .par2 = 1,
                                                   }}}; /* Missing 2 commas here */

/* No trailing commas - good only for small and simple structures */
static const my_struct_t my_var_4 = {.type = TYPE4, .type_data = {.type4 = {.par1 = 0, .par2 = 1}}};
  • Declare counter variables in for loop
/* OK */
for (size_t i = 0; i < 10; ++i)

/* OK, if you need counter variable later */
size_t i;
for (i = 0; i < 10; ++i) {
    if (...) {
        break;
    }
}
if (i == 10) {

}

/* Wrong */
size_t i;
for (i = 0; i < 10; ++i) ...
  • Avoid variable assignment with function call in declaration, except for single variables
void
a(void) {
    /* Avoid function calls when declaring variable */
    int32_t a, b = sum(1, 2);

    /* Use this */
    int32_t a, b;
    b = sum(1, 2);

    /* This is ok */
    uint8_t a = 3, b = 4;
}
  • Except char, float or double, always use types declared in stdint.h library, eg. uint8_t for unsigned 8-bit, etc.
  • Do not use stdbool.h library. Use 1 or 0 for true or false respectively
/* OK */
uint8_t status;
status = 0;

/* Wrong */
#include <stdbool.h>
bool status = true;
  • Never compare against true, eg. if (check_func() == 1), use if (check_func()) { ... }
  • Always compare pointers against NULL value
void* ptr;

/* ... */

/* OK, compare against NULL */
if (ptr == NULL || ptr != NULL) {

}

/* Wrong */
if (ptr || !ptr) {

}
  • Prefer the use of pre-increment (and decrement respectively) instead of post-increment (and decrement respectively) whenever possible
int32_t a = 0;
...

a++;            /*  */
++a;            /* Preferred */

for (size_t j = 0; j < 10; ++j) {}  /* OK */
  • Always use size_t for length or size variables
  • Always use const for pointer if function should not modify memory pointed to by pointer
  • Always use const for function parameter or variable, if it should not be modified

/* When d could be modified, data pointed to by d could not be modified */
void
my_func(const void* d) {

}

/* When d and data pointed to by d both could not be modified */
void
my_func(const void* const d) {

}

/* Not REQUIRED, it is advised */
void
my_func(const size_t len) {

}

/* When d should not be modified inside function, only data pointed to by d could be modified */
void
my_func(void* const d) {

}
  • When function may accept pointer of any type, always use void *, do not use uint8_t *
    • Function MUST take care of proper casting in implementation
/*
 * To send data, function should not modify memory pointed to by `data` variable
 * thus `const` keyword is important
 *
 * To send generic data (or to write them to file)
 * any type may be passed for data,
 * thus use `void *`
 */
/* OK example */
void
send_data(const void* data, size_t len) { /* OK */
    /* Do not cast `void *` or `const void *` */
    const uint8_t* d = data;/* Function handles proper type for internal usage */
}

void
send_data(const void* data, int len) {    /* Wrong, not not use int */
}
  • Always use brackets with sizeof operator
  • Never use Variable Length Array (VLA). Use dynamic memory allocation instead with standard C malloc and free functions or if library/project provides custom memory allocation, use its implementation
    • Take a look at LwMEM, custom memory management library
/* OK */
#include <stdlib.h>
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 */
}
  • Always compare variable against zero, except if it is treated as boolean type
  • Never compare boolean-treated variables against zero or one. Use NOT (!) instead
size_t length = 5;  /* Counter variable */
uint8_t is_ok = 0;  /* Boolean-treated variable */
if (length)         /* Wrong, length is not treated as boolean */
if (length > 0)     /* OK, length is treated as counter variable containing multi values, not only 0 or 1 */
if (length == 0)    /* OK, length is treated as counter variable containing multi values, not only 0 or 1 */

if (is_ok)          /* OK, variable is treated as boolean */
if (!is_ok)         /* OK, -||- */
if (is_ok == 1)     /* Wrong, never compare boolean variable against 1! */
if (is_ok == 0)     /* Wrong, use ! for negative check */
  • Always use /* comment */ for comments, even for single-line comment
  • Always include check for C++ with extern keyword in header file
  • Every function MUST include doxygen-enabled comment, even if function is static
  • Use English names/text for functions, variables, comments
  • Use lowercase characters for variables
  • Use underscore if variable contains multiple names, eg. force_redraw. Do not use forceRedraw
  • Never cast function returning void *, eg. uint8_t* ptr = (uint8_t *)func_returning_void_ptr(); as void * is safely promoted to any other pointer type
    • Use uint8_t* ptr = func_returning_void_ptr(); instead
  • Always use < and > for C Standard Library include files, eg. #include <stdlib.h>
  • Always use "" for custom libraries, eg. #include "my_library.h"
  • When casting to pointer type, always align asterisk to type, eg. uint8_t* t = (uint8_t*)var_width_diff_type
  • Always respect code style already used in project or library

Comments

  • Comments starting with // are not allowed. Always use /* comment */, even for single-line comment
//This is comment (wrong)
/* This is comment (ok) */
  • For multi-line comments use space+asterisk for every line ```c /*
  • This

Core symbols most depended-on inside this repo

sum
called by 0
template.c

Shape

Function 1

Languages

C100%

Modules by API surface

template.c1 symbols

For agents

$ claude mcp add c-code-style \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact