我最近在写stm32单片机的程序在写的过程中发现自己编写函数名、类型名、宏定义的时候自己编写的代码总是有点别扭主要原因就是宏定义、函数名、全局变量名、类型名、局部变量名、枚举值等这些元素的命名上有问题我查阅了uboot、linux内核源码、以及我以前同事编写的代码积累了这些元素在命名方面的一些共性现在记录下来。1宏定义的宏名尽量用三个大写单词以内下划线方式实现。#define KEXEC_ARM_ATAGS_OFFSET 0x1000 #define KEXEC_ARM_ZIMAGE_OFFSET 0x80002枚举类型定义中的枚举值尽量用三个大写单词加下划线方式实现。3枚举类型名尽量用三个小写单词加下划线方式实现uboot和linux内核源码中习惯用小写例如enum km_type { KM_BOUNCE_READ, KM_SKB_SUNRPC_DATA, KM_SKB_DATA_SOFTIRQ, KM_USER0, KM_USER1, KM_BIO_SRC_IRQ, KM_BIO_DST_IRQ, KM_PTE0, KM_PTE1, KM_IRQ0, KM_IRQ1, KM_SOFTIRQ0, KM_SOFTIRQ1, KM_L1_CACHE, KM_L2_CACHE, KM_KDB, KM_TYPE_NR };4结构体、共用体类型名和成员变量名尽量用三个小写单词加下划线方式实现内部成员变量也尽量用三个小写单词加下划线方式实现。struct root_device { struct device dev; struct module *owner; };5函数名尽量用三个小写单词加下划线方式实现如下所示。static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct device_attribute *dev_attr to_dev_attr(attr); struct device *dev to_dev(kobj); ssize_t ret -EIO; if (dev_attr-show) ret dev_attr-show(dev, dev_attr, buf); if (ret (ssize_t)PAGE_SIZE) { print_symbol(dev_attr_show: %s returned bad count\n, (unsigned long)dev_attr-show); } return ret; }6全局变量名尽力用三个小写单词加下划线方式实现如下所示。static const struct sysfs_ops dev_sysfs_ops { .show dev_attr_show, .store dev_attr_store, };7函数内部定义的参数或者临时栈变量尽量使用三个小写单词加下划线方式实现。这里容易犯的一个错误是参数名或者临时栈变量名中包含了函数名中已经有的单词导致参数名或者临时栈变量名特别长之所以会犯这样的错误是因为“编写程序的时候错误下意识以为临时栈变量的作用域也为全局因此保证起参数或者临时栈变量名字的时候不能重名”这其实是错误的。我们都知道函数内部的参数或者临时栈变量的作用域只局限在函数内部因此就算名字重复了也是没有任何影响的也可以这样理解函数名是唯一的前缀就算函数内部参数和变量名字重复了整体看来也能做到命名不重复。为了缩短参数或者临时栈变量名的长度采用比较有效的办法就是在参数名或者临时栈变量名中将函数名中已经表明的英文单词给省略掉。8if条件判断中的反逻辑和正逻辑方式三编写的函数也就是返回值为某个错误状态码在父函数中经过传参和函数调用后直接或间接利用返回值参与逻辑运算最常见的就是作为if条件判断的表达式。表达式往往采用反逻辑与OK、SUCCESS等值做比较也就是使用不等于!OK、SUCCESS等数值这样做的好处就是采用防御思维编程卫语句可以降低代码嵌套级数。另外值得说明的是错误状态码中往往只有唯一的一个成功值SUCCESSOK值其他错误状态可能有多个因此与这个唯一的成功值SUCCESS、OK值参与反逻辑或者正逻辑运算而不是与其他错误状态码进行比较逻辑运算是比较简洁的方式。当然返回值使用正逻辑与唯一的OK或者SUCCESS值进行比较也是有的就是没有以防御卫语句编写方式编写还与个人编写代码习惯有关。9if-else二分法。在编程中经常使用二分法。也就只有2种情况真假、成功失败YES/NO等二分法编写代码是最简单的因为情况越多需要处理的情况代码也越多如果发生嵌套情况越多写代码难度越大。而且条件判断使用最多的就是if()触发判断和if-else二分法条件判断。这也与我以前以前文章中论述错误状态码只需要两种状态OK/ERROR、SUCCESS/FAILE、YES/NO而不需要细分错误状态码保持一致。(10) 什么是应用层程序。所谓应用层程序就是模块与模块之间的资源相关调用完成业务逻辑功能。这里的模块资源包括全局变量API函数公共宏定义、公共数据类型定义。在应用层往往发生模块A的函数调用模块B、C、D等模块的资源其实就是驱动层的资源完成应用层资源建立也就是创建私有/公共数据类型、私有/公共全局变量、私有/公共API函数私有/公共宏定义等。从宏观上看应用层程序编写与驱动层程序编写创建模块的各个资源本质上没有区别但是应用层程序的内容与业务逻辑相关并且会交叉调用驱动层的相关资源这也是应用层比驱动层负载的地方。11函数的复用性将相似代码段定义成一个函数关键是不同变量定义成参数可以减少代码行数节省代码段大小。最近我在写stm32项目程序的业务应用层程序其中使用了一个阶梯判断语句switch-case-break-default语句用于判断接收命令行命令ID的具体分析其中有几个命令ID的处理是非常相似的只有很少一部分语句不同。当我进行程序优化的时候感觉完全可以将这些命令ID的相似处理包含写成一个含参函数参数对应处理不同部分。这样函数在被调用时传递不同参数对应上述代码中不同部分剩余部分相同。返回值选为void也就是不参与父函数任何运算。static void app_USB5Vx_set(USB5V_PWR_X x) { if (cmd_rxbuf[4] 0x00) USB5Vx_power_set(x, USB5V_PWR_OFF); else USB5Vx_power_set(x, USB5V_PWR_ON); } case CMD_USB5V_1: /**0x55 0xAA 0x03 CMD_USB5V_1 0xXX[4] 0x00 0x0A */ // 第一路5V供电配置 app_USB5Vx_set(USB5V_PWR1); // 回传命令 cmd_send(cmd_rxbuf, cmd_rxlen); break; case CMD_USB5V_2: /**0x55 0xAA 0x03 CMD_USB5V_2 0xXX 0x00 0x0A */ // 第二路5V供电配置 app_USB5Vx_set(USB5V_PWR2); // 回传命令 cmd_send(cmd_rxbuf, cmd_rxlen); break; case CMD_USB5V_3: /**0x55 0xAA 0x03 CMD_USB5V_3 0xXX 0x00 0x0A */ // 第三路5V供电配置 app_USB5Vx_set(USB5V_PWR3); // 回传命令 cmd_send(cmd_rxbuf, cmd_rxlen); break;例如上述代码中几个命令ID的处理就是函数体的内容不同之处主要是函数体内函数USB5Vx_power_set()的第一个参数不同因此将这个不同的变量定义成参数变量返回值为void这样定义成一个函数函数调用的时候分别传递不同参数但是都调用同一段代码这样可以节省代码段的大小也体现了函数的复用性。后面如果发现有新共性继续补充。
