注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

风雨启示录

欢迎光临我的网店优优我心:http://uumyheart.taobao.com

 
 
 

日志

 
 

C语言编程修养,规范学习  

2010-03-17 15:33:12|  分类: C语言学习笔记 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
历时2天的学习笔记,重新学习下,感觉不错:

/******************************************************************/

/**************************    2010-03-16   **********************/

/**************************   edit by LIHF  **********************/

/*****************************************************************/

1、注释

    尽量使用块注释"/**/",因为一些老版本的C编译器不支持行注释"//"

2、块注释不能嵌套,可以用预编译来完成这个功能

    #if 0

    #endif

    括起来的编码将不会被编译

    例如:

        #if 0

            vPrintf("config size=%d\n", CONFIG_SIZE);

            dump_data("config:", config_data, CONFIG_SIZE);

        #endif

   

/******************************************************************/

/**************************    2010-03-17   **********************/

/**************************   edit by LIHF  **********************/

/*****************************************************************/

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

    对于参数的是指针的函数 ,首先要判断指针是否为空,如果是空,程序不做任何

    处理,系统可能导致崩溃

    写有参数的函数时,首要工作,就是要对传进来的所有参数进行合法性检查。而对于传

    出的参数也应该进行检查,这个动作当然应该在函数的外部,也就是说,调用完一个函数后,

    应该对其传出的值进行检查。

   

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

    对于一些系统调用,比如打开文件,要对fopen返回的指针做些判断

    fp = fopen("log.txt", "a")?

    if ( fp == NULL ){

        printf("Error: open file error\n");

        return FALSE;

    }

    其它还有许多,比如:socket 返回的socket 号,malloc 返回的内存。请对这些系统调

    用返回的东西进行判断。

   

5、if语句对出错的处理

    先看下面的代码

    if ( ch >= '0' && ch <= '9' )

    {

        /* 正常处理代码*/;

    }

    else

    {

        /* 输出错误信息*/

        printf("error ......\n");

        return ( FALSE );

    }

    这种结构很不好,特别是如果“正常处理代码”很长时,对于这种情况,最好不要用else。

    先判断错误

    if ( ch < '0' || ch > '9' )

    {

        /* 输出错误信息*/

        printf("error ......\n");

        return ( FALSE );

    }

    /* 正常处理代码*/

   

6、头文件中的#ifndef

    这是一个很关键的东西。比如你有两个C 文件,这

    两个C 文件都include 了同一个头文件。而编译时,这两个C 文件要一同编译成一个可运行

    文件,于是问题来了,大量的声明冲突。

    还是把头文件的内容都放在#ifndef 和#endif 中吧

    例子:

        stdio.h

        #ifndef _STDIO_H_

        #define _STDIO_H_

        ......

        #endif

   

7、在堆上分配内存

    栈"stack"和堆"heap":stack上分配的内训系统会自动释放,heap上分配的内存,系统不释放,

    即使程序退出,那一块内存还在那里。stack一般是静态分配内存,heap上一般是动态分配内存。

    由malloc 系统函数分配的内存就是从heap上分配内存。从heap上分配的内存一定要自己释放;

    对于malloc 和free 的操作有以下规则:

        1) 配对使用,有一个malloc,就应该有一个free。(C++中对应为new 和delete)

        2) 尽量在同一层上使用,不要像上面那种,malloc 在函数中,而free 在函数外。最好在同

            一调用层上使用这两个函数。

        3) malloc 分配的内存一定要初始化。free 后的指针一定要设置为NULL。

  

8、变量的初始化

    1) 对malloc 分配的内存进行memset 清零操作。(可以使用calloc 分配一块全零的内存)

    2) 对一些栈上分配的struct 或数组进行初始化。(最好也是清零)

        不过话又说回来了,初始化也会造成系统运行时间有一定的开销,所以,也不要对所有

        的变量做初始化,这个也没有意义;

    而对于全局变量,和静态变量,一定要声明时就初始化。因为你不知道它第一次会在哪

    里被使用。所以使用前初始这些变量是比较不现实的,一定要在声明时就初始化它们。如:

    Links *plnk = NULL? /* 对于全局变量plnk 初始化为NULL */

   

