Embedded C Coding Standards

Embedded C Coding Standards

Introduction: This article analyzes the embedded C coding standards shared by a foreigner on GitHub (recommended for careful reading): Embedded Mixed Bag.Keywords Embedded, C statements, Programming standards

  • Sharing the embedded C coding standards of a foreigner on GitHub (recommended for careful reading)

    [1]

01 Most Important Rules

  The most important rule when writing code is: check the surrounding code and try to mimic it.

  As a maintainer, it is frustrating when a patch you receive is clearly different in coding style from the surrounding code. This is disrespectful, just like someone walking into a spotless house with muddy shoes.

  Therefore, whatever this article recommends, if code has already been written and you are patching it, keep its current style consistent, even if it is not your favorite style.

Embedded C Coding Standards

02 General Rules

  Here are the most obvious and important general rules. Please check them carefully before you continue reading other sections.

  • Use C99 standard
  • Do not use tabs, use spaces instead
  • Use 4 spaces for each indentation level
  • Use one space between keywords and left parentheses
  • Do not use space between function names and left parentheses
int32_t a = sum(4, 3);              /* OK */
int32_t a = sum (4, 3);             /* Wrong */
  • Do not use _ or prefixes in variable / function / macro / type. This is reserved for the C language itself
  • For strictly private module functions, use prv_ name prefix
  • For variables / functions / macros / types that contain the underscore _, only lowercase letters can be used
  • The left curly brace is always on the same line as the 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 a single space before and after comparison operators 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 a single space after each comma
func_name(5, 4);        /* OK */
func_name(4,3);         /* Wrong */
  • Do not initialize static and global variables to 0( or NULL), let the compiler do it for you
static int32_t a;       /* OK */
static int32_t b = 4;   /* OK */
static int32_t a = 0;   /* Wrong */

void my_func(void) {
    static int32_t* ptr;/* OK */
    static char abc = 0;/* Wrong */
}
Embedded C Coding Standards
  • Declare all local variables of the same type on the same line
void my_func(void) {
    char a;             /* OK */
    char b;             /* Wrong, variable with char type already exists */
    char a, b;          /* OK */
}
  • Declare local variables in order

  i. Custom structures and enums

  ii. Integer types, wider unsigned types first

  iii. 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 a block, before the first executable statement
  • Declare counter variables in for loops
/* 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 using function calls to assign values to variables in declarations, 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;
}
  • Always use types declared in the stdint.h standard library except for char, float, or double. For example, 8 bit uint8_t, etc.
  • Do not use stdbool.h library. Use 1 or 0 to represent true or false
/* OK */
uint8_t status;
status = 0;

/* Wrong */
#include <stdbool.h>
bool status = true;
Embedded C Coding Standards
  • Never compare with real values. For example, use if(check_func()){…} instead of if (check_func() * 1)
  • Always compare pointers against null
void* ptr;

/* ... */

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

}

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

}
  • Always use pre-increment (and pre-decrement), not post-increment (and post-decrement)
int32_t a = 0;
...

a++;            /* Wrong */
++a;            /* OK */

for (size_t j = 0; j < 10; ++j) {}  /* OK */
  • Always use size_t for length or size variables
  • Always use const for pointers if the function should not modify the memory pointed to
  • Always use const if the function’s parameters or variables 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 a function can accept any type of pointer, always use void *, do not use uint8_t *. The function must be aware of the correct type conversion during 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 */
}
Embedded C Coding Standards
  • Always use parentheses with sizeof operator
  • Do not use variable length arrays. Use dynamic memory allocation instead of standard C malloc and free functions, or if the library / project provides custom memory allocation, use its implementation like LwMEM, a 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 with 0 unless it is treated as a boolean type
  • Never compare boolean treated variables with 0 or 1. 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 */
  • For comments, always use /* comment */, even for single line comments
  • Always include extern checks with c++ in header files
  • Every function must include doxygen-enabled comments, even if the function is static
  • Use English names / text for functions, variables, comments
  • Variables should use lowercase letters
  • If a variable contains multiple names, use underscores. force_redraw. Do not use forceRedraw
  • For include files from the C standard library, always use < and >. For example, #include <stdlib.h>
  • For custom libraries, always use "". For example, #include "my_library.h"
  • When converting to pointer types, always align the asterisk with the type, for example. uint8_t* t = (uint8_t*)var_width_diff_type
  • Always respect the coding style already used in the project or library
