概述

程序不仅要被计算机读,还要给程序员读。一个风格清爽而严谨的程序更容易被读懂,更容易被修改和排错。良好的编程风格和正确的习惯还有助于保持思维清晰,写出正确无误的代码。特别是一个开发团队共同工作时,保持一致的编程风格尤其重要。目前单片机开发人员对编程风格问题重视度还不够。事实上,每个初学者在项目初期都会因为不良编程习惯浪费大量时间,因此若能在开始写程序时就重视编程风格问题,对顺利渡过提高阶段有很大帮助。篇幅所限,本节仅浅述编程风格几个最基本原则。

一、变量命名规则

变量名尽量使用具有说明性的名称,避免使用 a,b,c,x,y,z 等无意义字符。使用范围大的变量,如全局变量,更应该有一个说明性的名称。变量名尽量使用名词,长度控制在1~4个单词最佳。若名称包含多个单词,每个单词首字母大写以便区分单词,例如:

int InputVoltage;    //输入电压
int Temperature;     //温度

当单词间必须出现空格才好理解的时候,可以用下划线’_’替代空格:

int Degree_C; //摄氏度
int Degree_F; //华氏度

当单词较长的时候,可以适当简写:

int NumOfInputChr; //输入字符数
int InputChrCnt;   //输入字符数
int Deg_F;         //华氏度

一旦约定某方式简写,以后必须保持风格统一。
若多个模块都可能出现某个变量,可以按“模块名_变量名”的方式命名:

char ADC_Status;      // ADC 的状态
char BT_IntervalFlag; // BasicTimer 定时到达标志
int UART_RxCharCnt;   // 串口收到的字符总数

对于约定俗成的变量,如用变量 i、j 做为循环变量,p、q 作为指针,s、t 表示字符串等,不要改动。这里采用更长的变量名反而不符合习惯。

二、函数命名规则

和变量一样,函数名称也应具有说明性。函数名应使用动词或动作性名字,后面可以跟名词说明操作对象。按照“模块名_功能名”的方式命名:

unsigned int ADC16_Sample(); //16 位 ADC 采样
char LCD_Init();             //LCD 初始化
char RTC_GetVal();           //获取实时钟的数据
void PWM_SetPeriod();        //设置 PWM 周期
void PWM_SetDuty();          //设置 PWM 占空比
void Flash_WriteChar();      //向 Flash 写入一字节数据
char UART_GetChar();         //从串口读取一字节数据
char Key_GetKey();           //从键盘读取一次按键
char TouchPad_GetKey();      //从触摸板读取一次按键

每个单词首字母大写,便于阅读,专有名词或缩略词(ADC/LCD 等)全部大写。遇到太长的单词也可以在不影响阅读的情况下适当简写,例如用Tx替代单词Transmit,Rx替代单词Receive,Num替代单词Number,Cnt替换Count,数字2(Two)替代单词To等。和变量一样,一旦约定某种简写方式,以后必须保持风格统一。对于所有模块都通用的函数,如求均值函数,可以不写模块名。对于返回值是布尔类型值的函数(真或假),名称应清楚反映返回值情况,例如编写某函数检查串口发送缓冲区是否填满:

char UART_CheckTxBuff();   //不恰当的函数名
char UART_IsTxBuffFull(); //意义明确的函数名

第一个函数字面意思是“检查发送缓冲区”,若返回真(1)表示什么意思不明确。
第二个函数字面意思是“发送缓冲区满了么?”,若返回真(1)有明确的意思:满了。

三、表达式

表达式应该尽量自然、简洁、无歧义。写代码的时候,要杜绝各类“技巧”。我们的目标是要写最清晰的代码,而不是最巧妙的代码。下面2个表达式所表达的条件是等价的,第一个逻辑拐了个弯,难以理解,写成第二种表达方式就清晰许多。

