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;
提示:
我们使用第三方开源软件时,尽量看清楚别人的使用说明,随便乱用, 从功能测试上可能没什么问题,
但性能等方面可能就发生很大问题了.