您的位置:首页 > 编程语言 > C语言/C++

嵌入式开发编程修养——C语言篇

2016-12-01 23:07 148 查看
什么是好的程序员?是不是懂得很多技术细节?还是懂底层编程?还是编程速度比较快?

我觉得都不是。对于一些技术细节来说和底层的技术,只要看帮助,查资料就能找到,对

于速度快,只要编得多也就熟能生巧了。

我认为好的程序员应该有以下几方面的素质:

1、有专研精神,勤学善问、举一反三。

2、积极向上的态度,有创造性思维。

3、与人积极交流沟通的能力,有团队精神。

4、谦虚谨慎,戒骄戒燥。

5、写出的代码质量高。包括:代码的稳定、易读、规范、易维护、专业。

这些都是程序员的修养,这里我想谈谈“编程修养”,也就是上述中的第5点。我觉得,如

果我要了解一个作者,我会看他所写的小说,如果我要了解一个画家,我会看他所画的图

画,如果我要了解一个工人,我会看他所做出来的产品,同样,如果我要了解一个程序员

,我想首先我最想看的就是他的程序代码,程序代码可以看出一个程序员的素质和修养,

程序就像一个作品,有素质有修养的程序员的作品必然是一图精美的图画,一首美妙的歌

曲,一本赏心悦目的小说。

我看过许多程序,没有注释,没有缩进,胡乱命名的变量名,等等,等等,我把这种人统

称为没有修养的程序,这种程序员,是在做创造性的工作吗?不,完全就是在搞破坏,他

们与其说是在编程,还不如说是在对源程序进行“加密”,这种程序员,见一个就应该开

除一个,因为他编的程序所创造的价值,远远小于需要在上面进行维护的价值。

程序员应该有程序员的修养,那怕再累,再没时间,也要对自己的程序负责。我宁可要那

种动作慢,技术一般,但有良好的写程序风格的程序员,也不要那种技术强、动作快的“

搞破坏”的程序员。有句话叫“字如其人”,我想从程序上也能看出一个程序员的优劣。

因为,程序是程序员的作品,作品的好坏直截关系到程序员的声誉和素质。而“修养”好

的程序员一定能做出好的程序和软件。

有个成语叫“独具匠心”,意思是做什么都要做得很专业,很用心,如果你要做一个“匠

”,也就是造诣高深的人,那么,从一件很简单的作品上就能看出你有没有“匠”的特性

,我觉得做一个程序员不难,但要做一个“程序匠”就不简单了。编程序很简单,但编出

有质量的程序就难了。

我在这里不讨论过深的技术,我只想在一些容易让人忽略的东西上说一说,虽然这些东西

可能很细微,但如果你不注意这些细微之处的话,那么他将会极大的影响你的整个软件质

量,以及整个软件程的实施,所谓“千里之堤,毁于蚁穴”。

“细微之处见真功”,真正能体现一个程序的功底恰恰在这些细微之处。

这就是程序员的——编程修养。我总结了在用C/C++语言(主要是C语言)进行程序写作上

的三十二个“修养”,通过这些,你可以写出质量高的程序,同时也会让看你程序的人渍

渍称道,那些看过你程序的人一定会说:“这个人的编程修养不错”。

————————————————————————

01、版权和版本

02、缩进、空格、换行、空行、对齐

03、程序注释

04、函数的[in][out]参数

05、对系统调用的返回进行判断

06、if 语句对出错的处理

07、头文件中的#ifndef

08、在堆上分配内存

09、变量的初始化

10、h和c文件的使用

11、出错信息的处理

12、常用函数和循环语句中的被计算量

13、函数名和变量名的命名

14、函数的传值和传指针

15、修改别人程序的修养

16、把相同或近乎相同的代码形成函数和宏

17、表达式中的括号

18、函数参数中的const

19、函数的参数个数

20、函数的返回类型,不要省略

21、goto语句的使用

22、宏的使用

23、static的使用

24、函数中的代码尺寸

25、typedef的使用

26、为常量声明宏

27、不要为宏定义加分号

28、||和&&的语句执行顺序

29、尽量用for而不是while做循环

30、请sizeof类型而不是变量

31、不要忽略Warning

32、书写Debug版和Release版的程序

21、goto语究 使劲

22、宏的使用

23、static的使用

24、函数中的代码尺寸

25、typedef的使用

26、为常量声明宏

27、不要为宏定义加分号

28、||和&&的语句执行顺序

29、尽量用for而不是while做循环

30、请sizeof类型而不是变量

31、不要忽略Warning