if(!(RxCharNum<20)||(!(RxCharNum>=16)) //晦涩的表达式
if((RxCharNum>=20)||(RxCharNum<16)) //清晰的表达式

再看下面的表达式想要干什么?

SubKey=SubKey>>(Bits-(Bits/8)*8); //难懂的表达式

最内层表达式把Bits除以8再乘以8,实际上相当于把最低三位清零。再从 Bits 原值减去这个结果,实际上相当于取Bits的最低3位。最后用这三位确定SubKey的移位次数。实际上与下面简洁的表达式等价:

SubKey=SubKey >> (Bits & 0x07); //清晰的表达式
SubKey >>= (Bits & 0x07); //清晰的表达式
SubKey >>= (Bits%8); //清晰的表达式

一个好的表达式应该能够用英语朗读出来。可以作为检验表达式好坏的依据。例如:

if(UART_IsTxBuffFull()) UART_ClearTxBuff();
else UART_PutChar(0x55);

上面的表达式可以被朗读出来: “如果串口发送缓冲区满了,就清空发送缓冲区,否则从串口发送字符 0x55”,可读性很好。
表达式不仅要清晰,还要消除歧义。若初学者对i++ 和++i顺序比较含糊,完全可以将表达式拆开避免歧义。若对运算符优先级问题没有把握,可以用括号消除可能出现的歧义。

四、风格一致性

对于书写格式,向来争议很大,例如括号配对就有 2 种流行的写法:

for(i=0;i<100;i++){for(j=0;j<200;j++){ //括号配对风格 1...}
}
while(a==b){if(c==d){ //括号配对风格 1...}else{...}
}

另一种写法是

for(i=0;i<100;i++){ //括号配对风格 2for(j=0;j<200;j++){...}}
while(a==b)
{if(c==d){ //括号配对风格 2...}else{...}
}

除了书写风格,命名方法也有很多种标准,且经常可以看到哪种格式更好的讨论。事实上,最好的格式、最好的命名方法是和惯用风格保持一致。如果加入一个开发团队,团队目前所使用的风格就是最好的格式;如果改写别人写的程序,保持原程序的风格就是最好的风格。总之,程序的一致性比本人的习惯更重要。如果初学者还没有形成自己的风格,那么可以参考官方提供的范例程序,或者与平台供应商的代码保持风格一致。

五、注释

注释是帮助程序读者的一种手段,但是如果注释只是代码的重复,将会变得毫无意义。若注释与代码矛盾,反倒会帮倒忙。最好的注释是简洁明了的点明程序的突出特征,或者阐明思路,或提供宏观的功能解释,或者指出特殊之处,以帮助别人理解程序。比较同一段代码的两种注释方法:

for(i=6;i>DOT;i--) //从第 6 位(最高位)到小数点之间依次递减
{if (DispBuff[i]==0) DispBuff[i]=’ ’; //如果该位数值是 0,则替换成空格else break; //如果不是,则跳出循环
}

另一种注释方法

for(i=6;i>DOT;i--)
{if (DispBuff[i]==0) DispBuff[i]=’ ’; //消隐显示数据小数点前的无效 0else break;
}

第1种注释每行都有注释,但读者仍然不明白这段程序功能或目的是什么。因为每个注释无非是代码的解释和重复。第2种注释虽然只有1行,但简明扼要的说明了这个for循环的功能,帮助读者理解程序。写注释的时候要从读程序的思路来考虑而不要以设计者的角度思考。对于每个函数,特别是底层函数,都要注释。在函数前面注释该函数的名称、参数、参数值域、返回值、设计思路、功能、注意事项等。如果参与团队开发,还应写若干典型应用范例,供他人参考。商业化的代码中,注释比程序长的情况很常见。在代码维护、调试与排错时,若修改代码,要养成立即修改注释的习惯,否则很容易出现代码与注释不一致的情况,很可能造成难以排查的错误,严重影响工作效率。

六、宏定义

数值没有任何能表达自身含义的可读性,因此对于程序中出现的数值,他们应该有自己的名字。一般可以用宏定义来实现,并且用宏定义处理数据之间的关系。在寄存器操作章节中,我们已经看到头文件中对寄存器地址、标志位等都作了宏定义,用宏对寄存器赋值后,程序立刻有了可读性。类似的,我们可以用宏定义来对常数数值赋予可读性。

#define TXBUFF_SIZE (128) /*发送缓冲大小*/
#define LCM_ROW (64) /*点阵液晶行数*/
#define LCM_CLUMN (128) /*点阵液晶列数*/
#define LCM_BUF_SIZE (LCD_CLUMN*LCD_ROW/8) /*点阵液晶缓冲区大小*/

将常数值用宏定义之后写出来的代码可读性增强了:

unsigned char TxBuff[TXBUFF_SIZE]; //定义发送缓冲区char IsTxBuffFull()
{if(NumOfTxChars>=TXBUFF_SIZE) return(1); //缓冲区是否满?else return(0);
}

在上例中,一旦需要改变缓冲区大小,只需要修改宏定义即可。宏定义属于字符型替代,因此在使用宏定义的时候要注意防止产生歧义。例如数据全部加括号,以免和程序前后文构成意料之外的运算优先级。宏定义后的注释使用 /**/而不要用// ,以免某些版本的编译器在代码中将宏定义连同注释全部替换造成错误。
使用宏定义还要防止定点计算溢出,例如:

#define VOLT_RATE (1000) /*比例系数*/
...
int Voltage;
int InputValue;
...
Voltage=InputValue*VOLT_RATE; //可能溢出

若将宏定义做如下修改即可避免溢出问题:

#define VOLT_RATE ((long)1000) /*比例系数,强整成 long*/

摘自《MSP430系列单片机系统设计与实践》第一章第六节

关于单片机代码的风格相关推荐

  1. 《Java和Android开发实战详解》——2.5节良好的Java程序代码编写风格

    本节书摘来自异步社区<Java和Android开发实战详解>一书中的第2章,第2.5节良好的Java程序代码编写风格,作者 陈会安,更多章节内容可以访问云栖社区"异步社区&quo ...

  2. VHDL+Verilog良好的代码编写风格(转载)

    良好代码编写风格可以满足信.达.雅的要求.在满足功能和性能目标的前提下,增强代码的可读性.可移植性,首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单,以文档的形式记录下来,并要求每 ...

  3. 良好的代码编写风格(二十五条)

    良好代码编写风格可以满足信.达.雅的要求.在满足功能和性能目标的前提下,增强代码的可读性.可移植性,首要的工作是在项目开发之前为整个设计团队建立一个命名约定和缩略语清单,以文档的形式记录下来,并要求每 ...

  4. 优秀 Java 程序员写代码的风格

    转载自 涨姿势 | 优秀 Java 程序员写代码的风格 今天突发奇想,对编码习惯和 编程风格 很感兴趣,于是乎,找了一下关于编程风格(Java篇)的资料,希望对爱好编码或者开始学习编码的同学有帮助! ...

  5. 单片机sleep函数的头文件_单片机代码模块化设计思想浅谈

    前言:前段时间分享的文章[单片机裸机代码框架设计思路],很多读者给我留言,觉得很不错,对于初学者而言,这是一个进阶的技巧,对于我而言,这是对自己总结和表达能力的一个提升. 本文章我们再谈谈单片机代码的 ...

  6. PHP编码规范 代码样式风格规范

    一.基本约定 1.源文件 (1).纯PHP代码源文件只使用 <?php 标签,省略关闭标签 ?> : (2).源文件中PHP代码的编码格式必须是无BOM的UTF-8格式: (3).使用 U ...

  7. 单片机单口不可用或被占用_新唐单片机代码评审总结

    昨晚上,我们一个同事组织了一个小会议,大家一起讨论了一个项目的单片机代码,这个单片机用的是新唐单片机,期间大家也讨论了一些问题,总结一下,希望对写单片机的同学们有帮助. 我这个同事写的代码非常优秀,具 ...

  8. php团队规范,PHP团队 编码规范 代码样式风格规范

    一.基本约定 1.源文件 2.缩进 3.行 4.关键字 和 True/False/Null 5.命名 6.代码注释标签 7.业务模块 二.代码样式风格 1.命名空间(Namespace) 和 导入(U ...

  9. idata 单片机 新唐_新唐单片机代码评审总结

    昨晚上,我们一个同事组织了一个小会议,大家一起讨论了一个项目的单片机代码,这个单片机用的是新唐单片机,期间大家也讨论了一些问题,总结一下,希望对写单片机的同学们有帮助. 我这个同事写的代码非常优秀,具 ...

最新文章

  1. 知乎网解决HTML5 Placeholder的方案
  2. Java实现 String类型的ip与整数之间的相互转换(2021.8.1百度提前批面试题)
  3. Maven编译项目时报错:不再支持源选项 5。请使用 6 或更高版本。 不再支持目标选项 1.5。请使用 1.6 或更高版本。
  4. nemesis什么车_狂野飙车9TrionNemesis介绍 S级车Trion复仇女神属性详解
  5. android webapi 返回html 代码,ANDROID调用VS2013 ASP.NET WEBAPI 返回DATATABLE 注意
  6. bash内部命令-1
  7. 笨鸟都没有先飞怎么办。。。
  8. ansible 第二次练习
  9. 通俗讲解比特币的原理及运作机制
  10. 嘉兴学院c语言期末考试题库,液压与气压传动(嘉兴学院)知到APP答案
  11. 在注视之外:对于认知和认知发展研究,眼动追踪还能揭示什么?
  12. 增量式编码器和绝对式编码器,ABI信号和UVW信号、编码器PWM信号
  13. Docker 书籍在线阅读(Docker 从入门到实践)
  14. Win7系统不同程序无法同时播放声音是怎么回事?
  15. 在家用电饼铛自制潮汕美食肠粉过程,想学的赶紧来看
  16. 如何把PDF转成PPT文件?这几招简单方便
  17. html上中下布局关键字,HTML中关键字SEO优化布局位置
  18. mysql求当月有多少天
  19. PDF编辑器怎么使用?PDF编辑器的操作方法
  20. 【洋桃电子】STM32入门100步-03

热门文章

  1. 数据结构与算法基础Day2
  2. arcengin交互式动图制作
  3. 最后谁剩下来了就返回哪个阵营 Dota2 Senate
  4. 为什么要停止过度使用置换重要性来寻找影响特征
  5. aspx连接mysql木马_让你变成ASP木马高手_安全教程_脚本之家
  6. Sklearn中predict_proba函数用法及原理详解
  7. 基于SSM框架的企业信息管理平台
  8. 关于Spire.PDF for .NET
  9. 数学建模学习笔记(清风)——插值算法
  10. python|安装skimage库报错:required to install pyproject.toml-based projects