Embedded programming specification for foreigners

Share the embedded C coding specification of a foreigner on GitHub (collection and close reading)

preface

One of the most important rules when writing code is to examine the surrounding code and try to imitate it.
  as maintenance personnel, it is frustrating if the patch received is obviously different from the coding style of the surrounding code. This is disrespectful, like someone wearing muddy shoes into a spotless house.
  therefore, no matter what is recommended in this article, if the code has been written and you are repairing it, please keep its current style consistent, even if it is not your favorite style.

Tip: the following is the main content of this article. The following cases can be used for reference

1, General rules

The most obvious and important general rules are listed here. Please check them carefully before you continue reading other chapters.

Use C99 standard
Instead of tabs, use spaces
Use 4 spaces for each indentation level
Use a space between the keyword and the left parenthesis
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 in variables / functions / macros / types_ Or prefix. This is reserved for the C language itself
For strictly module private functions, use prv_ name prefix
For with underscores_ The variable / function / macro / type of char can only use lowercase letters
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 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 */
}

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 type, wider unsigned type takes precedence

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 a 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 using function calls to assign 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 stdint except char, float, or double H type declared in the standard library. For example, 8-bit uint8_t et al
Do not use stdpool 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 with reality. For example, replace if (check_func() * 1) with if(check_func()) {...}
Always compare pointers to null values

void* ptr;

/* ... */

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

}

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

}

Always use pre increment (and decrement) instead of post increment (and decrement)

int32_t a = 0;
...

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

for (size_t j = 0; j < 10; ++j) {}  /* OK */

Always use size_t as length or size variable
If the function should not modify the memory pointed to by the pointer, const is always used as the pointer
const is always used if the formal parameters or variables of a function 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 *, not uint8_t *. When implementing functions, you must pay attention to the correct type conversion

/*
 * 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 parentheses and sizeof operators
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 to see 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 to 0 unless it is considered Boolean
Never compare Boolean processed variables with 0 or 1. Use NOT(!) replace

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 single line comments
A c + + check with the extern keyword is always included in the header file
Every function must contain a Doxygen enabled annotation, even if the function is static
Functions, variables and comments using English name / text
Variables use lowercase letters
If the variable contains more than one name, use an underscore. force_redraw. Do not use forceRedraw
For included files in the C standard library, always use < and >. For example, # include < stdlib h >
Always use '' for custom libraries. For example, # include "my_library.h"
When converting to a pointer type, always align the asterisk with the type, for example. uint8_t t = (uint8_t*)var_width_diff_type
Always respect the code style already used in the project or library

2, Annotation related rules

Comments starting with / / are not allowed. Always use ② comment * /, even single line comments
For multiline comments, use spaces + asterisks 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) */

Use 12 indents (12 * 4 spaces) offset when commenting. If the statement is greater than 12 indents, align the comment 4-space (the following example) to the next available indent

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 */
}

3, Rules for function definition

Each function that can be accessed from outside the module must contain a function prototype (or declaration)
Function names must be lowercase and can be underlined_ separate. (this principle seems to vary from person to person)

/* OK */
void my_func(void);
void myfunc(void);

/* Wrong */
void MYFunc(void);
void myFunc();

When the function returns a pointer, align the asterisk to 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 functional prototypes (using the same / similar functions) to improve 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);

The function implementation must contain the return type and optional additional keywords on a separate line

/* 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;
}

4, Variable related rules

Make variable names all lowercase and underline_ Character 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

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 */
}

Declare pointer variables with asterisks to align with types

/* OK */
char* a;

/* Wrong */
char *a;
char * a;

When declaring multiple pointer variables, you can use asterisks to declare variable names

/* OK */
char *p, *n;

5, Structure, enumeration type definition

The structure name or enumeration name must be lowercase and underlined between words_ character
Structures or enumerations can contain the typedef keyword
All structure members must be lowercase
All enumeration members must be uppercase
Structures / enumerations must follow doxygen document syntax
When declaring a structure, it can use one of three different options:
  1. When a structure is declared only by name, its name cannot be followed by_ t suffix.

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

2. When only typedef is used to declare a structure, its name must be followed by_ t suffix.

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

3. When the structure is declared with name and typedef, it cannot contain t as the basic name. 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 false statements and 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 on declaration, use the C99 initialization style

/* OK */
a_t a = {
    .a = 4,
    .b = 5,
};

/* Wrong */
a_t a = {1, 2};

When introducing a new typedef for a function handle, use_ 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);

6, 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; When nesting statements, each nesting contains 1 indent 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 an if or if else if statement, else must be on the same line as the closing bracket of the first statement

/* OK */
if (a) {

} else if (b) {

} else {

}

/* Wrong */
if (a) {

}
else {

}

/* Wrong */
if (a) {

}
else
{

}

In the case of a do while statement, the while part must be on the same line as the closing bracket 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());

Every open bracket needs to be indented

if (a) {
    do_a();
} else {
    do_b();
    if (c) {
        do_c();
    }
}

Don't make compound statements without curly braces, even single statements. The following examples show some bad practices

if (a) do_b();
else do_c();

if (a) do_a(); else do_b();

An empty while loop, do while loop, or for loop must contain 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 be the case in embedded programming), use empty single parentheses