32、书写Debug版和Release版的程序

————————————————————————

1、版权和版本

———————

好的程序员会给自己的每个函数,每个文件,都注上版权和版本。

对于C/C++的文件,文件头应该有类似这样的注释:

/[b]**************************************************************[/b]

* 文件名:network.c

* 文件描述:网络通讯函数集

* 创建人: Hao Chen, 2003年2月3日

* 版本号:1.0

* 修改记录:

*

[b]**************************************************************[/b]/

而对于函数来说,应该也有类似于这样的注释:

/*================================================================

* 函 数 名:XXX

* 参 数:

* type name [IN] : descripts

* 功能描述:

* ..

* 返 回 值:成功TRUE,失败FALSE

* 抛出异常:

* 作 者:ChenHao 2003/4/2

================================================================*/

这样的描述可以让人对一个函数,一个文件有一个总体的认识,对代码的易读性和易维护

性有很大的好处。这是好的作品产生的开始。

2、缩进、空格、换行、空行、对齐

————————————————

i) 缩进应该是每个程序都会做的,只要学程序过程序就应该知道这个,但是我仍然看过不

缩进的程序,或是乱缩进的程序,如果你的公司还有写程序不缩进的程序员,请毫不犹豫

的开除他吧,并以破坏源码罪起诉他,还要他赔偿读过他程序的人的精神损失费。缩进,

这是不成文规矩,我再重提一下吧,一个缩进一般是一个TAB键或是4个空格。(最好用TAB

键)

ii) 空格。空格能给程序代来什么损失吗?没有,有效的利用空格可以让你的程序读进来

更加赏心悦目。而不一堆表达式挤在一起。看看下面的代码:

ha=(ha*128+*key++)%tabPtr->size;

ha = ( ha * 128 + *key++ ) % tabPtr->size;

有空格和没有空格的感觉不一样吧。一般来说,语句中要在各个操作符间加空格,函

数调用时,要以各个参数间加空格。如下面这种加空格的和不加的:

if ((hProc=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid))==NULL){

}

if ( ( hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid) ) == NULL ){

}

iii) 换行。不要把语句都写在一行上,这样很不好。如:

for(i=0;i

以“注释 — 添加”方式修改别人的程序,要好于直接删除别人的程序。

16、把相同或近乎相同的代码形成函数和宏

—————————————————————

有人说,最好的程序员,就是最喜欢“偷懒”的程序,其中不无道理。

如果你有一些程序的代码片段很相似,或直接就是一样的,请把他们放在一个函数中。而

如果这段代码不多,而且会被经常使用,你还想避免函数调用的开销,那么就把他写成宏

吧。

千万不要让同一份代码或是功能相似的代码在多个地方存在,不然如果功能一变,你就要

修改好几处地方,这种会给维护带来巨大的麻烦,所以,做到“一改百改”,还是要形成

函数或是宏。

17、表达式中的括号

—————————

如果一个比较复杂的表达式中,你并不是很清楚各个操作符的忧先级,即使是你很清楚优

先级,也请加上括号,不然,别人或是自己下一次读程序时,一不小心就看走眼理解错了

,为了避免这种“误解”,还有让自己的程序更为清淅,还是加上括号吧。

比如,对一个结构的成员取地址:

GetUserAge( &( UserInfo->age ) );

虽然,&UserInfo->age中,->操作符的优先级最高,但加上一个括号,会让人一眼就看明

白你的代码是什么意思。

再比如,一个很长的条件判断:

if ( ( ch[0] >= ‘0’ || ch[0] <= ‘9’ ) &&

( ch[1] >= ‘a’ || ch[1] <= ‘z’ ) &&

( ch[2] >= ‘A’ || ch[2] <= ‘Z’ ) )

括号,再加上空格和换行,你的代码是不是很容易读懂了?

18、函数参数中的const

———————————

对于一些函数中的指针参数,如果在函数中只读,请将其用const修饰,这样,别人一读到

你的函数接口时,就会知道你的意图是这个参数是[in],如果没有const时,参数表示[in/

out],注意函数接口中的const使用,利于程序的维护和避免犯一些错误。

虽然,const修饰的指针,如:const char* p,在C中一点用也没有,因为不管你的声明是

不是const,指针的内容照样能改,因为编译器会强制转换,但是加上这样一个说明,有利

于程序的阅读和编译。因为在C中,修改一个const指针所指向的内存时,会报一个Warning

。这会引起程序员的注意。

C++中对const定义的就很严格了,所以C++中要多多的使用const,const的成员函数,cons

t的变量,这样会对让你的代码和你的程序更加完整和易读。(关于C++的const我就不多说

了)

19、函数的参数个数(多了请用结构)

—————————————————

函数的参数个数最好不要太多,一般来说6个左右就可以了,众多的函数参数会让读代码的

人一眼看上去就很头昏,而且也不利于维护。如果参数众多,还请使用结构来传递参数。

这样做有利于数据的封装和程序的简洁性。

也利于使用函数的人,因为如果你的函数个数很多,比如12个,调用者很容易搞错参数的

顺序和个数,而使用结构struct来传递参数,就可以不管参数的顺序。

而且,函数很容易被修改,如果需要给函数增加参数,不需要更改函数接口,只需更改结

构体和函数内部处理,而对于调用函数的程序来说,这个动作是透明的。

20、函数的返回类型,不要省略

——————————————

我看到很多程序写函数时,在函数的返回类型方面不太注意。如果一个函数没有返回值,

也请在函数前面加上void的修饰。而有的程序员偷懒,在返回int的函数则什么不修饰(因

为如果不修饰,则默认返回int),这种习惯很不好,还是为了原代码的易读性,加上int

吧。

所以函数的返回值类型,请不要省略。

另外,对于void的函数,我们往往会忘了return,由于某些C/C++的编译器比较敏感,会报

一些警告,所以即使是void的函数,我们在内部最好也要加上return的语句,这有助于代

码的编译。

21、goto语句的使用

—————————

N年前,软件开发的一代宗师——迪杰斯特拉(Dijkstra)说过:“goto statment is

harmful !!”,并建议取消goto语句。因为goto语句不利于程序代码的维护性。

这里我也强烈建议不要使用goto语句,除非下面的这种情况:

#define FREE(p) if(p) { /

free(p); /

p = NULL; /

}