Embedded C Coding Standards

03 Comment Related Rules

  • Comments starting with // are not allowed. Always use /* comment */, even for single line comments
  • For multi-line comments, use space + asterisk for each 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) */
  • When commenting, use 12 spaces (12 * 4 spaces) indentation. If the statement is greater than 12 spaces, align the comment to 4 spaces (as shown below) to the next available indentation
void 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 */
}
Embedded C Coding Standards

04 Function Definition Rules

  • Every function that can be accessed from outside the module must include a function prototype (or declaration)
  • Function names must be lowercase and can be separated by underscores. (This principle seems to vary from person to person)
/* OK */
void my_func(void);
void myfunc(void);

/* Wrong */
void MYFunc(void);
void myFunc();
  • When a function returns a pointer, align the asterisk with the return type
/* 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);
  • Align all function prototypes (using the same/similar functions) for better readability
/* 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);
  • Function implementations must contain return type and optional other keywords on separate lines
/* 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;
}
Embedded C Coding Standards

05 Variable Related Rules

  • Make variable names all lowercase, underscores _ are optional
/* OK */
int32_t a;
int32_t my_var;
int32_t myvar;

/* Wrong */
int32_t A;
int32_t myVar;
int32_t MYVar;
  • Group local variables by type together
void foo(void) {
    int32_t a, b;   /* OK */
    char a;
    char b;         /* Wrong, char type already exists */
}
  • Do not declare variables after the first executable statement
void foo(void) {
    int32_t a;
    a = bar();
    int32_t b;      /* Wrong, there is already executable statement */
}
  • You can declare new variables at the next indentation level
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 */
}
  • Align pointer variable declarations with type using asterisks
/* OK */
char* a;

/* Wrong */
char *a;
char * a;
  • When declaring multiple pointer variables, you can declare the asterisk with the variable names
/* OK */
char *p, *n;
Embedded C Coding Standards

06 Structure, Enum Type Definition

  • Structure names or enum names must be lowercase, with underscores _ between words
  • Structures or enums can contain typedef keyword
  • All structure members must be lowercase
  • All enum members must be uppercase
  • Structures / enums must follow doxygen documentation syntax
  • When declaring a structure, it can use one of the following three options:

  1. When a structure is declared only with a name, it should not contain the _t suffix after its name.

struct struct_name {
    char* a;
    char b;
};

  2. When a structure is declared only using typedef, it must contain the _t suffix after its name.

typedef struct {
    char* a;
    char b;
} struct_name_t;

  3. When a structure is declared with name and typedef, it must not contain t as the base name, and it must contain t suffix after its name as the typedef part.

typedef struct struct_name {
    char* a;
    char b;
    char c;
} struct_name_t;
  • Examples of incorrectly declared structures 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;
  • When initializing a structure at the time of declaration, use C99 initialization style
/* OK */
a_t a = {
    .a = 4,
    .b = 5,
};

/* Wrong */
a_t a = {1, 2};
  • When introducing new typedef for function handles, use the _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);
Embedded C Coding Standards

07 Compound Statement Rules

  • Each compound statement must include left and right curly braces, even if it contains only 1 nested statement
  • Each compound statement must contain a single indentation; for nested statements, each nested contains 1 indentation size
/* OK */
if (c) {
    do_a();
} else {
    do_b();
}

/* Wrong */
if (c)
do_a();
else
do_b();

/* Wrong */
if (c) do_a();
else do_b();
  • In the case of if or ifelseif statements, else must be on the same line as the right brace of the first statement
/* OK */
if (a) {

} else if (b) {

} else {

}

/* Wrong */
if (a) {

}
else {

}

/* Wrong */
if (a) {

}
else
{

}
  • In the case of do-while statements, the while part must be on the same line as the right brace of the do part
/* OK */
do {
    int32_t a;
    a = do_a();
    do_b(a);
} while (check());

/* Wrong */
do
{
/* ... */
} while (check());

/* Wrong */
do {
/* ... */
}
while (check());
Embedded C Coding Standards
  • Every opening brace needs indentation
if (a) {
    do_a();
} else {
    do_b();
    if (c) {
        do_c();
    }
}
  • Do not make compound statements without curly braces, even for single statements. The following examples show some bad practices
