C语言初学者编程规范-23.4函数的命名规范(1)函数的命名应该尽量用英文(或英文缩写、中文全拼、中文全拼缩写)表达出函数完成的功能一一函数名应准确描述函数的功能。遵循动宾结构的命名法则,函数名中动词在前,并在命名前加入函数的前缀,函数名的长度不得少于8个字母。函数名首字大写,若包含有两个单词的每个单词首字母大写。如果是OOP方法,可以只有动词(名词是对象本身)。示例:LONGGetDeviceCount(......);void print_record(unsigned int rec_ind);int input_record(void ) ;unsigned char get_current_color(void);(2)避免使用无意义或含义不清的动词为函数命名。如使用process、handle等为函数命名,因为这些动词并没有说明要具体做什么。(3)必须使用函数原型声明。函数原型声明包括:引用外来函数及内部函数,外部引用必须在右侧注明函数来源:模块名及文件名;内部函数,只要注释其定义文件名一一和调用者在同一文件中(简单程序)时不需要注释。应确保每个函数声明中的参数的名称、类型和定义中的名称、类型一致。3.5函数参数命名规范(1)参数名称的命名参照变量命名规范。2)为了提高程序的运行效率,减少参数占用的堆栈,传递大结构的参数,一律采用指针或引用方式传递。(3)为了便于其他程序员识别某个指针参数是入口参数还是出口参数,同时便于编译器检查错误,应该在入口参数前加入const标志。如:......cmCopyString(constCHAR*c_szSource,CHAR*szDest)3.6文件名(包括动态库、组件、控件、工程文件等)的命名规范文件名的命名要求表达出文件的内容,要求文件名的长度不得少于5个字母,严禁使用象file1,myfile之类的文件名。4可读性4.1避免使用默认的运算优先级注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级,可防止阅读程序时产生误解,防止因默认的优先级与设计思想不符而导致程序出错。示例:下列语句中的表达式word = (high << 8) / low (1)if (a|b) && (a&c)) (2)if ((ab) <(c&d))(3)如果书写为:high<<8/lowab&&a&ca|b<c&d由于high << 8 / low =(high << 8) / lowa/b&&a&c=(a/b)&&(a&c),(1)(2)不会出错,但语句不易理解;
C语言初学者编程规范-2 3.4 函数的命名规范 (1)函数的命名应该尽量用英文(或英文缩写、中文全拼、中文全拼缩写)表达出函数完成的功能——函 数名应准确描述函数的功能。遵循动宾结构的命名法则,函数名中动词在前,并在命名前加入函数的 前缀,函数名的长度不得少于8个字母。函数名首字大写,若包含有两个单词的每个单词首字母大 写。如果是OOP 方法,可以只有动词(名词是对象本身)。示例: LONG GetDeviceCount(.); void print_record( unsigned int rec_ind ) ; int input_record( void ) ; unsigned char get_current_color( void ) ; (2)避免使用无意义或含义不清的动词为函数命名。如使用process、handle等为函数命名,因为这些 动词并没有说明要具体做什么。 (3)必须使用函数原型声明。函数原型声明包括:引用外来函数及内部函数,外部引用必须在右侧注 明函数来源:模块名及文件名;内部函数,只要注释其定义文件名——和调用者在同一文件中(简单 程序)时不需要注释。 应确保每个函数声明中的参数的名称、类型和定义中的名称、类型一致。 3.5 函数参数命名规范 (1)参数名称的命名参照变量命名规范。 (2)为了提高程序的运行效率,减少参数占用的堆栈,传递大结构的参数,一律采用指针或引用方式 传递。 (3)为了便于其他程序员识别某个指针参数是入口参数还是出口参数,同时便于编译器检查错误,应 该在入口参数前加入const标志。如: .cmCopyString(const CHAR * c_szSource, CHAR * szDest) 3.6 文件名(包括动态库、组件、控件、工程文件等)的命名规范 文件名的命名要求表达出文件的内容,要求文件名的长度不得少于5个字母,严禁使用象file1,myfile 之类的文件名。 4 可读性 4.1 避免使用默认的运算优先级 注意运算符的优先级,并用括号明确表达式的操作顺序,避免使用默认优先级,可防止阅读程序时 产生误解,防止因默认的优先级与设计思想不符而导致程序出错。 示例:下列语句中的表达式 word = (high << 8) | low (1) if ((a | b) && (a & c)) (2) if ((a | b) < (c & d)) (3) 如果书写为: high << 8 | low a | b && a & c a | b < c & d 由于 high << 8 | low = ( high << 8) | low, a | b && a & c = (a | b) && (a & c), (1)(2)不会出错,但语句不易理解;
ab<c&d=al(b<c)&d,(3)造成了判断条件出错。4.2使用有意义的标识,避免直接使用数字避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应直接使用数字,必须用有意义的枚举或宏来代替。示例:如下的程序可读性差。if (Trunk[index].trunk_state == 0)1Trunk[index].trunk_state = 1;.../l programcode7应改为如下形式。#defineTRUNK_IDLE0#defineTRUNKBUSY1if (Trunk[index].trunk_state ==TRUNK_IDLE)1Trunk[index].trunk_state = TRUNK_BUSY;.../l programcode74.3源程序中关系较为紧密的代码应尽可能相邻这样做的好处是便于程序阅读和查找。示例:以下代码布局不太合理。rect.length=10;char_poi=str;rect.width = 5;若按如下形式书写,可能更清晰一些。rect.length = 10;rect.width=5;II矩形的长与宽关系较密切,放在一起。char_poi= str;4.4不要使用难懂的技巧性很高的语句、复杂的表达式除非很有必要时,原则上不要使用难懂的技巧性很高的语句和复杂的表达式一一高技巧语句不等于高效率的程序,源程序占用空间的节约并不等于目标程序占用空间的节约,实际上程序的效率关键在于算法。(1)如下表达式,考虑不周就可能出问题,也较难理解。* stat_poi ++ += 1;* ++ stat_poi += 1;应分别改为如下:*stat_poi += 1;stat_poi++;Il此二语句功能相当于"*stat_poi+++=1;"++ stat_poi;*stat_poi+=1;I此二语句功能相当于"*++stat_poi+=1;"(2)如下表达式,不同的编译器给出的结果不一样,b[是否先执行?x=b + i++;应改为:x = bi + i;i++;5变量与结构
a | b < c & d = a | (b < c) & d,(3)造成了判断条件出错。 4.2 使用有意义的标识,避免直接使用数字 避免使用不易理解的数字,用有意义的标识来替代。涉及物理状态或者含有物理意义的常量,不应 直接使用数字,必须用有意义的枚举或宏来代替。 示例:如下的程序可读性差。 if (Trunk[index].trunk_state == 0) { Trunk[index].trunk_state = 1; . // program code } 应改为如下形式。 #define TRUNK_IDLE 0 #define TRUNK_BUSY 1 if (Trunk[index].trunk_state == TRUNK_IDLE) { Trunk[index].trunk_state = TRUNK_BUSY; . // program code } 4.3 源程序中关系较为紧密的代码应尽可能相邻 这样做的好处是便于程序阅读和查找。示例:以下代码布局不太合理。 rect.length = 10; char_poi = str; rect.width = 5; 若按如下形式书写,可能更清晰一些。 rect.length = 10; rect.width = 5; // 矩形的长与宽关系较密切,放在一起。 char_poi = str; 4.4 不要使用难懂的技巧性很高的语句、复杂的表达式 除非很有必要时,原则上不要使用难懂的技巧性很高的语句和复杂的表达式——高技巧语句不等于 高效率的程序,源程序占用空间的节约并不等于目标程序占用空间的节约,实际上程序的效率关键 在于算法。 (1)如下表达式,考虑不周就可能出问题,也较难理解。 * stat_poi ++ += 1; * ++ stat_poi += 1; 应分别改为如下: *stat_poi += 1; stat_poi++; // 此二语句功能相当于" * stat_poi ++ += 1; " ++ stat_poi; *stat_poi += 1; // 此二语句功能相当于" * ++ stat_poi += 1; " (2)如下表达式,不同的编译器给出的结果不一样,b[i]是否先执行? x=b[i] + i++; 应改为: x = b[i] + i; i++; 5 变量与结构
5.1谨慎使用全局(公共)变量(1)去掉没必要的公共变量。公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以降低模块间的耦合度。(2)仔细定义并明确公共变量的含义、作用、取值范围及公共变量间的关系。在对变量声明的同时,应对其含义、作用及取值范围进行注释说明,同时若有必要还应说明与其它变量的关系。(3)防止局部变量与公共变量同名一一通过使用较好的命名规则来消除此问题。5.2数据类型间的转换(1)编程时,要注意数据类型的强制转换。当进行数据类型强制转换时,其数据的意义、转换后的取值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。(2)对编译系统默认的数据类型转换,也要有充分的认识。示例:如下赋值,多数编译器不产生告警,但值的含义还是稍有变化char chr;unsigned short int exam;chr = -1;exam=chr;l编译器不产生告警,此时exam为0xFFFF。(3)尽量减少没有必要的数据类型默认转换与强制转换。例如,所有的unsigned类型都应该有后缀"U"以明确其类型。(4)合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转换。(5)对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。注意其命名方式在同一产品中的统一,并且保证没有多重定义。使用自定义类型,可以弥补编程语言提供类型少、信息量不足的缺点,并能使程序清晰、简洁。示例:可参考如下方式声明自定义数据类型。下面的声明可使数据类型的使用简洁、明了。typedefunsignedcharBYTEtypedefunsigned short WORD;typedefunsigned int DWORD下面的声明可使数据类型具有更丰富的含义。typedeffloatDISTANCE;typedeffloatSCORE(6)不要用八进制数一一整型常数以0"开始会被认为是8进制。示例:code[1]=109code[2]=100code[3]=052code[4]=071如果是对总线消息初始化,会有危险。6函数与过程6.1函数的功能与规模设计(1)函数应当短而精美,而且只做一件事。不要设计多用途面面俱到的函数,多功能集于一身的函数,很可能使函数的理解、测试、维护等变得困难。一个函数应最多占满1或2个屏幕(就象我们知道的那样,ISO/ANSI的屏幕大小是80X24),只做一件事并且把它做好。一个函数的最大长度与它的复杂度和缩进级别成反比。所以,如果如果你有一个概念上简单(案,“简单"是simple而不是easy)的函数,它恰恰包含着一个很长的case语句,这样你不得不为不同的情况准备不懂的处理,那么这样的长函数是没问题的。然而,如果你有一个复杂的函数,你猜想一个并非天才的高一学生可能看不懂得这个函数,你就应当努力把它减缩得更接近前面提到的最大函数长度限制。可以使用一些辅助函数,给它们取描述性
5.1 谨慎使用全局(公共)变量 (1)去掉没必要的公共变量。公共变量是增大模块间耦合的原因之一,故应减少没必要的公共变量以 降低模块间的耦合度。 (2)仔细定义并明确公共变量的含义、作用、取值范围及公共变量间的关系。在对变量声明的同时, 应对其含义、作用及取值范围进行注释说明,同时若有必要还应说明与其它变量的关系。 (3)防止局部变量与公共变量同名——通过使用较好的命名规则来消除此问题。 5.2 数据类型间的转换 (1)编程时,要注意数据类型的强制转换。当进行数据类型强制转换时,其数据的意义、转换后的取 值等都有可能发生变化,而这些细节若考虑不周,就很有可能留下隐患。 (2)对编译系统默认的数据类型转换,也要有充分的认识。示例:如下赋值,多数编译器不产生告 警,但值的含义还是稍有变化。 char chr; unsigned short int exam; chr = -1; exam = chr; // 编译器不产生告警,此时exam为0xFFFF。 (3)尽量减少没有必要的数据类型默认转换与强制转换。例如,所有的 unsigned类型都应该有后 缀"U"以明确其类型。 (4)合理地设计数据并使用自定义数据类型,避免数据间进行不必要的类型转换。 (5)对自定义数据类型进行恰当命名,使它成为自描述性的,以提高代码可读性。注意其命名方式在 同一产品中的统一,并且保证没有多重定义。使用自定义类型,可以弥补编程语言提供类型少、信 息量不足的缺点,并能使程序清晰、简洁。示例:可参考如下方式声明自定义数据类型。 下面的声明可使数据类型的使用简洁、明了。 typedef unsigned char BYTE; typedef unsigned short WORD; typedef unsigned int DWORD; 下面的声明可使数据类型具有更丰富的含义。 typedef float DISTANCE; typedef float SCORE; (6)不要用八进制数——整型常数以"0"开始会被认为是8进制。示例: code[1]=109 code[2]=100 code[3]=052 code[4]=071 如果是对总线消息初始化,会有危险。 6 函数与过程 6.1 函数的功能与规模设计 (1)函数应当短而精美,而且只做一件事。不要设计多用途面面俱到的函数,多功能集于一身的函 数,很可能使函数的理解、测试、维护等变得困难。一个函数应最多占满1或2个屏幕(就象我们知道 的那样,ISO/ANSI的屏幕大小是80X24),只做一件事并且把它做好。 一个函数的最大长度与它的复杂度和缩进级别成反比。所以,如果如果你有一个概念上简单(案,"简 单"是simple而不是easy)的函数,它恰恰包含着一个很长的case语句,这样你不得不为不同的情况准 备不懂的处理,那么这样的长函数是没问题的。 然而,如果你有一个复杂的函数,你猜想一个并非天才的高一学生可能看不懂得这个函数,你就应 当努力把它减缩得更接近前面提到的最大函数长度限制。可以使用一些辅助函数,给它们取描述性
的名字(如果你认为这些辅助函数的调用是性能关键的,可以让编译器把它们内联进来,这比在单个函数内完成所有的事情通常要好些)。对函数还存在另一个测量标准:局部变量的数目。这不该超过5到10个,否则你可能会弄错。应当重新考虑这个函数,把它分解成小片。人类的大脑一般能同时记住7个不同的东西,超过这个数目就会犯糊涂。或许你认为自己很聪明,那么请你理解一下从现在开始的2周时间你都做什么了。(2)为简单功能编写函数。虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使功能明确化,增加程序可读性,亦可方便维护、测试。示例:如下语句的功能不很明显。value=(a>b)?a:b:改为如下就很清晰了。int max (int a, int b)1return((a>b)?a:b)}value =max (a, b);或改为如下。#define MAX (a, b) ((a) > (b) ? (a) : (b))value=MAx(a,b)当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替-这样可以增加编程效率和程序的可读性。示例:在某过程中较多引用TheReceiveBuffer[FirstSocket].byDataPtr,则可以通过以下宏定义来代替:#definepSOCKDATATheReceiveBuffer[FirstScoket].byDataPtr(3)防止把没有关联的语句放到一个函数中,防止函数或过程内出现随机内聚。随机内聚是指将没有关联或关联很弱的语句放到同一个函数或过程中。随机内聚给函数或过程的维护、测试及以后的升级等造成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造将产生随机内聚的函数。示例:如下函数就是一种随机内聚。void Init Var(void)1Rect.length = 0;Rect.width=O;/*初始化矩形的长与宽*Point.x = 10;Point.y=10;/*初始化"点"的坐标*1矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。应如下分为两个函数:voidInitRect(void)1Rect.length = 0O;Rect.width=O;*初始化矩形的长与宽*1void Init_Point(void)1Point.x = 10;
的名字(如果你认为这些辅助函数的调用是性能关键的,可以让编译器把它们内联进来,这比在单个 函数内完成所有的事情通常要好些)。 对函数还存在另一个测量标准:局部变量的数目。这不该超过5到10个,否则你可能会弄错。应当重 新考虑这个函数,把它分解成小片。人类的大脑一般能同时记住7个不同的东西,超过这个数目就会 犯糊涂。或许你认为自己很聪明,那么请你理解一下从现在开始的2周时间你都做什么了。 (2)为简单功能编写函数。虽然为仅用一两行就可完成的功能去编函数好象没有必要,但用函数可使 功能明确化,增加程序可读性,亦可方便维护、测试。示例:如下语句的功能不很明显。 value = ( a > b ) ? a : b ; 改为如下就很清晰了。 int max (int a, int b) { return ((a > b) ? a : b); } value = max (a, b); 或改为如下。 #define MAX (a, b) (((a) > (b)) ? (a) : (b)) value = MAX (a, b); 当一个过程(函数)中对较长变量(一般是结构的成员)有较多引用时,可以用一个意义相当的宏代替 ——这样可以增加编程效率和程序的可读性。示例:在某过程中较多引用 TheReceiveBuffer[FirstSocket].byDataPtr,则可以通过以下宏定义来代替: # define pSOCKDATA TheReceiveBuffer[FirstScoket].byDataPtr (3)防止把没有关联的语句放到一个函数中,防止函数或过程内出现随机内聚。随机内聚是指将没有 关联或关联很弱的语句放到同一个函数或过程中。随机内聚给函数或过程的维护、测试及以后的升 级等造成了不便,同时也使函数或过程的功能不明确。使用随机内聚函数,常常容易出现在一种应 用场合需要改进此函数,而另一种应用场合又不允许这种改进,从而陷入困境。 在编程时,经常遇到在不同函数中使用相同的代码,许多开发人员都愿把这些代码提出来,并构成 一个新函数。若这些代码关联较大并且是完成一个功能的,那么这种构造是合理的,否则这种构造 将产生随机内聚的函数。 示例:如下函数就是一种随机内聚。 void Init_Var( void ) { Rect.length = 0; Rect.width = 0; /* 初始化矩形的长与宽 */ Point.x = 10; Point.y = 10; /* 初始化"点"的坐标 */ } 矩形的长、宽与点的坐标基本没有任何关系,故以上函数是随机内聚。 应如下分为两个函数: void Init_Rect( void ) { Rect.length = 0; Rect.width = 0; /* 初始化矩形的长与宽 */ } void Init_Point( void ) { Point.x = 10;
Point.y=10;*初始化"点"的坐标*7(4)如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题。若此段代码各语句之间有实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的函数。(5)减少函数本身或函数间的递归调用。递归调用特别是函数间的递归调用(如A->B->C->A),影响程序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用,对于safe-related系统不能用递归,因为超出堆栈空间很危险。6.2函数的返回值(1)对于函数的返回位置,尽量保持单一性,即一个函数尽量做到只有一个返回位置。(单入口单出口)。要求大家统一函数的返回值,所有的函数的返回值都将以编码的方式返回。例如编码定义如下:#defineCM_POINT_IS_NULLCMMAKEHR(OX200)::建议函数实现如下:LONG函数名(参数....1LONGIResult;I/保持错误号IResult=CM_OK;1如果参数有错误则返回错误号if(参数==NULL)1IResult=CM_POINT_IS_NULL;goto END;人ENDreturnIResult:7(2)除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换方式作为返回值返回。(3)函数的返回值要清楚、明了,让使用者不容易忽视错误情况。函数的每种出错返回值的意义要清晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。4)函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出。带有内部存储器"的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样的函数既不易于理解又不利于测试和维护。在C/C++语言中,函数的static局部变量是函数的内部存储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是STATIC的局部变量的地址作为返回值,若为AUTO类,则返回为错针。示例:如下函数,其返回值(即功能)是不可预测的。unsignedintintegersum(unsignedintbase)1unsigned int index;staticunsignedintsum=O;Il注意,是static类型的I若改为auto类型,则函数即变为可预测。for(index=1;index<=base;index++)1sum += index;
Point.y = 10; /* 初始化"点"的坐标 */ } (4)如果多段代码重复做同一件事情,那么在函数的划分上可能存在问题。若此段代码各语句之间有 实质性关联并且是完成同一件功能的,那么可考虑把此段代码构造成一个新的函数。 (5)减少函数本身或函数间的递归调用。递归调用特别是函数间的递归调用(如A->B->C->A),影响程 序的可理解性;递归调用一般都占用较多的系统资源(如栈空间);递归调用对程序的测试有一定影 响。故除非为某些算法或功能的实现方便,应减少没必要的递归调用,对于safe-related 系统不能用 递归,因为超出堆栈空间很危险。 6.2 函数的返回值 (1)对于函数的返回位置,尽量保持单一性,即一个函数尽量做到只有一个返回位置。(单入口单出 口)。 要求大家统一函数的返回值,所有的函数的返回值都将以编码的方式返回。 例如编码定义如下: #define CM_POINT_IS_NULL CMMAKEHR(0X200) : : 建议函数实现如下: LONG 函数名(参数,.) { LONG lResult; //保持错误号 lResult=CM_OK; //如果参数有错误则返回错误号 if(参数==NULL) { lResult=CM_POINT_IS_NULL; goto END; } . END: return lResult; } (2)除非必要,最好不要把与函数返回值类型不同的变量,以编译系统默认的转换方式或强制的转换 方式作为返回值返回。 (3)函数的返回值要清楚、明了,让使用者不容易忽视错误情况。函数的每种出错返回值的意义要清 晰、明了、准确,防止使用者误用、理解错误或忽视错误返回码。 (4)函数的功能应该是可以预测的,也就是只要输入数据相同就应产生同样的输出。带有内部"存储 器"的函数的功能可能是不可预测的,因为它的输出可能取决于内部存储器(如某标记)的状态。这样 的函数既不易于理解又不利于测试和维护。在C/C++语言中,函数的static局部变量是函数的内部存 储器,有可能使函数的功能不可预测,然而,当某函数的返回值为指针类型时,则必须是STATIC的 局部变量的地址作为返回值,若为AUTO类,则返回为错针。 示例:如下函数,其返回值(即功能)是不可预测的。 unsigned int integer_sum( unsigned int base ) { unsigned int index; static unsigned int sum = 0; // 注意,是static类型的。 // 若改为auto类型,则函数即变为可预测。 for (index = 1; index <= base; index++) { sum += index;