Rule #1:
Braces ({ }) shall always surround the blocks of code (also known as compound statements) following if, else, switch, while, do, and for keywords. Single statements and empty statements following these keywords shall also always be surrounded by braces.
应该使用括号来包围if,else,switch,while等后面的代码段,就算是单行语句甚至是空语句也应该使用括号括起来。
错误例子:if (timer.done)
timer.control = TIMER_RESTART;
正确例子:while (!timer.done)
{
// 就算是一个空的语句也应该用括号括起来
}
Reasoning: Considerable
risk is associated with the presence of empty statements and single
statements that are not surrounded by braces. Code constructs of this
type are often associated with bugs when nearby code is changed or
commented out. This type of bug is entirely eliminated by the
consistent use of braces.
理由:一个空的语句或单行语句如果没有被括号包起来,所造成的风险是值得考虑的。这种结构的代码如果附近的语句改变或被注释,很有可能会导致bug的出现。这种bug完全可以通过使用大括号来消除。
Rule #2:
The const keyword shall be used whenever possible, including:
To declare variables that should not be changed after initialization,
To define call-by-reference function parameters that should not be modified (for example, char const * p_data),
To
define fields in structs and unions that cannot be modified (such as in
a struct overlay for memory-mapped I/O peripheral registers), and
As a strongly typed alternative to #define for numerical constants.
Reasoning: The upside of using const as much as possible is compiler-enforced protection from unintended writes to data that should be read-only.
只要有可能,尽量使用const关键字,包括下面的情况:
用于声明一个初始化后就不会改变的变量,
用于定义一个通过引用的方式来被函数使用,并且不希望其改变的参数,如char const * p_data
用于定义结构体或公用体中不能被改变的部分,譬如使用一个结构体来组织外设的寄存器内存映射
用于替代#define来定义一个静态变量,强制使其具有数字类型
理由:上面对const的使用是使用编译器强制的方式来保证只读的数据不会被无意的修改。
Rule #3: The static
keyword shall be used to declare all functions and variables that do
not need to be visible outside of the module in which they are declared.
所有在其声明的模块外部不被使用的函数与变量均应该使用static关键字修饰。
Reasoning: C's static keyword has several meanings. At the module-level, global variables and functions declared static are protected from inadvertent access from other modules. Heavy-handed use of static in this way thus decreases coupling and furthers encapsulation.
理由:C语言的static关键字具有多种含义。在模块的使用中,使用static来声明全局变量与函数可以保护其不被其他模块无意的访问。大量的通过这种方式来使用static关键字可以减少模块间的联结并且可以更好的封装。
Rule #4:
The volatile keyword shall be used whenever appropriate, including:
To declare a global variable accessible (by current use or scope) by any interrupt service routine,
To declare a global variable accessible (by current use or scope) by two or more tasks, and
To declare a pointer to a memory-mapped I/O peripheral register set (for example, timer_t volatile * const p_timer).
Reasoning: Proper use of volatile
eliminates a whole class of difficult-to-detect bugs by preventing the
compiler from making optimizations that would eliminate requested reads
or writes to variables or registers that may be changed at any time by
a parallel-running entity
volatile关键字应该在适当的地方尽量使用,包括:
用于声明一个会被中断服务程序访问的全局变量,
用于声明一个会被两个或多个任务访问的全局变量,
用于声明一个指向外设寄存器组内存映射的指针,如timer_t volatile * const p_timer
理由:适当的使用volatile可以消除大量难以察觉的bug,通过使用volatile可以防止编译器优化那些可能在任何时候被另一个平行运行的实体读写的的变量或寄存器。
Rule #5:
Comments shall neither be nested nor used to disable a block of code,
even temporarily. To temporarily disable a block of code, use the
preprocessor's conditional compilation feature (for example, #if 0 ... #endif).
注释符号不应该嵌套,也不应用来注释掉某个代码块,就算是暂时的。如果需要暂时注释某个代码块,使用预处理条件编译特性,如#if 0 ... #endif
错误例子:/*
a = a + 1;
/* 注释 */
b = b + 1;
*/
Reasoning: Nested
comments and commented-out code both run the risk of allowing
unexpected snippets of code to be compiled into the final executable.
理由:嵌套的注释与对代码的注释均是一种冒险的行为,有可能会允许一段意想不到的代码被编译进最终的执行文件中。
Rule #6:
Whenever
the width, in bits or bytes, of an integer value matters in the
program, a fixed-width data type shall be used in place of char, short, int, long, or long long. The signed and unsigned fixed-width integer types shall be as shown in Table 1.
无论何时,整形数的位长度在编程中总是要注意的,应该使用一个定宽的类型来代替char, short, int, long, long long。如上图所示。
Reasoning: The ISO C standard allows implementation-defined widths for char, short, int, long, and long long
types, which leads to portability problems. Though the 1999 standard
did not change this underlying issue, it did introduce the uniform type
names shown in the table, which are defined in the new header file <stdint.h>;. These are the names to use even if you have to create the typdefs by hand.
理由:标
准C允许编译器实现定义char, short, int, long, long
long的长度,这可能会导致某些问题。虽然1999标准没有更改这个底层的问题,但其引入了统一的类型名字,如上表所示,这些类型定义在一个新的头文件
<stdint.h> 中。应该使用这些名字,就算是你需要自己动手去写typdef。
Rule #7:
None of the bit-wise operators (in other words, &, |, ~, ^, <<, and >>) shall be used to manipulate signed integer data.
任何位操作(也就是 &, |, ~, ^, <<, 与 >>)都不应该对有符号的整形数来使用。
错误例子:int8_t signed_data = -4;
signed_data >>= 1; // not necessarily -2
Reasoning: The
C standard does not specify the underlying format of signed data (for
example, 2's complement) and leaves the effect of some bit-wise
operators to be defined by the compiler author.
理由:标准C并没有指定一个有符号数的底层实现(如二补数 2's complement),位操作的效果是由编译器作者来定义的。
Rule #8:
Signed integers shall not be combined with unsigned integers in
comparisons or expressions. In support of this, decimal constants meant
to be unsigned should be declared with a 'u' at the end.
有符号整形不应该与无符号整形在比较或表达式中混合使用。为了支持这一点,作为无符号数使用的十进制常量在声明时要在后面加个'u'
错误例子:uint8_t a = 6u;
int8_t b = -9;
if (a + b < 4)
{
// 如果如预料中的 -9+6=-3 < 4,这个正确的部分会运行
}
else
{
// 实际上会运行这个错误的分支程序
// 因为 -9 + 6 变为
// (0xFF - 9) + 6 = 252.
}
Reasoning:
Several details of the manipulation of binary data within signed
integer containers are implementation-defined behaviors of the C
standard. Additionally, the results of mixing signed and unsigned data
can lead to data-dependent bugs.
理由:对某些有符号整形的二进制实现的细节的C标准是由编译器作者来定义实现的,混合有符号与无符号数会导致数据依赖(data-dependent)的bug。
Rule #9:
Parameterized macros shall not be used if an inline function can be written to accomplish the same task
如果内联函数可以实现同样的功能,就不应该使用参数化的宏。
Example:// 不应该这样 ...
#define MAX(A, B) ((A) > (B) ? (A) : (B))
// ... 如果你可以通过这种方式实现
inline int max(int a, int b)
Reasoning: There are a lot of risks associated with the use of preprocessor #defines,
and many of them relate to the creation of parameterized macros. The
extensive use of parentheses (as shown in the example) is important,
but doesn't eliminate the unintended double increment possibility of a
call such as MAX(i++, j++). Other risks of macro misuse include comparison of signed and unsigned data or any test of floating-point data.
理由:使
用预处理 #define
会有很多的风险,其中很多是与使用参数化的宏有关。大量使用括号很重要(如例子所示),但是并不能排除无意的双倍增加的情况出现,例如一个
MAX(i++, j++)这样的调用。其他的风险包括有符号与无符号数的比较,或者对浮点数的任何检验。
Rule #10:
The comma (,) operator shall not be used within variable declarations.
在变量定义时不应该使用逗号(,)。
错误的例子:char * x, y; // 你是要y为指针还是非指针?
Reasoning:
The cost of placing each declaration on a line of its own is low. By
contrast, the risk that either the compiler or a maintainer will
misunderstand your intentions is high.
理由:把每个变量的声明放在单独的一行所付出的代价很低。相对而言,编译器或维护人员误解你的意图所付出的代价要高很多。
文章评论(0条评论)
登录后参与讨论