C编程实践——句柄设计

 
1 循环
1.1 for循环
  禁止修改循环条件和步进长度
  for循环的基本框架结构如下,
for (i = start; i < end; ++i)
{
    /* In this area, user shall not change end and i*/
}
  在该框架下,end的值应该是确定的不可变,即在循环过程中不能更改循环结束条件。同时,i的值也不应该在循环体中被改变。如果确实需要在循环过程中修改end或i的值,请选择使用while循环。
 
  少用--
  for循环按照步进方向可以分为两种写法,
/* Increasing circulation */
for (i = start; i < end; ++i)
/* Decreasing circulation */
for (i = end-1; i >= start; --i)
  但在实际实践中,还是要尽量避免--的写法,特别是在嵌入式底层开发中。在下述例证中,该for循环会由于无法达到0,从而陷入死循环中
1 for (unsinged char i=50; i>=0; i--)

 

  死循环的最好选择
  在某些场景下,需要使用死循环语句,特别是主程序循环。死循环有两种写法,最好还是选用for循环,因为该语句编译器通常不会有警告。但是死循环在程序中通常都是危险的行为,如果需要死循环,请加以注释说明。
1 for(;;) {}
2 while(1) {}
 
1.2 do-while
  在实际的编程中,do-while语句的使用场景有限,因为该结构的循环特性与for、while不一致,使用不仔细或者按照for、while的习惯使用,可能引入隐性BUG。不过在如下场景中,do-while语句却是十分重要。该语句将do-while部分的代码打包成一个整体,在宏替换时,可以保证内部语句执行完整,且不受外部使用的影响。
1 #define MEMEM_DIM2_FREE(data)    do {\
2     MEM_FREE(data.UserPtr);\
3     MEM_FREE(data.PhyPtr);\
4 } while(0) 
  另一个重要的使用方式就是需要随意终止执行流的场景中。
 1 do {
 2     if (condition1 < condition2)
 3     {
 4         break;
 5     }
 6     
 7     ...
 8     
 9     if (condition3 < condition4)
10     {
11         break;
12     }
13 } while(0);
View Code

 

1.3 while
  嵌入式编程追求的是尽可能高的确定性,因此建议优先使用for循环。如果不适合使用for循环的场景,选择使用while循环。
 
2 注释选择
  目前C语言有两种注释方式,使用//或/**/。
1 // This is comments.
2 /* This is comments. */
  在高可靠性编程中,建议使用/**/,禁用//。因为/**/注释有开始和结束位置,而//是开放性语句。同时,/**/的写作负载高于//,使用//可以很轻易的注释一条语句,而该语句到底是否应该被注释,对于阅读代码的人来说是一种负担。
  如果需要连续注释,建议/**/的注释写作如下格式,
1 /*
2  * This is comments.
3  */
  关于注释位置问题,建议优先选择在语句上方进行注释,少用或者不用尾注释。代码的一个重要作用给程序员阅读,因此整体一致的风格往往能提高阅读舒适度。
1 /* Init modulation parameter */
2 Demo_FunInit(Demo_SystemData);
 
3 条件判断
  对于不少于一个else if的语句,一定需要有else语句,哪怕这条语句没有不执行任何操作。这样做的目的是在多判断条件下,有else表明设计者认为所有条件已经考虑清楚。而没有else语句,代码阅读者不确定设计者是否考虑完所有的判断情况。
 1 if (condition1 < condition2)
 2 {
 3     ...
 4 }
 5 else if (condition3 < condition4)
 6 {
 7     ...
 8 }
 9 else
10 {
11     /*do nothing*/
12 }
  对于范围判断,可以使用if-else结构,但是如果是单值判断,建议替换为switch-case结构。
 1 /*Bad practice*/
 2 if (val == 1)
 3 {
 4     ...
 5 }
 6 else if (val == 10)
 7 {
 8     ...
 9 }
10 ...
  代码可以修改为,
1 /* Good pratice */
2 switch (val)
3 {
4     case 1:
5         ...
6     case 10:
7         ...
8     ...
9 }
View Code
 
4 switch-case
  switch-case的基本句式为,
 1 switch (val)
 2 {
 3     case condition1:
 4         ...
 5         break;
 6     case condition2:
 7         ...
 8         break;
 9         
10     ...
11    
12    default:
13        ...
14        break;  
15 }
switch-case
  善用switch语句,可以增加代码的条理性,在某些场景下也能提高代码执行效率。需要注意,在所有switch-case中都必须有default路径。
  如果有可能,用表驱动的方式替换switch-case语句,可以让代码更加简单。
 
5 变量
5.1 局部变量
  局部变量必须初始化,这样能避免由于使用未初始化变量而引入的稀奇古怪的问题。
  局部变量使用统一而区别于全局变量的命名方式。
  禁止使用汉语拼音构建变量名称。
  变量命名应该能体现变量的实际意义。
  如果变量占用内存过大,应考虑将该变量转变为全局变量或使用动态内存分配方式,防止栈溢出。
  遵守C语言编译规则,变量定义应该在作用域的开始位置;遵循C++规则,可以在使用之前定义。如果代码要在不同的环境中编译,建议遵循C语言规则。
  过多的局部变量可能意味着过长的代码或过于复杂的代码,此时需要考虑将函数拆分。
 
5.2 局部静态变量
  虽然部分教程列举了局部静态变量相对于全局变量的优点,但在工程实践中,请禁用局部静态变量。
  在高安全高可靠性领域的应用中,要求变量定义在特定的内存区域中,用于数据保护,此时局部静态变量对于这种场景而言是灾难性的存在。
  在高性能领域,并发往往是常见的需求,此时局部静态变量就是暗藏的炸弹。
 
5.3 全局变量
  全局变量应限制在模块内使用,禁止在模块之间使用。例如ADC模块定义一个全局变量Adc_GlobalRegBuffer,该变量可以在Adc.c,Adc_Hardware.c中使用,但不能在Dio.c中使用。
  在同一个模块中,只定义一个全局变量,全局变量的类型是结构体。模块中使用的全局变量均定义在该结构体中,当调试时拉一个变量就能观测到整个模块的运行状态。同时,这种构建方式,也方便定义变量在内存中的位置,或者将其转为局部变量。

posted @ 2021-04-05 09:18  寒霜未降  阅读(394)  评论(0编辑  收藏  举报