9、h文件和c文件的使用

    H 文件和C 文件最大的用处就是声明和实现分开。

    有些人喜欢把函数写在H 文件中,这种习惯很不好。

    最后,有一个最需要注意的地方就是:带初始化的全局变量不要放在H 文件中!

    例如有一个处理错误信息的结构:

    char* errmsg[] = {

    /* 0 */ "No error",

    /* 1 */ "Open file error",

    /* 2 */ "Failed in sending/receiving a message",

    /* 3 */ "Bad arguments",

    /* 4 */ "Memeroy is not enough",

    /* 5 */ "Service is down? try later",

    /* 6 */ "Unknow information",

    /* 7 */ "A socket operation has failed",

    /* 8 */ "Permission denied",

    /* 9 */ "Bad configuration file format",

    /* 10 */ "Communication time out",

    ......

    ......

    };

    请不要把这个东西放在头文件中,因为如果你的这个头文件被5 个函数库(.lib 或是.a)

    所用到,于是他就被链接在这5 个.lib 或.a 中,而如果你的一个程序用到了这5 个函数库中

    的函数,并且这些函数都用到了这个出错信息数组。那么这份信息将有5 个副本存在于你的

    执行文件中。如果你的这个errmsg 很大的话,而且你用到的函数库更多的话,你的执行文

    件也会变得很大。

    正确的写法应该把它写到C 文件中,然后在各个需要用到errmsg 的C 文件头上加上

    extern char* errmsg[] 的外部声明,让编译器在链接时才去管他,这样一来,就只会有一个

    errmsg 存在于执行文件中,而且,这样做很利于封装。

   

10、出错信息的处理

    你会处理出错信息吗?它并不是简单的输出。看下面的示例:  

    if ( p == NULL ){

        printf ( "ERR: The pointer is NULL\n" )?

    }

    正确的方法是:

        如果要管理错误信息,那就要有以下的处理:

    /* 声明出错代码*/

    #define ERR_NO_ERROR 0 /* No error */

    #define ERR_OPEN_FILE 1 /* Open file error */

    #define ERR_SEND_MESG 2 /* sending a message error */

    #define ERR_BAD_ARGS 3 /* Bad arguments */

    #define ERR_MEM_NONE 4 /* Memeroy is not enough */

    #define ERR_SERV_DOWN 5 /* Service down try later */

    #define ERR_UNKNOW_INFO 6 /* Unknow information */

    #define ERR_SOCKET_ERR 7 /* Socket operation failed */

    #define ERR_PERMISSION 8 /* Permission denied */

    #define ERR_BAD_FORMAT 9 /* Bad configuration file */

    #define ERR_TIME_OUT 10 /* Communication time out */

    /* 声明出错信息*/

    char* errmsg[] = {

        /* 0 */ "No error",

        /* 1 */ "Open file error",

        /* 2 */ "Failed in sending/receiving a message",

        /* 3 */ "Bad arguments",

        /* 4 */ "Memeroy is not enough",

        /* 5 */ "Service is down? try later",

        /* 6 */ "Unknow information",

        /* 7 */ "A socket operation has failed",

        /* 8 */ "Permission denied",

        /* 9 */ "Bad configuration file format",

        /* 10 */ "Communication time out",

    }?

    /* 声明错误代码全局变量*/

    long errno = 0?

    /* 打印出错信息函数*/

    void perror( char* info)

    {

        if ( info ){

        printf("%s: %s\n", info, errmsg[errno] )?

        return?

    }

    printf("Error: %s\n", errmsg[errno] )?

    }

    这个基本上是ANSI 的错误处理实现细节了,于是当你程序中有错误时你就可以这样处理:

    bool CheckPermission( char* userName )

    {

        if ( strcpy(userName, "root") != 0 ){

        errno = ERR_PERMISSION_DENIED?

        return (FALSE)?

    }

    ...

    }

    main()

    {

        ...

        if (! CheckPermission( username ) ){

        perror("main()")?

        }

        ...

    }

    一个即有共性,也有个性的错误信息处理,这样做有利同种错误出一样的信息,统一用

    户界面,而不会因为文件打开失败,A 程序员出一个信息,B 程序员又出一个信息。而且这

    样做,非常容易维护。代码也易读。

    当然,物极必反,也没有必要把所有的输出都放到errmsg 中,抽取比较重要的出错信息或

    是提示信息是其关键,但即使这样,这也包括了大多数的信息。

   

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

    我看到许多程序对变量名和函数名的取名很草率,特别是变量名,什么a,b,c,aa,bb,cc,还有

    什么flag1,flag2, cnt1, cnt2,这同样是一种没有“修养”的行为。即便加上好的注释。好的变量

    名或是函数名,我认为应该有以下的规则:

        1) 直观并且可以拼读,可望文知意,不必“解码”。

        2) 名字的长度应该即要最短的长度,也要能最大限度的表达其含义。

        3) 不要全部大写,也不要全部小写,应该大小写都有,如:GetLocalHostName 或是

            UserAccount。

        4) 可以简写,但简写得要让人明白,如: ErrorCode >

            ErrCode, ServerListener >

            ServLisner,UserAccount >

            UsrAcct 等。

        5) 为了避免全局函数和变量名字冲突,可以加上一些前缀,一般以模块简称做为前缀。

        6) 全局变量统一加一个前缀或是后缀,让人一看到这个变量就知道是全局的。

        7) 用匈牙利命名法命名函数参数,局部变量。但还是要坚持“望文生意”的原则。

        8) 与标准库(如:STL)或开发库(如:MFC)的命名风格保持一致。

       

