新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → [推荐]C/C++编程新手错误语录 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 5637 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: [推荐]C/C++编程新手错误语录 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     longshentailang 帅哥哟,离线,有人找我吗?
      
      
      威望:1
      等级:计算机学士学位
      文章:325
      积分:2990
      门派:XML.ORG.CN
      注册:2006/6/20

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给longshentailang发送一个短消息 把longshentailang加入好友 查看longshentailang的个人资料 搜索longshentailang在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看longshentailang的博客楼主
    发贴心情 [推荐]C/C++编程新手错误语录


    1.引言

      还记得当年学数学、英语都有个窍门,那就是搞个错题集。经常复习一下这个错题集,就可以避免下次犯同样的错误。而几乎所有的程序员都是从犯错误开始的,我们也很有必要总结一下编程新手的常见错误,本文的目的在于此。文中所列出的都是笔者在项目开发中接触到的新手真实的言谈,笔者学学文革腔调,姑且称之为“错误语录”。

      2.语录

      (1)“我的程序都是对的,可结果不对”

      想想你的周围,是不是也有人说这样的话?如果你也曾经说过,那就此打住,不要再说这句话,因为这句话只会显示说话者的无知。既然程序都是对的,那为什么结果不对?

      (2)“程序=算法+数据结构”

      如果刚刚学完C语言,我们说这样的话,完全可以理解,而且可以说是正确的。但是如果你是一位即将从事C/C++编程的程序员,那么很遗憾,这个说法只能判错,殊不知,世界上还有另一种说法:

      程序 = 对象 + 消息

      “程序=算法+数据结构”只对面向过程的语言(C)成立,而对面向对象的语言(C++),则只能表述为“程序=对象+消息”。传统的过程式编程语言以过程为中心以算法为驱动,面向对象的编程语言则以对象为中心以消息为驱动。这里的消息是广义的,对象A调用了对象B的成员函数,可看作对象A给B发消息。

      (3)“程序编出来,运行正确就行了”

      运行正确的程序并不一定是好程序,程序员时刻要牢记的一条就是自己写的程序不仅是给自己看的,要让别人也能轻易地看懂。很遗憾,许多的编程新手不能清晰地驾驭软件的结构,对头文件和实现文件的概念含糊不清,写出来的程序可读性很差。

      C程序采用模块化的编程思想,需合理地将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求,在模块的划分上主要依据功能。模块由头文件和实现文件组成,对头文件和实现文件的正确使用方法是:

      规则1 头文件(.h)中是对于该模块接口的声明,接口包括该模块提供给其它模块调用的外部函数及外部全局变量,对这些变量和函数都需在.h中文件中冠以extern关键字声明;

      规则2 模块内的函数和全局变量需在.c文件开头冠以static关键字声明;

      规则3 永远不要在.h文件中定义变量;

      许多程序员对定义变量和声明变量混淆不清,定义变量和声明变量的区别在于定义会产生内存分配的操作,是汇编阶段的概念;而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如:

    /*模块1头文件:module1.h*/
    int a = 5; /* 在模块1的.h文件中定义int a */
    /*模块1实现文件:module1 .c*/
    #include “module1.h” /* 在模块1中包含模块1的.h文件 */
    /*模块2实现文件: module2.c*/
    #include “module1.h” /* 在模块2中包含模块1的.h文件 */
    /*模块2 实现文件:module3 .c*/
    #include “module1.h” /* 在模块3中包含模块1的.h文件 */

      以上程序的结果是在模块1、2、3中都定义了整型变量a,a在不同的模块中对应不同的地址单元,这明显不符合编写者的本意。正确的做法是:

    /*模块1头文件:module1.h*/
    extern int a; /* 在模块1的.h文件中声明int a */
    /*模块1实现文件:module1 .c*/
    #include “module1.h” /* 在模块1中包含模块1的.h文件 */
    int a = 5; /* 在模块1的.c文件中定义int a */
    /*模块2 实现文件: module2 .c*/
    #include “module1.h” /* 在模块2中包含模块1的.h文件 */
    /*模块3 实现文件: module3 .c*/
    #include “module1.h”   /* 在模块3中包含模块1的.h文件 */

      这样如果模块1、2、3操作a的话,对应的是同一片内存单元。

      规则4 如果要用其它模块定义的变量和函数,直接包含其头文件即可。

      许多程序员喜欢这样做,当他们要访问其它模块定义的变量时,他们在本模块文件开头添加这样的语句:

    extern int externVar; 

      抛弃这种做法吧,只要头文件按规则1完成,某模块要访问其它模块中定义的全局变量时,只要包含该模块的头文件即可。

      (4)“数组名就是指针”

      许多程序员对数组名和指针的区别不甚明了,他们认为数组名就是指针,而实际上数组名和指针有很大区别,在使用时要进行正确区分,其区分规则如下:

      规则1 数组名指代一种数据结构,这种数据结构就是数组;

      例如:

    char str[10];
    char *pStr = str;
    cout << sizeof(str) << endl;
    cout << sizeof(pStr) << endl;

      输出结果为:

    10
    4

      这说明数组名str指代数据结构char[10]。

      规则2 数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;

    char str[10];
    char *pStr = str;
    str++; //编译出错,提示str不是左值 
    pStr++; //编译正确

      规则3 指向数组的指针则是另外一种变量类型(在WIN32平台下,长度为4),仅仅意味着数组的存放地址;

      规则4 数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。

      例如:

    void arrayTest(char str[])
    {
    cout << sizeof(str) << endl;   //输出指针长度
        str++; //编译正确
    }
    int main(int argc, char* argv[])
    {
     char str1[10] = "I Love U";
     arrayTest(str1);
    return 0;
    }

      (5)“整形变量为32位”

      整形变量是不是32位这个问题不仅与具体的CPU架构有关,而且与编译器有关。在嵌入式系统的编程中,一般整数的位数等于CPU字长,常用的嵌入式 CPU芯片的字长为8、16、32,因而整形变量的长度可能是8、16、32。在未来64位平台下,整形变量的长度可达到64位。

      长整形变量的长度一般为CPU字长的2倍。

      在数据结构的设计中,优秀的程序员并不会这样定义数据结构(假设为WIN32平台):

    typedef struct tagTypeExample
    {
    unsigned short x;
    unsigned int y;
    }TypeExample;

      他们这样定义:

    #define unsigned short UINT16 //16位无符号整数
    #define unsigned int UINT32 //32位无符号整数
    typedef struct tagTypeExample
    {
    UINT16 x;
    UINT32 y;
    }TypeExample;

      这样定义的数据结构非常具有通用性,如果上述32平台上的数据发送到16位平台上接收,在16位平台上仅仅需要修改UINT16、UINT32的定义:

    #define unsigned int UINT16 //16位无符号整数
    #define unsigned long UINT32 //32位无符号整数

      几乎所有的优秀软件设计文档都是这样定义数据结构的。

      (6)“switch和if …else…可随意替换”

      switch语句和一堆if…else…的组合虽然功能上完全一样,但是给读者的感受完全不一样。if…else…的感觉是进行条件判断,对特例进行特别处理,在逻辑上是“特殊与一般”的关系,而switch给人的感觉是多个条件的关系是并列的,事物之间不存在特殊与一般的关系,完全“对等”。

      譬如:

    //分别对1-10的数字进行不同的处理,用switch
    switch(num)
    {
    case 1:

    case 2:

    }
    //对1-10之间的数字进行特殊处理,用if
    if(num < 10 && num > 1)
    {

    }
    else
    {

    }

      许多时候,虽然不同的代码可实现完全相同的功能,但是给读者的感觉是完全不同的。譬如无条件循环:

    while(1)
    {
    }

      有的程序员这样写:

    for(;;)
    {
    }

      这个语法没有确切表达代码的含义,我们从for(;;)看不出什么,只有弄明白for(;;)在C/C++语言中意味着无条件循环才明白其意。而不懂C/C++语言的读者看到while(1)也可猜到这是一个无条件循环。

      (7)“免得麻烦,把类里面的成员函数都搞成public算了”

      许多人编C++程序的时候,都碰到这样的情况,先前把某个成员函数定义成类的private/protected函数,后来发现又要从外面调用这个函数,就轻易地将成员函数改为public类型的。甚至许多程序员为了避免访问的麻烦,干脆把自己添加的成员函数和成员变量都定义成public类型。

      殊不知,这是一种规划的失败。在类的设计阶段,我们就要很清晰地知道,这个类的成员函数中哪些是这个类的接口,哪些属于这个类内部的成员函数和变量。一般的准则是接口(public成员)应在满足需求的前提下尽可能简单!

      所以不要轻易地将private/protected成员改为public成员,真正的工作应该在规划阶段完成。

      3.结束语

      所有的程序员都要经历一个从糊涂到清晰的过程,文中的错误如果你也犯了,切勿自惭。

      更多的错误语录,希望能在后续文章中陆续推出。


       收藏   分享  
    顶(0)
      




    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/1/15 21:45:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客2
    发贴心情 
    呵呵 当年都是这样过来的。。。。

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/1/16 11:44:00
     
     GoogleAdSense射手座1984-11-30
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/26 15:00:13

    本主题贴数2,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    6,871.094ms