Embedded Programming Standards from an Expert’s Perspective

Embedded Programming Standards from an Expert's Perspective

Today, I found a coding standard to share with everyone, which can help avoid many bugs.

Introduction: This article analyzes the embedded C coding standards shared by a foreign expert on GitHub (recommended for careful reading): A Mixed Bag of Embedded Systems.Keywords: Embedded, C Statements, Coding Standards

  • Sharing the embedded C coding standards from a foreign expert on GitHub (recommended for careful reading)[1]

01 The Most Important Rule

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

  As a maintainer, it is frustrating to receive patches that are clearly different in coding style from the surrounding code. This is disrespectful, like someone walking into a spotless house with muddy shoes.

  Therefore, regardless of what is recommended in this article, if you are patching existing code, please keep its current style consistent, even if it is not your preferred style.

02 General Rules

  Here are the most obvious and important general rules. Please review them carefully before proceeding to other sections.

  • Use the <span>C99</span> standard
  • Do not use tabs; use spaces instead
  • Use <span>4</span> spaces for each indentation level
  • Use one space between keywords and left parentheses
  • Do not use spaces between function names and left parentheses
int32_t a = sum(4, 3);              /* OK */
int32_t a = sum (4, 3);             /* Wrong */
  • Do not use <span>_</span> or prefixes in variable<span>/</span> function<span>/</span> macro<span>/</span> type names. This is reserved for the <span>C</span> language itself
  • For strictly private module functions, use the <span>prv</span>_ <span>name</span> prefix
  • For variables<span>/</span> functions<span>/</span> macros<span>/</span> types that include underscores<span>_</span>, only use lowercase letters
  • The left curly brace is always on the same line as the keyword<span>(for,</span> <span>while,</span> <span>do,</span> <span>switch,</span> <span>if</span>,…<span>)</span>
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 <span>0(</span> or <span>NULL)</span><code><span>, let the compiler do it for you</span>
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 */
}
  • 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 enumerations

  ii. Integer types, wider unsigned types first

  iii. Single/double floating points

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 <span>stdint.h</span> standard library, except for <span>char</span>, <span>float</span>, or <span>double</span>. For example, use <span>uint8_t</span> for 8-bit unsigned integers
  • Do not use the <span>stdbool.h</span> library. Use <span>1</span> or <span>0</span> to represent true or false
/* OK */
uint8_t status;
status = 0;

/* Wrong */
#include <stdbool.h>
bool status = true;
  • Never compare with true. For example, use <span>if(check_func()){…}</span> instead of <span>if (check_func() * 1)</span>
  • 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 decrement), not post-increment (and decrement)
int32_t a = 0;
...

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

for (size_t j = 0; j < 10; ++j) {}  /* OK */
  • Always use <span>size_t</span> for length or size variables
  • If a function should not modify the memory pointed to by a pointer, always use <span>const</span> for the pointer
  • If a function’s parameters or variables should not be modified, always use <span>const</span>
/* 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 <span>void</span> *, do not use <span>uint8_t</span> *. The function must pay attention to correct type casting 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, do not use int */
}
  • Always use parentheses with the <span>sizeof</span> operator
  • Do not use variable-length arrays. Use dynamic memory allocation instead with the standard <span>C</span> <span>malloc</span> and free functions, or if the library<span>/</span> project provides custom memory allocation, use its implementation like <span>LwMEM</span>, 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 <span>variable</span> with <span>0</span>, unless it is treated as a boolean type
  • Never compare boolean variables with <span>0</span> or <span>1</span>. Use <span>NOT(!)</span> 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 <span>/</span> <span>* comment */</span>, even for single-line comments
  • Always include a doxygen comment at the beginning of header files, followed by a brief description (when using doxygen)
/**
 * ile            template.h
 * rief           Template include file
 */
                    /* Here is empty line */
  • Each file (header or source) must include a license (starting comment includes a single asterisk, as doxygen must ignore this)
  • Use the same license as already used in the project/library
/**
 * ile            template.h
 * rief           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 <[email protected]>
 */
  • Header files must include guard symbols <span>#ifndef</span>
  • Header files must include <span>c++</span> checks
  • Include external header files outside of <span>c++</span> checks
  • Include STL and C files first, then application custom files
  • Header files must include all other header files for correct compilation, but should not include more header files<span>(</span><code><span>if needed,</span> .c should include the remaining header files<span>)</span>
  • Header files should only expose public variables/types/functions of the module
  • Use <span>extern</span> for global module variables in header files, defined 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 <span>.c</span> files in another <span>.c</span> file
  • <span>.c</span> files should first include their corresponding <span>.h</span> files, then other files unless explicitly necessary
  • Do not include module private declarations in header files
  • Header file example <span>(</span><span>license not included in example)</span>
/* 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 */

References

[1]

Sharing the embedded C coding standards from a foreign expert on GitHub: 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

Embedded Programming Standards from an Expert's Perspective

Embedded Programming Standards from an Expert's Perspective

1. Where is the future of machine learning?

2. Cadence’s “System Dynamics Dual Sword”, such a powerful tool must be used!

3. Practical | Sharing a useful memory management module applicable to microcontrollers

4. What is the relationship between MCU, RTOS, and IoT?

5. Hong Kong emergency: 5 million chip robbery case! Full search!

6. Liver | An efficient serial port transmission idea and solution

Embedded Programming Standards from an Expert's Perspective

Disclaimer: This article is a network reprint, and the copyright belongs to the original author. If there are copyright issues, please contact us, and we will confirm the copyright based on the copyright certificate you provide and pay the remuneration or delete the content.

Leave a Comment