最近看到一个关于编程语言的调查,我发现到目前为止,C 编程语言在全球开发者中仍然稳居前三,如下图所示。
前20名榜单排行榜:
如此多的代码使用C来编写,我想分享我多年学习总结的一些好的C语言编程实践:
一、不要使用gets()和strcpy()
再也不要使用诸如gets()、strcpy()、sprintf()等等这些函数,这已经是一个广为人知的一个事实(好吧,缓冲区溢出大家都知道吧),但这些函数仍然在库中,以用来支持那些已经使用这些函数的代码。如果你使用man手册关于gets()的说明,会发现:
Never use gets(). Because it is impossible to tell without knowing the data in advance how many characters gets() will read, and because gets() will continue to store characters past the end of the buffer, it is extremely dangerous to use. It has been used to break computer security. Use fgets() instead.
当你使用这些函数时,甚至一些编译器如gcc 都会给你警告,gcc 给出类似下面的警告:
warning: the `gets’ function is dangerous and should not be used.
足见整个开发环境已经意识到这个问题,所以我们应该重视起来,使用像fgets()、strncpy()、snprinf()等这些函数,它们更好地根据缓冲区的大小来控制输入。
二、函数只能有一个返回点
在一个函数中随意地使用return语句,这是一个很不好的编程习惯,函数有多个返回点会增加代码管理的复杂度。通常一个代码由多个开发者完成就可以出现这个问题,但即使如此,任何人在开发的过程中都会造成这个问题。记住仅有一个返回点是好的编程习惯。
举例来说:
#include<stdio.h> ... ... int main(void) { ... ... ... if(/*some condition*/) return 1; // Not a good programming practice else if(/*some condition*/) return 2; // Not a good programming practice else return -1; // Not a good programming practice }
更好的代码应该这样写:
#include<stdio.h> ... ... int main(void) { int ret = 0; ... ... ... if(/*some condition*/) ret = 1; else if(/*some condition*/) ret = 2; else ret = -1; return ret; }
三、适当的地方进行基本的优化
有时当我们写一个逻辑时,我们会忽略事情是如何通过这个逻辑完成的。我的意思是,我们应该在适当的地方进行一些基本的优化。
看下面的一段代码:
... ... int len = 0; char *buff = "Linux"; while(/*Some condition*/) { ... ... // updating 'len' here ... if(len > strlen(buff)) { //Do some stuff here } ... ... ... } ... ... ...
看看上面的代码,函数strlen()在循环中一次又一次地计算buff指向的字符串的长度,这是没经过优化的代码,因为buff指向的缓冲区在 while循环中始终没有改变,因此长度也不会改变。更好的做法是,在while循环之前调用一次strlen()函数,用一个变量来保存buff的长 度,在循环中使用这个变量就可以了。像下面这种做法:
... ... ... int len = 0; int buff_len = 0; char *buff = "Linux"; buff_len = strlen(buff); while(/*Some condition*/) { ... ... // updating 'len' here ... if(len > buff_len) { //Do some stuff here } ... ... ... } ... ... ...
这种做法保证了你的代码在适当的地方进行了基本的优化,在大数据量的情况下,会省不少时间。
四、总是使用相应的库调用函数
假设你想在Linux下通过C语言打开一个文件,有两个函数可用——fopen()和open()。fopen()是一个库函数调用,而open()是一个系统调用,你会选择哪个函数呢?
如果你选择的是open()系统调用,那么我必须说如果在相应的库函数存在的情况下,选择一个系统调用不是一个好的编程实践。这是因为:
- 系统调用与系统相关的。举例来说,在Linux系统下写的代码不能确保也能运行在非Linux系统下。然而,带有C库的系统都有相应的库函数,这儿的问题是移植性的问题。
- 其次,系统调用与库函数比较起来相对来说更耗时。
所以,确保任何地方可用均使用库调用。
五、用宏替代常量
假设你需要写成千上万行代码,涉及到上百个文件,你在整个文件和代码中用到一个固定值,比如说1024。现在突然你意识到要将这个值改成2048, 你将怎么做?你要确定所有使用这个值的位置,并手动将值改成2048;或者,在你的IDE中使用“查找”命令,然后试图用2048来替换1024。
然而,这两种方法都不是很好的,更好的编程实践是在头文件中使用C语言宏定义这个值,在其它需要使用这个值的地方用宏来代替,这样,如果需要改变这个值的时候,只需要改变一个地方即可。同时,给宏取一个有关联的名字,可以使这个值用来做什么变得非常清晰。
编译自:5 good C programming practices from http://mylinuxbook.com/5-good-c-programming-practices/