/* 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 & (1 << 13)) {}        /* OK, empty loop contains no spaces inside curly brackets */
while (*addr & (1 << 13)) { }       /* Wrong */
while (*addr & (1 << 13)) {         /* Wrong */

}
while (*addr & (1 << 13));          /* Wrong, curly brackets are missing. Can lead to compiler warnings or unintentional bugs */

Try to avoid incrementing variables within Circular blocks, see example

/* Not recommended */
int32_t a = 0;
while (a < 10) {
    .
    ..
    ...
    ++a;
}

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

}

/* Better, if inc may not happen in every cycle */
for (size_t a = 0; a < 10; ) {
    if (...) {
        ++a;
    }
}

7, Branch statement rules

Add a single indentation for each case statement
Use additional single indent 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 default statements

/* OK */
switch (var) {
    case 0:
        do_job();
        break;
    default: break;
}

/* Wrong, default is missing */
switch (var) {
    case 0:
        do_job();
        break;
}

If you need a local variable, use curly braces and put a break statement inside. Place the left curly bracket 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;
}

8, Macros and preprocessing instructions

Always use macros instead of literal constants, especially for numbers
All macros must be fully capitalized and underlined_ Characters (optional), unless they are explicitly marked as function, may be replaced in the future by conventional function syntax

/* 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) < (y) ? (x) : (y))

/* Wrong */
#define MIN(x, y)           x < y ? x : y

Always protect the final macro calculation with parentheses

/* Wrong */
#define MIN(x, y)           (x) < (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) < (y) ? (x) : (y))
#define SUM(x, y)           ((x) + (y))

When a macro uses multiple statements, use the do while (0) statement to protect it

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)->px = (x); (p)->py = (y)    /* 2 statements. Last one should not implement semicolon */

SET_POINT(&p, 3, 4);        /* Set point to position 3, 4. This evaluates to... */
(&p)->px = (3); (&p)->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(&p, 3, 4);/* Set point to x = 3, y = 4 */
    else
        SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */

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

/* Or if we rewrite it a little */
if (a)
    if (b)
        (&p)->px = (3);
        (&p)->py = (4);
    else
        (&p)->px = (5);
        (&p)->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)->px = (x); (p)->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)->px = (x);                  \
    (p)->py = (y);                  \
} while (0)                             /* 2 statements. No semicolon after while loop */

/* Now original code evaluates to */
if (a)
    if (b)
        do { (&p)->px = (3); (&p)->py = (4); } while (0);
    else
        do { (&p)->px = (5); (&p)->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(&p, 3, 4);/* Set point to x = 3, y = 4 */
    } else {
        SET_POINT(&p, 5, 6);/* Set point to x = 5, y = 6 */
    }
}

Do not indent statements within #if sub 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) */

file
Documented code allows doxygen parsing and generic html/pdf/latex output, so proper execution is very important.
Use doxygen supported document styles for variables, functions, and structures / enumerations
Often use \ as a doxygen, do not use it@
Always use 5x4 spaces (5 tabs) as the offset at the beginning of the text line

/**
 * \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;

Each structure / enumeration member must contain a document
Use a 12x4 space offset at the beginning of the comment

/**
 * \brief           This is point struct
 * \note            This structure is used to calculate all point
 *                      related stuff
 */
typedef struct {
    int32_t x;                                  /*!< Point X coordinate */
    int32_t y;                                  /*!< Point Y coordinate */
    int32_t size;                               /*!< Point size.
                                                    Since comment is very big,
                                                    you may go to next line */
} point_t;

/**
 * \brief           Point color enumeration
 */
typedef enum {
    COLOR_RED,                                  /*!< Red color. This comment has 12x4
                                                    spaces offset from beginning of line */
    COLOR_GREEN,                                /*!< Green color */
    COLOR_BLUE,                                 /*!< Blue color */
} point_color_t;

The documentation of the function must be written in the function implementation (usually the source file)
The function must include a brief and all parameter documentation
If each parameter is in or out input and output respectively, you must pay attention to
If a function returns a value, it must include a return parameter. This does not apply to void functions
Functions can contain other doxygen keywords, such as note or warning
Use colons 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 the function returns an enumerated member, use the ref keyword to specify which member

/**
 * \brief           My enumeration
 */
typedef enum {
    MY_ERR,                                     /*!< Error value */
    MY_OK                                       /*!< 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;
}

The macro's documentation must include the 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) < (y) ? (x) : (y))

9, Header / source file

Leave a blank line at the end of the file
Each file must include a doxygen comment for the file and a brief description followed by an empty line (when doxygen is used)

/**
 * \file            template.h
 * \brief           Template include file
 */
                    /* Here is empty line */

Each file (header or source) must contain a license (the opening comment includes a single asterisk because doxygen must ignore this)
Use the same license that the project / library already uses

/**
 * \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 <optional_email@example.com>
 */

The header file must contain a protector #ifndef
The header file must contain a c + + check
Include external header files in addition to c + + checks
First include the external header file with the STL C file, and then the application customization file
The header file must contain all other header files for proper compilation, but cannot contain more header files (if necessary,. c should include the remaining header files)
The header file must only expose module public variables / types / functions
Use extern as global module variables in the header file, and define them later in the source file

/* 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 */

Don't take it The c file is contained in another c in the document
. The c file should first contain the corresponding h documents, followed by other documents, unless otherwise expressly necessary
Module private declaration is not included in the header file
Header file example (no license 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 */

Keywords: C

Added by chawkimatta on Wed, 29 Dec 2021 20:36:40 +0200