if (a) do_b();
else do_c();

if (a) do_a(); else do_b();
  • Empty while loops, do-while loops, or for loops must include curly braces
/* OK */
while (is_register_bit_set()) {}

/* Wrong */
while (is_register_bit_set());
while (is_register_bit_set()) { }
while (is_register_bit_set()) {
}
  • If while (or for, do-while, etc.) is empty (which may also happen in embedded programming), use empty single-line braces
/* Wait for bit to be set in embedded hardware unit
uint32_t* addr = HW_PERIPH_REGISTER_ADDR;

/* Wait bit 13 to be ready */
while (*addr &amp; (1 &lt;&lt; 13)) {}        /* OK, empty loop contains no spaces inside curly brackets */
while (*addr &amp; (1 &lt;&lt; 13)) { }       /* Wrong */
while (*addr &amp; (1 &lt;&lt; 13)) {         /* Wrong */
}
while (*addr &amp; (1 &lt;&lt; 13));          /* Wrong, curly brackets are missing. Can lead to compiler warnings or unintentional bugs */
  • Avoid incrementing variables within loop blocks, see examples
/* Not recommended */
int32_t a = 0;
while (a &lt; 10) {
    .
    ..
    ...
    ++a;
}

/* Better */
for (size_t a = 0; a &lt; 10; ++a) {

}

/* Better, if increment may not happen in every cycle */
for (size_t a = 0; a &lt; 10; ) {
    if (...) {
        ++a;
    }
}
Embedded C Coding Standards

08 Branch Statement Rules

  • Add a single indentation for each case statement
  • Use an extra single indentation for break statements in each case or default
/* OK, every case has single indent */
/* OK, every break has additional indent */
switch (check()) {
    case 0:
        do_a();
        break;
    case 1:
        do_b();
        break;
    default:
        break;
}

/* Wrong, case indent missing */
switch (check()) {
case 0:
    do_a();
    break;
case 1:
    do_b();
    break;
default:
    break;
}

/* Wrong */
switch (check()) {
    case 0:
        do_a();
    break;      /* Wrong, break must have indent as it is under case */
    case 1:
    do_b();     /* Wrong, indent under case is missing */
    break;
    default:
        break;
}
  • Always include a default statement
/* OK */
switch (var) {
    case 0:
        do_job();
        break;
    default: break;
}

/* Wrong, default is missing */
switch (var) {
    case 0:
        do_job();
        break;
}
  • If local variables are needed, use curly braces and put break statements inside. Place the left curly brace on the same line as the case statement
switch (a) {
    /* OK */
    case 0: {
        int32_t a, b;
        char c;
        a = 5;
        /* ... */
        break;
    }

    /* Wrong */
    case 1:
    {
        int32_t a;
        break;
    }

    /* Wrong, break shall be inside */
    case 2: {
        int32_t a;
    }
    break;
}
Embedded C Coding Standards

09 Macros and Preprocessing Directives

  • Always use macros instead of literal constants, especially for numbers
  • All macros must be in uppercase, with underscores _ as optional, unless they are explicitly marked as function, which may be replaced by regular function syntax in the future
/* OK */
#define MY_MACRO(x)         ((x) * (x))

/* Wrong */
#define square(x)           ((x) * (x))
  • Always protect input parameters with parentheses
/* OK */
#define MIN(x, y)           ((x) &lt; (y) ? (x) : (y))

/* Wrong */
#define MIN(x, y)           x &lt; y ? x : y
  • Always protect the final macro calculation with parentheses
/* Wrong */
#define MIN(x, y)           (x) &lt; (y) ? (x) : (y)
#define SUM(x, y)           (x) + (y)

/* Imagine result of this equation using wrong SUM implementation */
int32_t x = 5 * SUM(3, 4);  /* Expected result is 5 * 7 = 35 */
int32_t x = 5 * (3) + (4);  /* It is evaluated to this, final result = 19 which is not what we expect */

/* Correct implementation */
#define MIN(x, y)           ((x) &lt; (y) ? (x) : (y))
#define SUM(x, y)           ((x) + (y))
  • When macros use multiple statements, protect them with do-while(0) statements
typedef struct {
    int32_t px, py;
} point_t;
point_t p;                  /* Define new point */

/* Wrong implementation */