main()

main()

{

char *fname=NULL, *lname=NULL, *mname=NULL;

fname = ( char* ) calloc ( 20, sizeof(char) );

if ( fname == NULL ){

goto ErrHandle;

}

lname = ( char* ) calloc ( 20, sizeof(char) );

if ( lname == NULL ){

goto ErrHandle;

}

mname = ( char* ) calloc ( 20, sizeof(char) );

if ( mname == NULL ){

goto ErrHandle;

}

……

ErrHandle:

ErrHandle:

FREE(fname);

FREE(lname);

FREE(mname);

ReportError(ERR_NO_MEMOEY);

}

也只有在这种情况下,goto语句会让你的程序更易读,更容易维护。(在用嵌C来对数据库

设置游标操作时,或是对数据库建立链接时,也会遇到这种结构)

22、宏的使用

——————

很多程序员不知道C中的“宏”到底是什么意思?特别是当宏有参数的时候,经常把宏和函

数混淆。我想在这里我还是先讲讲“宏”,宏只是一种定义,他定义了一个语句块,当程

序编译时,编译器首先要执行一个“替换”源程序的动作,把宏引用的地方替换成宏定义

的语句块,就像文本文件替换一样。这个动作术语叫“宏的展开”

使用宏是比较“危险”的,因为你不知道宏展开后会是什么一个样子。例如下面这个宏:

#define MAX(a, b) a>b?a:b

当我们这样使用宏时,没有什么问题: MAX( num1, num2 ); 因为宏展开后变成

num1>num2?num1:num2;。 但是,如果是这样调用的,MAX( 17+32, 25+21 ); 呢,编译时

出现错误,原因是,宏展开后变成:17+32>25+21?17+32:25+21,哇,这是什么啊?

所以,宏在使用时,参数一定要加上括号,上述的那个例子改成如下所示就能解决问题了



#define MAX( (a), (b) ) (a)>(b)?(a):(b)

即使是这样,也不这个宏也还是有Bug,因为如果我这样调用 MAX(i++, j++); , 经过这

个宏以后,i和j都被累加了两次,这绝不是我们想要的。

所以,在宏的使用上还是要谨慎考虑,因为宏展开是的结果是很难让人预料的。而且虽然

,宏的执行很快(因为没有函数调用的开销),但宏会让源代码澎涨,使目标文件尺寸变

大,(如:一个50行的宏,程序中有1000个地方用到,宏展开后会很不得了),相反不能

让程序执行得更快(因为执行文件变大,运行时系统换页频繁)。

因此,在决定是用函数,还是用宏时得要小心。

。 开始使劲

#define MAX( (a), (b) ) (a)>(b)?(a):(b)

即使是这样,也不这个宏也还是有Bug,因为如果我这样调用 MAX(i++, j++); , 经过这

