▼For more exciting recommendations, please follow us ▼Hello everyone, I am Xiao Shu. Today, I would like to discuss programming standards with you. Whether it is a brand new project or the maintenance of an old one, adhering to programming standards can be pleasing to the eye, not only bringing joy to oneself but also to others, and significantly improving work efficiency. I have previously shared many articles about programming (for example: the painstakingly organized C language notes, thankfully I did not give up), and today I will share the programming style of foreigners.
01 The Most Important Rule
The most important rule when writing code is: check the surrounding code and try to imitate it.
As a maintainer, it is frustrating to receive a patch that clearly differs from the surrounding code’s coding style. 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 maintain its current style, even if it is not your favorite 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><span>C99</span></span>
standard - Do not use tabs; use spaces instead
- Use
<span><span>4</span></span>
spaces for each indentation level - Use a space between keywords and left parentheses
- Do not use a space 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>_</span></span>
or prefixes in variable<span><span>/</span></span>
function<span><span>/</span></span>
macro<span><span>/</span></span>
type names. This is reserved for the<span><span>C</span></span>
language itself - For strictly private module functions, use the
<span><span>prv</span></span>
<span><span>name</span></span>
prefix - For variables
<span><span>/</span></span>
functions<span><span>/</span></span>
macros<span><span>/</span></span>
types that contain underscores, only use lowercase letters - The left curly brace is always on the same line as the keyword
<span><span>(for,</span></span>
<span><span>while,</span></span>
<span><span>do,</span></span>
<span><span>switch,</span></span>
<span><span>if</span></span>
,…<span><span>)</span></span>
in the same line
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><span>0(</span></span><span><span> or </span></span><code><span><span>NULL)</span></span><span><span>, let the compiler do it for you</span></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><span>stdint.h</span></span>
standard library, except for<span><span>char</span></span>
,<span><span>float</span></span>
, or<span><span>double</span></span>
. For example,<span><span>8</span></span>
bit uint<span><span>8</span></span>
_t, etc. - Do not use the
<span><span>stdbool.h</span></span>
library. Use<span><span>1</span></span>
or<span><span>0</span></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, replace
<span><span>if(check_func()){…}</span></span>
with<span><span>if (check_func() * 1)</span></span>
- Always compare pointers with 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><span>size_t</span></span>
as length or size variables - If a function should not modify the memory pointed to by a pointer, always use
<span><span>const</span></span>
as the pointer type - If a function’s parameters or variables should not be modified, always use
<span><span>const</span></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><span>void</span></span>
*, do not use<span><span>uint8_t</span></span>
*. The function must pay attention to 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, do not use int */
}
- Always use parentheses with the
<span><span>sizeof</span></span>
operator - Do not use variable-length arrays. Use dynamic memory allocation instead with the standard
<span><span>C</span></span>
<span><span>malloc</span></span>
and free functions, or if the library<span><span>/</span></span>
project provides custom memory allocation, use it, for example,<span><span>LwMEM</span></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><span>variable</span></span>
with<span><span>0</span></span>
, unless it is treated as a boolean type - Never compare boolean variables with
<span><span>0</span></span>
or<span><span>1</span></span>
. Use<span><span>NOT(!)</span></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></span>
* comment */, even for single-line comments - For multi-line comments, use space + asterisk at the beginning of 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 a 12-space (12 * 4 spaces) indentation offset for comments. If the statement exceeds 12 spaces, align the comment to the next available indentation (as shown in the example below)
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 */
}
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 include the 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;
}
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 together 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 */
}
- Align pointer variable declarations with the type
/* OK */
char* a;
/* Wrong */
char *a;
char * a;
- When declaring multiple pointer variables, you can declare the asterisk with the variable name
/* OK */
char *p, *n;
06 Structure and Enumeration Type Definitions
- Structure names or enumeration names must be lowercase, with underscores (_) between words
- Structures or enumerations can include the
<span><span>typedef</span></span>
keyword - All structure members must be lowercase
- All enumeration members must be uppercase
- Structures/enumerations must follow
<span><span>doxygen</span></span>
documentation syntax - When declaring a structure, it can use one of the following three options:
1. When a structure is declared only by name, it must not have the <span><span>_t</span></span>
suffix after its name.
struct struct_name {
char* a;
char b;
};
2. When declaring a structure using only <span><span>typedef</span></span>
, it must have the <span><span>_t</span></span>
suffix after its name.
typedef struct {
char* a;
char b;
} struct_name_t;
3. When a structure is declared with both <span><span>name</span></span>
and <span><span>typedef</span></span>
, it must not include t as the base name, and it must have the t suffix after its name as part of the <span><span>typedef</span></span>
part.
typedef struct struct_name {
char* a;
char b;
char c;
} struct_name_t;
- Examples of incorrect declarations 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 declaration, use C99 initialization style
/* OK */
a_t a = {
.a = 4,
.b = 5,
};
/* Wrong */
a_t a = {1, 2};
- When introducing a new typedef for function handles, use the
<span><span>_fn</span></span>
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);
07 Compound Statement Rules
- Each compound statement must include a left curly brace and a right curly brace, even if it contains only 1 nested statement
- Each compound statement must contain a single indentation; for nested statements, each nested statement 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
<span><span>if</span></span>
or<span><span>if</span></span>
–<span><span>else</span></span>
–<span><span>if</span></span>
statements,<span><span>else</span></span>
must be on the same line as the right brace of the first statement
/* OK */
if (a) {
} elseif (b) {
} else {
}
/* Wrong */
if (a) {
}
else {
}
/* Wrong */
if (a) {
}
else
{
}
- In the case of
<span><span>do-while</span></span>
statements, the<span><span>while</span></span>
part must be on the same line as the right brace of the<span><span>do</span></span>
part
/* OK */
do {
int32_t a;
a = do_a();
do_b(a);
} while (check());
/* Wrong */
do
{
/* ... */
} while (check());
/* Wrong */
do {
/* ... */
}
while (check());
- Every opening brace needs to be indented
if (a) {
do_a();
} else {
do_b();
if (c) {
do_c();
}
}
- Do not create compound statements without braces, even for single statements. The following examples demonstrate some bad practices
if (a) do_b();
else do_c();
if (a) do_a(); else do_b();
- Empty
<span><span>while</span></span>
loops,<span><span>do-while</span></span>
loops, or<span><span>for</span></span>
loops must include braces
/* OK */
while (is_register_bit_set()) {}
/* Wrong */
while (is_register_bit_set());
while (is_register_bit_set()) { }
while (is_register_bit_set()) {
}
- If a
<span><span>while</span></span>
(or for,<span><span>do-while</span></span>
, etc.) is empty (this may also be the case 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 & (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 */
- Avoid incrementing variables within loop 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;
}
}
08 Branch Statement Rules
- Add a single indentation for each
<span><span>case</span></span>
statement - Use an additional single indentation for each
<span><span>break</span></span>
statement in each<span><span>case</span></span>
or<span><span>default</span></span>
/* 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
<span><span>default</span></span>
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 braces and place the
<span><span>break</span></span>
statement inside. Place the left brace on the same line as the<span><span>case</span></span>
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;
}
09 Macros and Preprocessor Directives
- Always use macros instead of literal constants, especially for numbers
- All macros must be in uppercase, with underscores (_) optional, unless they are explicitly marked as
<span><span>function</span></span>
, 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) < (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 macros use multiple statements, protect them with a
<span><span>do-while(0)</span></span>
statement
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 sub-statements within
<span><span>#if</span></span>
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
<span><span>doxygen</span></span>
to parse and generate general<span><span>html</span></span>
/<span><span>pdf</span></span>
/<span><span>latex</span></span>
output, so it is very important to execute it correctly. - Use
<span><span>doxygen</span></span>
supported documentation styles for variables, functions, and structures/enumerations - Frequently use
<span><span>\</span></span>
as<span><span>doxygen</span></span>
, do not use<span><span>@</span></span>
- Always use
<span><span>5x4</span></span>
spaces (5 tabs) as the offset for the beginning of text lines
/**
* \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 include documentation
- Use a
<span><span>12x4</span></span>
space offset for the beginning of comments
/**
* \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;
- Function documentation must be written in the function implementation (usually in the source file)
- Functions must include brief and all parameter documentation
- If each parameter is either
<span><span>in</span></span>
or<span><span>out</span></span>
input and output, it must be noted - If a function returns a value, the return parameter must be included. This does not apply to
<span><span>void</span></span>
functions - Functions can include other
<span><span>doxygen</span></span>
keywords, such as<span><span>note</span></span>
or<span><span>warn</span></span>
<span><span>in</span></span>
g - Use a colon between parameter names and descriptions
<span><span>:</span></span>
/**
* \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
<span><span>ref</span></span>
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
<span><span>(' NULL ' => NULL)</span></span>
/**
* \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 the
<span><span>hideinitializer doxygen</span></span>
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))
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 (the starting comment includes a single asterisk, as doxygen must ignore this)
- Use the same license as already used by 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 <[email protected]>
*/
- Header files must include guard symbols
<span><span>#ifndef</span></span>
- Header files must include
<span><span>c++</span></span>
checks - Include external header files outside of
<span><span>c++</span></span>
checks - Header files must include external header files first, followed by application custom files
- Header files must include all other header files for correct compilation, but should not include more header files (if needed,
<span><span>.c</span></span>
should include the remaining header files) - Header files must only expose public variables/types/functions of the module
- Use
<span><span>extern</span></span>
for global module variables in header files, defining 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 */
- Do not include
<span><span>.c</span></span>
files in another<span><span>.c</span></span>
file - .c
<span><span> files should first include their corresponding </span></span><code><span><span>.h</span></span>
files, followed by other files unless there is a clear necessity - Do not include module private declarations in header files
- 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 */
This concludes today’s article. The programming style of foreigners seems very familiar, such as the official driver library from ST, which everyone can refer to.
👉 The painstakingly organized C language notes, thankfully I did not give up👉General software and hardware version naming conventions👉How can engineers take on side jobs in their spare time? What are the channels?👉Three application architectures for microcontrollers👉Analysis of commonly used standard libraries in C
Welcome to share, collect, like, and view