/* Define macro to set point */
#define SET_POINT(p, x, y)  (p)-&gt;px = (x); (p)-&gt;py = (y)    /* 2 statements. Last one should not implement semicolon */

SET_POINT(&amp;p, 3, 4);        /* Set point to position 3, 4. This evaluates to... */
(&amp;p)-&gt;px = (3); (&amp;p)-&gt;py = (4); /* ... to this. In this example this is not a problem. */

/* Consider this ugly code, however it is valid by C standard (not recommended) */
if (a)                      /* If a is true */
    if (b)                  /* If b is true */
        SET_POINT(&amp;p, 3, 4);/* Set point to x = 3, y = 4 */
    else
        SET_POINT(&amp;p, 5, 6);/* Set point to x = 5, y = 6 */

/* Evaluates to code below. Do you see the problem? */
if (a)
    if (b)
        (&amp;p)-&gt;px = (3); (&amp;p)-&gt;py = (4);
    else
        (&amp;p)-&gt;px = (5); (&amp;p)-&gt;py = (6);

/* Or if we rewrite it a little */
if (a)
    if (b)
        (&amp;p)-&gt;px = (3);
        (&amp;p)-&gt;py = (4);
    else
        (&amp;p)-&gt;px = (5);
        (&amp;p)-&gt;py = (6);

/*
 * Ask yourself a question: To which `if` statement `else` keyword belongs?
 *
 * Based on first part of code, answer is straight-forward. To inner `if` statement when we check `b` condition
 * Actual answer: Compilation error as `else` belongs nowhere
 */

/* Better and correct implementation of macro */
#define SET_POINT(p, x, y)  do { (p)-&gt;px = (x); (p)-&gt;py = (y); } while (0)    /* 2 statements. No semicolon after while loop */
/* Or even better */
#define SET_POINT(p, x, y)  do {    \   /* Backslash indicates statement continues in new line */
    (p)-&gt;px = (x);                  \
    (p)-&gt;py = (y);                  \
} while (0)                             /* 2 statements. No semicolon after while loop */

/* Now original code evaluates to */
if (a)
    if (b)
        do { (&amp;p)-&gt;px = (3); (&amp;p)-&gt;py = (4); } while (0);
    else
        do { (&amp;p)-&gt;px = (5); (&amp;p)-&gt;py = (6); } while (0);

/* Every part of `if` or `else` contains only `1` inner statement (do-while), hence this is valid evaluation */

/* To make code perfect, use brackets for every if-ifelse-else statements */
if (a) {                    /* If a is true */
    if (b) {                /* If b is true */
        SET_POINT(&amp;p, 3, 4);/* Set point to x = 3, y = 4 */
    } else {
        SET_POINT(&amp;p, 5, 6);/* Set point to x = 5, y = 6 */
    }
}
  • Do not indent sub-statements within #if statements
/* OK */
#if defined(XYZ)
#if defined(ABC)
/* do when ABC defined */
#endif /* defined(ABC) */
#else /* defined(XYZ) */
/* Do when XYZ not defined */
#endif /* !defined(XYZ) */

/* Wrong */
#if defined(XYZ)
    #if defined(ABC)
        /* do when ABC defined */
    #endif /* defined(ABC) */
#else /* defined(XYZ) */
    /* Do when XYZ not defined */
#endif /* !defined(XYZ) */
  • Documented code allows doxygen to parse and generate general html/pdf/latex output, so it is very important to execute it correctly.
  • Use doxygen supported documentation style for variables, functions, and structures / enums
  • Frequently use ackslash as doxygen, do not use @
  • Always use 5x4 spaces (5 tabs ) as the offset for lines starting with text
/**
 * \brief           Holds pointer to first entry in linked list
 *                  Beginning of this text is 5 tabs (20 spaces) from beginning of line
 */
static
type_t* list;
Embedded C Coding Standards
  • Each structure/enumeration member must include documentation
  • Comment indentation should use 12x4 space offsets
/**
 * \brief           This is point struct
 * \note            This structure is used to calculate all point
 *                      related stuff
 */
typedef struct {
    int32_t x;                                  /*!&lt; Point X coordinate */
    int32_t y;                                  /*!&lt; Point Y coordinate */
    int32_t size;                               /*!&lt; Point size.
                                                    Since comment is very big,
                                                    you may go to next line */
} point_t;

/**
 * \brief           Point color enumeration
 */