个宏以后,i和j都被累加了两次,这绝不是我们想要的。

所以,在宏的使用上还是要谨慎考虑,因为宏展开是的结果是很难让人预料的。而且虽然

,宏的执行很快(因为没有函数调用的开销),但宏会让源代码澎涨,使目标文件尺寸变

大,(如:一个50行的宏,程序中有1000个地方用到,宏展开后会很不得了),相反不能

让程序执行得更快(因为执行文件变大,运行时系统换页频繁)。

因此,在决定是用函数,还是用宏时得要小心。

23、static的使用

————————

static关键字,表示了“静态”,一般来说,他会被经常用于变量和函数。一个static的

变量,其实就是全局变量,只不过他是有作用域的全局变量。比如一个函数中的static变

量:

char*

getConsumerName()

{

static int cnt = 0;

….

cnt++;

….

}

cnt变量的值会跟随着函数的调用次而递增,函数退出后,cnt的值还存在,只是cnt只能在

函数中才能被访问。而cnt的内存也只会在函数第一次被调用时才会被分配和初始化,以后

每次进入函数,都不为static分配了,而直接使用上一次的值。

对于一些被经常调用的函数内的常量,最好也声明成static(参见第12条)

但static的最多的用处却不在这里,其最大的作用的控制访问,在C中如果一个函数或是一

个全局变量被声明为static,那么,这个函数和这个全局变量,将只能在这个C文件中被访

问,如果别的C文件中调用这个C文件中的函数,或是使用其中的全局(用extern关键字)

,将会发生链接时错误。这个特性可以用于数据和程序保密。

24、函数中的代码尺寸

——————————

一个函数完成一个具体的功能,一般来说,一个函数中的代码最好不要超过600行左右,越

少越好,最好的函数一般在100行以内,300行左右的孙函数就差不多了。有证据表明,一

个函数中的代码如果超过500行,就会有和别的函数相同或是相近的代码,也就是说,就可

以再写另一个函数。

另外,函数一般是完成一个特定的功能,千万忌讳在一个函数中做许多件不同的事。函数

的功能越单一越好,一方面有利于函数的易读性,另一方面更有利于代码的维护和重用,

功能越单一表示这个函数就越可能给更多的程序提供服务,也就是说共性就越多。

虽然函数的调用会有一定的开销,但比起软件后期维护来说,增加一些运行时的开销而换

来更好的可维护性和代码重用性,是很值得的一件事。

25、typedef的使用

—————————

typedef是一个给类型起别名的关键字。不要小看了它,它对于你代码的维护会有很好的作

用。比如C中没有bool,于是在一个软件中,一些程序员使用int,一些程序员使用short,

会比较混乱,最好就是用一个typedef来定义,如:

typedef char bool;

一般来说,一个C的工程中一定要做一些这方面的工作,因为你会涉及到跨平台,不同的平

台会有不同的字长,所以利用预编译和typedef可以让你最有效的维护你的代码,如下所示



#ifdef SOLARIS2_5

typedef boolean_t BOOL_T;

#else

#else

typedef int BOOL_T;

#endif

typedef short INT16_T;

typedef unsigned short UINT16_T;

typedef int INT32_T;

typedef unsigned int UINT32_T;

#ifdef WIN32

typedef _int64 INT64_T;

#else

typedef long long INT64_T;

#endif

typedef float FLOAT32_T;

typedef char* STRING_T;

typedef unsigned char BYTE_T;

typedef time_t TIME_T;

typedef INT32_T PID_T;

使用typedef的其它规范是,在结构和函数指针时,也最好用typedef,这也有利于程序的

易读和可维护性。如:

typedef struct _hostinfo {

HOSTID_T host;

INT32_T hostId;

STRING_T hostType;

STRING_T hostModel;

FLOAT32_T cpuFactor;

INT32_T numCPUs;

INT32_T nDisks;

INT32_T memory;

INT32_T swap;

} HostInfo;

typedef INT32_T (*RsrcReqHandler)(

void *info,

JobArray *jobs,

AllocInfo *allocInfo,

AllocList *allocList);

C++中这样也是很让人易读的:

typedef CArray

#ifdef DEBUG
void TRACE(char* fmt, ...)
{
......
}
#else
#define TRACE(char* fmt, ...)
#endif


于是,让所有的程序都用TRACE输出调试信息,只需要在在编译时加上一个参数“-DDEBUG ”,如:

gcc -DDEBUG -o target target.c

于是,预编译器发现DEBUG变量被定义了,就会使用TR
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 嵌入式开发
相关文章推荐