先上一段名人名言:
同一颗鸡蛋,当你屁股坐的位置不一样时,看到的形状也不一样。
—— 达•芬奇
编程风格,是软件设计过程中,非常重要的一环。编程风格就像达•芬奇的鸡蛋一样,不同的人会从不同的角度,给出各自的结论。编程风格是好还是不好,没有定论,取决于你看问题的角度。今天呢,我只是从我的角度,给大家普及一下我个人认可的编程风格。您可以觉得我说的不对,或者若干年后,您成为一名程序员,觉得我这篇文章误人子弟了,这里先说一声对不起。
所以呢,在这里,我不给编程风格做价值判断,等您坐到自己的位置上之后,会有自己的判断。
你要是问我,我心里肯定有一个价值判断,但是我的价值判断,决定于我屁股的位置。
源代码是给人看的
源代码是给人看的,这个其实有点反直觉。计算机能直接执行的,只有机器语言,因此汇编、C、C++、Python …… 等各种计算机语言出现,都是为了降低当程序员的门槛、减轻程序员的负担,而且哪怕是“自己写的程序,三天不看,就是别人写的”,当你写出一个常人无法看懂的程序,有可能三天之后发现自己也是肉体凡胎,无法看懂自己三天前的程序了。
如果你无法看懂的程序,一般有两个原因。一是因为自己水平的确很菜,无法看懂程序里精妙的算法;另一个就是代码逻辑不复杂,但是风格不好,让人无法理解其中简单的算法。
所以作为初学者的你,应该每个月抽一点时间,把自己上个月写的源代码再看一遍,明明很简单的算法,为什么看不懂,是不是哪里的风格不够好,影响了自己理解算法?
如果有空,也应该去找一些开源的代码看看,大多数开源代码,风格都是可以借鉴的。
命名风格
命名
到目前我讲过的内容,有三样东西需要命名:变量、函数、常量。C语言中规定,命名可以使用的字符有:大写字母、小写字母、数字和下划线,且首字符不能使用数字;大多数编程风格,都会硬性规定:常量名不用小写字母;
名称内部分隔
先说变量名和函数名的内部分隔,有两种方式,驼峰命名法,和下划线分隔法,请看代码:
// 变量名:学号
int XueHao; // 驼峰命名法
int StudentId;// 驼峰命名法
int xue_hao; // 下划线分隔法
int student_id;// 下划线分隔法
驼峰命名法,就是每个汉语拼音或者单词的首字母大写,剩余的字母小写。具体用哪一种,再学校要按照老师的要求,工作只有要看单位的规定、项目的规定……,如果没有任何规定,当然就可以按照自己的习惯来。
对于常量,因为不使用小写字母,因此,只能使用下划线作为分隔:
const double YUAN_ZHOU_LV = 3.141592653589793;
#define M_YUAN_ZHOU_LV (3.141592653589793)
变量名前缀
变量名可以通过加前缀的方式,标识(zhì)变量类型,比如代码:
int i_xue_hao;
unsigned int ui_xue_hao;
short int s_xue_hao;
unsigned short int us_xue_hao;
char c_xue_hao;
unsigned char uc_xue_hao;
前缀 i 标识该变量为整型;前缀 ui 标识该前缀为无符号整形;前缀 s 标识该变量为 短整形;……
当然,还有其他的前缀,用来标识变量的作用域等,也许你的老师会给你一份提交作业的风格手册;将来工作了,单位会有内部的编程风格标准;给开源项目提交代码时,也会有项目的风格标准。总之,到哪个庙拜哪个神,你到肯德基买不到巨无霸,在麦当劳也买不到老北京鸡肉卷。
函数名前缀
函数名通常通过一个动词,来表示该函数所做的功能,比如“set” 标识该函数会写入某个变量;“get” 标识该函数会读取某个变量;“is”标识该函数会获取某个变量,这个变量可以用作判断。
函数返回值
所有函数都应该有返回值,如果实在不知道函数返回值应该是什么,就用 int 型。通常情况下,如果返回非负数,则函数成功执行;如果返回负数,则函数执行错误。
这里要强调一下,函数执行正确时,如果返回值没有特殊含义,通常返回 0,表示函数执行正确;返回负数,表示函数执行错误。因为函数执行正确时,一般不关心为什么会正确,所以返回 0就行了。但是函数执行错误时,都得分析一下为什么错误,所以需要返回不同的值,来标识程序错误的原因。当然这个并不是金科玉律,仅仅是大多数情况,明显的反例就是,当指针作为返回值时,0 就表示执行出了问题。
缩进
请看代码:
int main(void)
{// 此处缩进了 4个空格int i_fen_shu;if(i_fen_shu >= 60){// 此处缩进了 4个空格if(i_fen_shu > 80){// 此处缩进了 4个空格printf("优秀\r\n");}else{// 此处缩进了 4个空格printf("及格\r\n");}}else{// 此处缩进了 4个空格printf("不及格\r\n");}
}
通常,大家都能接受的缩进单位是4个空格,C语言中,只要遇到左括号,后面的代码,缩进量就应该增加4个空格。遇到右括号,缩进量减少4个空格。
缩进的好处是,代码块一目了然,不用手动去数括号。当语句嵌套层数很多时,缩进量可以让你快速的匹配语句,确定代码块。
不要担心行数太多
if-else 语句和 while 语句,如果所跟的代码只有一个语句(一个封号),大括号是可以不要的,比如下面两个代码功能是等效的:
int time = 20;if(time != 0){while(time){time --;}}
int time = 20;if(time != 0)while(time)time --;
你甚至可以写成下面这样:
int time = 20; if(time != 0) while(time) time --;
上述三种代码块,我个人认为第一种、第二种也能看。但第三种也不是不行,有一种叫做防御性编程的风格,大概非常推崇这样的。
C语言允许直接用大括号把一段代码括起来,像下面这样:
int main(void)
{int time = 20;{time ++;time += 20;printf("%d\r\n", time);}time --;printf("%d\r\n", time);
}
其中下面五行代码,就是被括起来了,这个的具体应用场景,后面再讲,但是C语言允许这么做。
{time ++;time += 20;printf("%d\r\n", time);}
然后请看下面的代码,循环体是那部分代码,稍微走一下神,就判断失误了:
int main(void)
{int time = 20;while(time --) ;{printf("%d\r\n", time);}
}
如果写成下面这样,看起来是不是会更清爽一些:
int main(void)
{int time = 20;while(time --);{printf("%d\r\n", time);}
}
如果写成下面这样,是不是感觉会更加清爽:
int main(void)
{int time = 20;while(time --){}{printf("%d\r\n", time);}
}
当然,我还是要强调,上述三种写法,没有好坏之分,就像达•芬奇的鸡蛋一样,屁股决定脑袋,位置不一样,自然得出的价值判断会不一样。屁股的位置变了,得出的价值判断,自然也会变。
左括号的位置
C语言的左括号,通常有如下两种风格。
风格1,左括号另起一行:
int main(void)
{int time = 20;while(time){time --;}if(time == 0){printf("%d\r\n", time);}
}
风格2,左括号不起新行:
int main(void){int time = 20;while(time){time --;}if(time == 0){printf("%d\r\n", time);}
}