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.
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:
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]
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)C11 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 (static) functionslibname_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_ charfor, 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 */
{
}
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 */
func_name(5, 4); /* OK */
func_name(4,3); /* Wrong */
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;
}
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 */
}
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;
}
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}}};
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) ...
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;
}
char, float or double, always use types declared in stdint.h library, eg. uint8_t for unsigned 8-bit, etc.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;
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++; /* */
++a; /* Preferred */
for (size_t j = 0; j < 10; ++j) {} /* OK */
size_t for length or size variablesconst for pointer if function should not modify memory pointed to by pointerconst 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) {
}
void *, do not use uint8_t */*
* 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 */
}
sizeof operatormalloc and free functions or if library/project provides custom memory allocation, use its implementation/* 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 */
}
boolean typeboolean-treated variables against zero or one. Use NOT (!) insteadsize_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 */
/* 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< and > for C Standard Library include files, eg. #include <stdlib.h>"" 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
```c
/*$ claude mcp add c-code-style \
-- python -m otcore.mcp_server <graph>