12、修改别人程序的修养

    修改别人的程序时,请不要删除别人的程序,如果你觉得别人的程序有所不妥,

    请注释掉,然后添加自己的处理程序,必竟,你不可能100%的知道别人的意图;

    Eg:

        /* -----------Added by haoel 2003/04/12---------- */

        char* p = ( char* )calloc( 10, sizeof char )?

        /* -----------------------------------------------*/

       

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

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

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

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

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

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

    是宏。

   

14、函数参数中的const

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

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

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

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

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

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

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

   

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

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

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

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

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

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

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

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

   

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

    如果一个函数没有返回值,也请在函数前面加上void 的修饰。而有的程序员偷懒,

    在返回int 的函数则什么不修饰(因为如果不修饰,则默认返回int),这种习惯很不好,还是为了原代码的易读性,加上int 吧。

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

   

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

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

    于代码的编译。

   

17、static 的使用

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

    量,其实就是全局变量,只不过他是有作用域的全局变量。

   

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

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

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

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

   

18、函数中的代码尺寸

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

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

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

    可以再写另一个函数。

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

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

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

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

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

   

19、typedef 的使用

    typedef 是一个给类型起别名的关键字。

    Eg:

        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

       

        HostInfo *H;

       

20、为常量声明宏

    全局变量应该尽量的少用,全局变量不利于封装,也不利于维护,而且对程序执行空间有一定

    的开销,一不小心就造成系统换页,造成程序执行速度效率等问题。所以声明成宏,即可以免去全局变量的开销,也会有速度上

    的优势。

   

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

22、请sizeof 类型而不是变量

23、不要忽略Warning

    对于一些编译时的警告信息,请不要忽视它们。虽然,这些Warning 不会妨碍目标代码

    的生成,但这并不意味着你的程序就是好的。必竟,并不是编译成功的程序才是正确的,编

    译成功只是万里长征的第一步,后面还有大风大浪在等着你。从编译程序开始,不但要改正

    每个error,还要修正每个warning。这是一个有修养的程序员该做的事。

    一般来说,一面的一些警告信息是常见的:

        1)声明了未使用的变量。(虽然编译器不会编译这种变量,但还是把它从源程序中注释

            或是删除吧)

        2)使用了隐晦声明的函数。(也许这个函数在别的C 文件中,编译时会出现这种警告,

            你应该这使用之前使用extern 关键字声明这个函数)

        3)没有转换一个指针。(例如malloc 返回的指针是void 的,你没有把之转成你实际类

            型而报警,还是手动的在之前明显的转换一下吧)

        4)类型向下转换。(例如:float f = 2.0? 这种语句是会报警告的,编译会告诉你正试图

            把一个double 转成float,你正在阉割一个变量,你真的要这样做吗?还是在2.0 后面加个f

            吧,不然,2.0 就是一个double,而不是float 了)

      

  评论这张
 
阅读(646)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018