梁 兄

QQ: 160216918 QQ群: 26678700 MSN: lb_bing@hotmail.com

  C++博客 :: 首页 :: 联系 :: 聚合  :: 管理
  53 Posts :: 5 Stories :: 359 Comments :: 0 Trackbacks

1. 判断一个字符串是否为空:
 已有代码:
   if ( lpcLocalZone == NULL || strlen( lpcLocalZone ) == 0 ) {
   }
   
 问题分析:
   使用strlen()函数判断一个字符串为空,是浪费CPU的,因为大部分正常流程情况下,
     字符串都不为空,还比较长的话,就需要时间O(n)了。
    
 建议:
   if ( NULL == lpcLocalZone || '\0' == lpcLocalZone[ 0 ] ) {
   }
   字符串的定义是'\0'为结束符的字符序列,所以用判断第一个字符是否是'\0'才是本质。
   
2. 检查字符串的长度:
 已有代码:
   int JudgeValidSysUser( const char * asString )  {
   int liRet = 0;
   
   if( ( asString[ 0 ] >= 'a' && asString[ 0 ] <= 'z' )
    || ( asString[ 0 ] >= 'A' && asString[ 0 ] <= 'Z' ) ) {
        if ( strlen( asString ) <= 64  ) {
            unsigned i = 1 ;
           
            while( i < strlen( asString ) ) {
                if( asString[ i ] == '_' || asString[ i ] == '-' || isalnum( asString[ i ] )) {
                    continue; // 合法字符
                } else {
                    break ; // 非法字符
                }
               
                i++ ;
            }
           
            if ( i == strlen ( asString ) ) {
                liRet = 1 ; // 合法用户名
            }
        }
      }
     
      return liRet ;
    }
   
   问题分析:
      以上的函数是判断一个系统帐号是否由规定字符组成,也就是首字符要是字母,其它字符要是字母、
   数字、下划线或横线,最长64。问题是每次判断字符串长度时,都是通过strlen( asString )直接计算的,特别
   是while循环里的代码while( i < strlen( asString ) ),一个正常合法的字符串长度为n,那么要调用
   strlen( asString )的次数是n + 1次!我们要注意到:asString在整个函数里都没有被修改,也就是
   长度不变,那么基于这个实情,我们很容易想到只要求一次int liLen = strlen ( asString ), 以后
   都用liLen来判断就可以了。
  
   建议:
    int JudgeValidSysUser( const char * asString )  {
   int liRet = 0;
   
   if( ( asString[ 0 ] >= 'a' && asString[ 0 ] <= 'z' )
    || ( asString[ 0 ] >= 'A' && asString[ 0 ] <= 'Z' ) ) {
    int liLen = strlen( asString );
    
        if ( liLen <= 64  ) {
            unsigned i = 1 ;
           
            while( i < liLen ) {
                if( asString[ i ] == '_' || asString[ i ] == '-' || isalnum( asString[ i ] )) {
                    continue; // 合法字符
                } else {
                    break ; // 非法字符
                }
               
                i++ ;
            }
           
            if ( i == liLen ) {
                liRet = 1 ; // 合法用户名
            }
        }
      }
     
      return liRet ;
    }
   
   我自己喜欢的写法,根本不用调用strlen( asString ):
   int JudgeValidSysUser( const char * asString )  {
   int liRet = 0;
   
   if( ( asString[ 0 ] >= 'a' && asString[ 0 ] <= 'z' )
    || ( asString[ 0 ] >= 'A' && asString[ 0 ] <= 'Z' ) ) {
      int i;
            for( i = 1; i <= 64 && '\0' != asString[ i ]; i++ ) {
                if( asString[ i ] == '_' || asString[ i ] == '-' ) {
                    continue; // 合法字符
                } else if( isalnum( asString[ i ] ) ) {
                    continue; // 合法字符
                } else {
                    break; // 非法字符
                }
            }

            if( 64 < i ) {
                liRet = 0 ;// 长度太长
            } else if( '\0' != asString[ i ] ) {
                liRet = 0 ;// 非法字符跳出来的
            } else {
                liRet = 1 ;// 合法用户名
            }
      }
     
      return liRet ;
    }
   
