07 复合语句规则
每个复合语句必须包括左花括号和右花括号,即使它只包含1个嵌套语句
每个复合语句必须包含单个缩进;嵌套语句时,每个嵌套包含1个缩进大小
Plain Text
/* OK /if (c) {
do_a();
} else {
do_b();
}
/ Wrong /if (c)
do_a();
elsedo_b();
/ Wrong */if (c) do_a();
else do_b();
在
if
或
if
-
else
-
if
语句的情况下,
else
必须与第一条语句的右括号在同一行
CSS
/* OK /if (a) {
} else if (b) {
} else {
}
/ Wrong /if (a) {
}
else {
}
/ Wrong */if (a) {
}
else
{
}
在
do-while
语句的情况下,
while
部分必须与
do
部分的右括号在同一行
C++
/* OK /do {
int32_t a;
a = do_a();
do_b(a);
} while (check());
/ Wrong /do
{
/ ... /
} while (check());
/ Wrong /do {
/ ... */
}
while (check());
每一个开括号都需要缩进
Plain Text
if (a) {
do_a();
} else {
do_b();
if (c) {
do_c();
}
}
不要做没有花括号的复合语句,即使是单个语句。下面的例子展示了一些不好的做法
Plain Text
if (a) do_b();
else do_c();
if (a) do_a(); else do_b();
空
while
循环、
do-while
循环或
for
循环
必须包含花括号
C++
/* OK /while (is_register_bit_set()) {}
/ Wrong */while (is_register_bit_set());
while (is_register_bit_set()) { }
while (is_register_bit_set()) {
}
如果
while
(或for、
do-while
等)为空(嵌入式编程中也可能是这种情况),请使用空的单行括号
C++
/* 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 */
尽量避免在循环块内递增变量,参见示例
C++
/* 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 分支语句规则
为每个
case
语句添加单个缩进
使用额外的单缩进
break
语句在每个
case
或
default
C++
/* 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;
}
总是包含
default
语句
C#
/* OK /switch (var) {
case 0:
do_job();
break;
default: break;
}
/ Wrong, default is missing */switch (var) {
case 0:
do_job();
break;
}
如果需要
局部变量
,则使用花括号并在里面放入
break
语句。将左花括号放在
case
语句的同一行
C++
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 宏和预处理指令
总是使用宏而不是文字常量,特别是对于数字
所有的宏必须是全大写的,并带有下划线_字符(可选),除非它们被明确标记为
function
,将来可能会被常规函数语法替换
Plain Text
/* OK /#define MY_MACRO(x) ((x) (x))
/* Wrong /#define square(x) ((x) (x))
总是用圆括号保护输入参数
C++
/* OK /#define MIN(x, y) ((x) < (y) ? (x) : (y))/ Wrong */#define MIN(x, y) x < y ? x : y
总是用括号保护最终的宏计算
C++
/* 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))
当宏使用多个语句时,使用
do-while(0)
语句保护它
Rust
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);
elsedo { (&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 */
}
}
不缩进子语句内#if语句
C++
/* 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) */
文档
文档化的代码允许
doxygen
解析和通用的
html
/
pdf
/
latex
输出,因此正确地执行是非常重要的。
对变量、函数和结构
/
枚举使用
doxygen
支持的文档样式
经常使用
\
作为
doxygen
,不要使用
@
始终使用
5x4
空格
(5
个制表符
)
作为文本行开始的偏移量
C++
/**
* \brief Holds pointer to first entry in linked list
* Beginning of this text is 5 tabs (20 spaces) from beginning of line
/statictype_t list;
每个结构/枚举成员都必须包含文档
注释的开头使用
12x4
空格偏移量
C++
/**
* \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;
函数的文档必须在函数实现中编写
(
通常是源文件
)
函数必须包括简要和所有参数文档
如果每个参数分别为
in
或
out
输入和输出,则必须注意
如果函数返回某个值,则必须包含返回形参。这不适用于
void
函数
函数可以包含其他
doxygen
关键字,如
note
或warn
in
g
在参数名和描述之间使用冒号
:
C++
/**
* \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
/voidvoid_sum(int32_t a, int32_t b, int32_t* result) {
*result = a + b;
}
如果函数返回枚举的成员,则使用
ref
关键字指定哪个成员
C++
/**
* \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;
}
对常量或数字使用符号
(' NULL ' => NULL)
C++
/**
* \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;
}
宏的文档必须包括
hideinitializer doxygen
命令
Markdown
/**
* \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)) |