如何写出好的代码(二) - 编程技巧

前几回说到"怎么写好代码之编程规范",其实应该叫"怎么写出好看的代码",这是极为关键的,一段工整的代码肯定是能让读代码的人产生想读下去、想去了解这段代码的欲望。就好比第一眼看到一个美女,一般都会令人产生想要和她交个朋友的想法吧。我相信应该没有谁会在只有第一印象的情况下,想去和一位邋遢的人交朋友吧。
不过编程规范也只是其一,一段好的代码还需要看内涵,代码是否做到高效、简洁、尽可能减小bug出现的可能性等等,这就是所谓的看内在,就像看人一样,光长得好看还不够,还需要看性格、优点、特长等。由于我主要是做嵌入式C开发,在嵌入式和C语言这方面有些许经验,那么就从这两个方面来分享一些我的经验吧。
首先是C语言:
·尽可能避免使用全局变量
全局变量用起来很方便,但增大了程序模块之间的耦合性,对于后续的程序维护和移植非常不方便。如果必须使用全局变量,最好使用static将其作用域限制在单个文件中,对外则提供接口进行获取。
·if-else与switch-case的选择
当程序中使用到多个if-else结构时,若使用switch-case更简洁,则尽可能地转换为switch-case结构,比如针对单个变量的不同值进行不同的操作。当然有些情况下使用switch-case较为复杂,反而if-else更方便,需要视情况而定。
·成对操作申请与释放资源
当使用到资源申请时,可以先在结尾写上释放的操作再完成中间的内容,比如内存的申请,如果忘记释放则会导致内存泄露。
·反复检查数组越界情况
C语言是不安全的,程序员稍微一不注意就容易出现内存的非法访问,典型的就是数组越界访问,可能导致程序跑飞。
·善用面向对象的编程思想
C语言尽管是面向过程的编程语言,但稍加封装一下即可具备简单的面向对象功能。实现面向对象最主要的部分是结构体和函数指针,当一个文件使用到很多相关联的变量时,尽可能使用结构体进行封装。在函数参数传递时使用结构体指针传递,能减小函数形参的传递数量。
·项目较复杂时尽可能采用多线程
当一个项目比较复杂时,使用单线程方式可能会导致系统响应缓慢。这时可以采用多线程编程,各个线程分配任务和优先级,使用操作系统提供的线程通信和同步机制对各个线程进行合理地调度。
其次是嵌入式编程:
对于嵌入式编程,我将分享我个人的一些底层驱动代码的写法和一些基于RTOS的开发经验。
·按键驱动
为了程序的通用性,按键的驱动代码一般分为3部分:
①芯片的IO输入配置
②按键动作的处理判断代码(消抖、长短按判断等)
③按键的应用代码(按下后做什么)
其中:
①是与芯片相关,除非可以整合全部芯片的IO驱动代码,否则基本上是需要改动的。
②是按键处理通用的代码,这部分是可以只写一次的,因为与硬件完全无关。为了提高CPU的利用率,一般按键的响应采用IO口的中断,而消抖则采用定时器进行消抖。
③应用代码则是需要根据需求而定,根据面向对象的编程思想,这里可以选择使用函数指针,对外提供一个函数接口用于注册按键的应用代码。这样一个通用的按键驱动就完成了,移植的时候仅需要移植底层的芯片IO驱动即可。
·总线式芯片驱动
对于一些I2C和SPI等使用总线进行通讯的设备,且总线下可以挂载多个设备。此时驱动通常分为3部分:
①芯片的总线驱动,如SPI驱动。
②设备器件的驱动。
③设备驱动提供给上层的接口。
其中:
①是与芯片相关,在更换芯片时需要移植的。通常单独编写一个模块,即一个独立的.c文件和.h文件,提供给设备器件总线数据读写等接口函数。
②则是设备器件的驱动,使用①提供的总线数据读写函数对器件进行操作,如果可以的话,使用上函数指针进行读写函数注册是最佳的。同样通常只需要写一次即可,作为一个独立的模块,这样移植时直接复制,不需要修改任何逻辑。
③则是提供器件功能的接口函数到应用层,比如一个MPU6050姿态传感器就是提供给应用层获取当前的角度、角速度的函数。
·使用信号量(semphore)或者进行任务同步
在使用RTOS进行开发时,任务之间的操作可以使用信号量进行同步,比如某一个任务需要等待有按键按下才需要执行,此时可以使用信号量使该任务阻塞(进入休眠态),直到有按键按下后,在按键的应用代码中向该任务发送一个信号量来使该任务进入就绪态开始执行。这样可以极大的使任务切换频率降低。
·使用互斥体(mutex)或信号量(semphore)对设备互斥访问
当一个总线下挂载多个设备时,同一时刻只能允许一个设备访问总线,这时可以使用互斥体或者信号量(值为1)对总线进行保护,在设备使用总线前先尝试获取互斥体或者信号量,如果获取不到则表示当前总线被占用,任务阻塞(进入休眠态),直到使用总线的设备释放信号量后,才会唤醒被阻塞的任务开始使用总线。
·任务间通信采用消息机制而非全局变量
任务间的通信尽管采用全局变量会更方便,但同时也带来了隐患,因为全局变量多个任务都可以访问,在没有资源保护的情况下,容易造成竞争发生数据错误。采用消息机制则是在任务之间进行数据的发送,操作系统会自动同步,确保这个任务写入完毕,另一个任务开始读取,不会造成竞争的关系,而且采用消息机制,任务之间的耦合性也降低了。
·中断服务程序快进快出
中断服务程序的优先级往往是最高的,如果占用太多时间会导致其他任务被阻塞无法及时得到执行。因此中断服务函数中只处理一些设置和清除标志位的操作就立刻退出。剩下的操作则是作为线程(任务)与其他线程(任务)一起参与系统的调度,这里同样可以使用信号量做一个同步,当中断退出时,发送一个信号量到处理中断的任务,使任务转入就绪态开始执行。
由于时间和篇幅的关系,暂时写这么多吧。总的来说,一段好代码就像一个绝世倾城的美女一般,首先看外表,其次看内在,二者缺一不可。