3. 关于读取配置文件的代码:
  已有代码:
   int giVerifyZoneIP( const char *apcZone , const char * apcIP ) {
     int liRet = 0 ;
       char lsTemp[ 128 ] = "";
   
       if ( apcZone[ 0 ] == '\0' || apcIP[ 0 ] == '\0' ){
           liRet = -1 ;       
       }

       if ( liRet == 0 ){
         //读取配置文件的代码
           class clsConfigFile loINI( "../config/waninterface.ini" ) ;
           loINI.csGetSetting( "localzone", apcZone , lsTemp, sizeof( lsTemp ) );
           
           .......//比较IP的代码, 细节不在讨论范围,省略
     }
     
     return liRet ;
   }
   
   很多地方的调用代码,这种代码是每次用户请求就会调用一次,好在后来在前端系统里处理了,这里就注释了:
     /*
           if ( csClientIP[ 0 ] != '\0' && giVerifyZoneIP( csLocalZone , csClientIP ) < 0 ){
           liRet = ERR_MISMATCH_ZONEIP ;
           }else
         */
        
  问题分析:
    大家很容易看出,giVerifyZoneIP()里是随着每次调用都会读取配置文件的配置项,这个就是
  主要的问题:读取配置文件是很慢的,应该在系统启动时一次性读取,而不是每次调用读取,INI文件
  里的配置内容都是不变的。
  
  建议:
    这种问题应该是个低级问题,程序员不应该出现的低级问题,一般人都知道应该在系统启动时或者
  在第一次使用时读取配置文件一次,而不是每次读取这个不变的配置项,所以怎么修改都不用再说。
  
4. 对libxml2组件初始化和关闭接口的调用:
  已有代码:
    clsSAXParser::clsSAXParser( ) {
      xmlInitParser();
    }
    
    clsSAXParser::~clsSAXParser( ) {
      xmlCleanupParser();
            xmlMemoryDump();
    }
    
    使用代码:
    clsSAXParser loXMLParser();
    
  问题分析:
    libxml2是一个XML解析的组件,细看提供的文档就知道,它的初始化和关闭代码只需要调用一次,
  而上面的写法变成每定义一个对象,就调用一次,这个是十分耗时的,在压力测试下显示整个程序都
  有问题了。
  
  已经修改的代码:
    class cls_InitAndDestoryXml{
       public:
          cls_InitAndDestoryXml(){
              xmlInitParser();
          }
          
          ~cls_InitAndDestoryXml(){
              xmlCleanupParser();
              xmlMemoryDump();
          }
    };
    
    static cls_InitAndDestoryXml moInitAndDestroyXml;
  
  提示:
    我们使用第三方开源软件时,尽量看清楚别人的使用说明,随便乱用, 从功能测试上可能没什么问题,
  但性能等方面可能就发生很大问题了.

posted on 2008-03-05 12:53 梁-兄 阅读(1324) 评论(6)  编辑 收藏 引用 所属分类: C/C++项目管理

Feedback

# re: 评审公司去年项目代码发现的一些问题[未登录] 2008-03-05 17:07 ......
lpcLocalZone == NULL 与 strlen( lpcLocalZone ) == 0 是等效的。  回复  更多评论
  

# re: 评审公司去年项目代码发现的一些问题 2008-03-05 18:43 Fox
@......
你的话太吓人了。。。空指针和空串不等效,这是任何一个学过C/C++的人应该具备的常识。。。。  回复  更多评论
  

# re: 评审公司去年项目代码发现的一些问题 2008-03-05 21:34 xingd
lpcLocalZone为null时, strlen(lpcLocalZone)返回0。  回复  更多评论
  

# re: 评审公司去年项目代码发现的一些问题 2008-03-05 21:40 xingd
实际测过了,strlen(NULL)出错
  回复  更多评论
  

# re: 评审公司去年项目代码发现的一些问题 2008-03-05 23:42 long.muyi
一个判断是否是有效指针,一个判断是否是空串!两回事。  回复  更多评论
  

# re: 评审公司去年项目代码发现的一些问题 2008-03-07 13:51 francis
。。。 lpcLocalZone为不为null和strlen(lpcLocalZone)有屁关系啊。。  回复  更多评论