typedef enum {
    COLOR_RED,                                  /*!&lt; Red color. This comment has 12x4
                                                    spaces offset from beginning of line */
    COLOR_GREEN,                                /*!&lt; Green color */
    COLOR_BLUE,                                 /*!&lt; Blue color */
} point_color_t;
  • Function documentation must be written in the implementation of the function (usually the source file)
  • Functions must include brief and all parameter documentation
  • If each parameter is in or out input and output, it must be noted
  • If a function returns a value, the return parameter must be included. This does not apply to void functions
  • Functions can include other doxygen keywords, such as note or warn
  • Use a colon : between parameter names and descriptions
/**
 * \brief           Sum `2` numbers
 * \param[in]       a: First number
 * \param[in]       b: Second number
 * \return          Sum of input values
 */
int32_t
sum(int32_t a, int32_t b) {
    return a + b;
}

/**
 * \brief           Sum `2` numbers and write it to pointer
 * \note            This function does not return value, it stores it to pointer instead
 * \param[in]       a: First number
 * \param[in]       b: Second number
 * \param[out]      result: Output variable used to save result
 */
void
void_sum(int32_t a, int32_t b, int32_t* result) {
    *result = a + b;
}
  • If a function returns a member of an enumeration, use the ref keyword to specify which member
/**
 * \brief           My enumeration
 */
typedef enum {
    MY_ERR,                                     /*!&lt; Error value */
    MY_OK                                       /*!&lt; OK value */
} my_enum_t;

/**
 * \brief           Check some value
 * \return          \ref MY_OK on success, member of \ref my_enum_t otherwise
 */
my_enum_t
check_value(void) {
    return MY_OK;
}
  • Use symbols for constants or numbers (' NULL ' => NULL)
/**
 * \brief           Get data from input array
 * \param[in]       in: Input data
 * \return          Pointer to output data on success, `NULL` otherwise
 */
const void *
get_data(const void* in) {
    return in;
}
  • Macro documentation must include hideinitializer doxygen command
/**
 * \brief           Get minimal value between `x` and `y`
 * \param[in]       x: First value
 * \param[in]       y: Second value
 * \return          Minimal value between `x` and `y`
 * \hideinitializer
 */
#define MIN(x, y)       ((x) &lt; (y) ? (x) : (y))
Embedded C Coding Standards

10 Header/Source Files

  • Leave an empty line at the end of the file
  • Each file must include a doxygen comment for the file followed by a brief description (when using doxygen)
/**
 * \file            template.h
 * \brief           Template include file
 */
                    /* Here is empty line */
  • Each file (header or source file) must include a license (starting comments include a single asterisk because doxygen must ignore this)
  • Use the same license that is already used in the project/library
/**
 * \file            template.h
 * \brief           Template include file
 */

/*
 * Copyright (c) year FirstName LASTNAME
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * This file is part of library_name.
 *
 * Author:          FirstName LASTNAME &lt;[email protected]&gt;
 */
  • Header files must include guard #ifndef
  • Header files must include c++ checks
  • Include external header files outside of c++ checks
  • First include external header files with STL C, then application custom files
  • Header files must include all other header files for proper compilation, but should not include more header files (if needed, .c should include the remaining header files)
  • Header files must only expose public variables/types/functions of the module
  • Use extern for global module variables in header files, and define them later in source files
/* file.h ... */
#ifndef ...

extern int32_t my_variable; /* This is global variable declaration in header */

#endif

/* file.c ... */
int32_t my_variable;        /* Actually defined in source */
  • Do not include .c files in another .c file
  • .c files should first include their corresponding .h files, then other files, unless explicitly necessary
  • Do not include module private declarations in header files
  • Header file example (license not included in the example)
/* License comes here */
#ifndef TEMPLATE_HDR_H
#define TEMPLATE_HDR_H

/* Include headers */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

/* File content here */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* TEMPLATE_HDR_H */
Embedded C Coding Standards

References

[1]

Sharing the embedded C coding standards of a foreigner on GitHub (recommended for careful reading): https://www.toutiao.com/i6949933402917306893/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1624066549&app=news_article&utm_source=weixin&utm_medium=toutiao_ios&use_new_style=1&req_id=202106190935490101502220423E347B11&share_token=AE95604B-F45F-4E2C-BA98-60094A007147&group_id=6949933402917306893

Leave a Comment

×