嵌入式Linux入门11:编程规范

作为一名软件工程师,日常与代码打交道,免不了接触到编程规范。而编程规范,可谓是各式各样,不同系统有不同风格,不同公司有不同风格。就笔者经历而言,在学生年代学习单片机编程时已经开始建立自己的“编程规范”。总的来说,笔者认为《高质量C、C++编程指南》、《Google C++ 编码规范》非常好,有指导意思。工作多年,接触了多种风格,并能自由切换。但还有有一些原则性的条例,是自己一直坚持的。

本文关注两大方面:编码风格以及设计原则。

一、编码风格

编码如写文章,有段落,有条理。如书法,有整体布局。本节主要就目录组织形式和书写命名方面进行介绍。


源码组织(目录格式)

将头文件及实现文件分开存放,但内部使用的头文件可以不按此约定。使用第三方库源码亦要与自实现代码分开存放。



命名


1、与当前所使用的代码风格保持一致。

一般地,内核及driverlib层使用linux风格


应用层程序使用 windows风格

借鉴他人(如TI的mcfw代码)


2、源码文件名称需要统一使用一种风格。比如,统一使用小写+下划线,或者统一使用大小写。


形式:动词+名词。

可顺序阅读的。如获取下一帧数据:getNextFrameData()



类:开头、所有词首字母大写

变量:开头小写

宏、枚举:全部大写

成员变量:加不加m?


专用名大写(PI、OSD)



void createNew()



int rtpPortNum



int maxCNAMElen



doEventLoop()



常见:

this

doit





代码布局

整个文件布局整齐、整洁,不能出现多余的空行,行尾也不应有多余的空格。

原则:如写文章一样,有段落。



注释

注释分为文件注释及函数接口注释,以清楚明白为原则,主要在头文件进行,在实现代码中需要写上重要的、必要的注释。风格不限,但需统一。建议使用doxygen格式的注释。

只对代码无法表达的东西写注释(Comments Only What the Code Cannot Say)



头文件包含

原则:只在必须的文件中进行,比如bar.h头文件不需要foo.h,而bar.c文件需要,则仅在bar.c文件中包含。

不要包含不必要的头文件。

对包含的头文件可以作一些必要的注释。如:

#include <strings.h>   /**< bzero */



二、设计原则

上一节的代码,只徒有其表,写得再漂亮的代码,没有内涵,没有条理性,没有逻辑,也是狂然。本节就一些设计原则进行介绍。——至于如架构设计、设计模式、架构模式涉及内容太多,就不在本文讨论范围。



函数设计

1、函数接口返回值一般应该为整型 int,成功返回0,错误返回负数,并在接口注释中标明错误码。

例外:像内存管理类的接口,根实际情况作处理,如申请共享内存,要返回内存地址。



2、一个函数做一件事,把相对独立的事情封装到一个函数中。

举例:从JPEG转为BMP,应分2个函数,解压JPEG得到RGB数据,将RGB数据保存为BMP。



参数设计

若参数为只读字符串,不需要修改,则使用const,如打开文件:

void foo_open(const char* file)

若参数过多,可考虑封装为结构体。

对于C++,参数可以使用引用的方式。



-------------------------------------------------------------------------------

宏定义及判断语句

以肯定语气为主,宏判断不宜过多。

示例:


#ifndef FOO_BAR

// something

#endif



#if (!defined(A_PLATFORM) && !defined(_B_PLATFORM_)) || (defined(_Z_APP_))



不好的示例(原型来自TI某代码):

switch (cmd)
 {
     case DISABLE_A:
         break;
     case DISABLE_B:
         break;
     case DISABLE_NONE:
         break;
     default:
         break;
 }

应改为:


switch (cmd)
 {
     case ENABLE_A:
         break;
     case ENABLE_B:
         break;
     case ENABLE_ALL:
         break;
     default:
         break;
 }



-------------------------------------------------------------------------------


信息打印

在调试期间,可以打印许多的信息,但要使用宏或者等级来控制,不要直接出现调用printf的实际函数。

该打印的一直要打印出来,比如错误信息,一些重要的步骤过程(监控代码运行)。

不该打印的不要打印,比如打印传递的参数值、为跟踪代码而打印的(作为debug打印)。

不同的错误原因,不能使用同一打印信息(为省事直接复制代码),打印信息应准确反应错误原因。

不好的示例:

if (init_foo() < 0)

     printf("foo error.\n");

if (exit_foo() < 0)

     printf("foo error.\n");

应改为:


if (init_foo() < 0)

     printf("init foo error.\n");

if (exit_foo() < 0)

     printf("exit foo error.\n");



-------------------------------------------------------------------------------

1、不要在if()括号中中写大量的函数调用,return也不要写。

if (foo() || bar() || a < b)

return (foo() ? b : a) || (bar() &&flag)

-->原因:1:函数调用不突出,因为在if里面。2:出错不好排查。因为不知道哪个函数出错。



2、不要一个语句写大量的函数。

如 ss* ptr = (ss*)foo()->getname()->file()->state()

-->如果中间有非法指针(如getname()返回NULL),可能会挂掉。


----------------------

1、数组越界,GCC检查不出来的。实际工程的代码示例:

int data[2]; // 保存到临时变量
data[0] = param->enable;
 data[1] = param->type;
 data[2] = param->width;
 data[3] = param->height;
 data[4] = param->count;



判断语句,布尔型的可以使用if (!enable),也可以使用if (enable == false)



===========================================



一定要用括号把优先级强调出来。——即使本身优先级已确定。

不要在宏中多次++操作。



==================================

C++结构体,直接当类使用、初始化:

struct ata_print_options
 {

bool drive_info;

ata_print_options()
     : drive_info(false),

{}

}



多用uint8_t、int32_t这类标准写法,不用int(在stdint.h头文件中,如没有,可自行定义)


本文涉及内容,仅为一人之言,不当之处,请方家斧正。

PS:如与参考资料有冲突之处,请以个人/公司实际情况为准。


参考资料:

《高质量C、C++编程指南》

《Google C++ 编码规范》

李迟 2017.9.5 夜




猜你喜欢

转载自blog.csdn.net/subfate/article/details/77859025