题记
近期笔试、面试中多次出现字符串处理函数的写法。包括最经典的:strcpy、strcat、strcmp、atoi、itoa,内存拷贝函数memcpy等。
Baidu、google一下会有很多版本,良莠不齐,给大家阅读和辨识形成了负担,本就紧张的笔试、面试备战在选择中浪费了不少时间。
本文涉及的函数的写法不敢说最优,但是是作者参考了诸多文献和深入分析下总结的,并全部通过VC6.0调试通过,希望对大家有帮助。
这里涉及一点,面试的时候,面试官可能就一张白纸,不提供函数原型的情况下让你实现库函数。这里就需要大家对Msdn里提供的库函数有所深入,参数个数、参数类型、返回值类型、函数实现的功能。
一、函数原型
以下函数原型摘自MSDN2001,VS2008、VS2010会有新的扩展比如:strcpy_s——
函数原型 | 函数功能 |
---|---|
char strcpy( char strDestination,const char *strSource); | 字符串拷贝(拷贝一个字符串到另一个字符串) |
int strcmp( const char string1, const char string2 ); | 比较两字符串,string1>string 返回值>0;string1 |
char strcat( char strDestination, const char *strSource ); | 字符串拼接函数 |
int atoi( const char *string ); | 字符串转化为整形数 |
char _itoa( int value, char string, intradix ); | 整形转化为字符串[注意]:radix取值范围是[2,36].string长度最长33个字节.10进制的数考虑负数的处理,其他进制不必考虑。 |
void memcpy( void dest, const void *src, size_tcount ); | 内存拷贝函数,【注意】:本身并没涉及地址覆盖,但一般面试的时候需要考虑。 |
二、自写函数实现
1. strcpy的实现
char* strcpy_t(char* strDst, const char* strSrc)
{
assert(strDst !=NULL && strSrc != NULL);
char* strRst =strDst;
while( (*strDst++= *strSrc++)!='\0');
return strRst;
}
2. strcmp的实现[扩展:<0 返回-1; >0 返回1; 等于0 返回0]
int strCmp_t(const char* strSrcL, const char* strSrcR)
{
assert(strSrcL!= NULL || strSrcR != NULL);
int ret = 0;
constchar* p1 = strSrcL;
constchar* p2 = strSrcR;
while(!(ret = (*p1 - *p2)) && *p2 != '\0') //注意循环终止条件
{
++p1;
++p2;
}
if(ret< 0)
{
ret= -1;
}
else if(ret > 0)
{
ret= 1;
}
return ret;
}
3. strcat的实现
char* strCat_t(char* strDst, const char* strSrc)
{
char* cp = strDst;
while(*cp != '\0')
{
cp++;//跳至 strDst的末尾.
}
while((*cp++= *strSrc++)!='\0');
return strDst;
}
4.atoi的实现【扩展:考虑了正负号的处理、大、小边界数的处理、非字符的处理、status决定了是什么状态导致的返回0】
//atoi_t
enum status{kValid = 0, kInvalid};
int g_Status =kInvalid;
int main()
{
int stringToInt(const char* str);
int stringToIntCore(const char* str,bool bMinus);
char* str1 = "-12345";
cout << stringToInt(str1)<< endl;
cout <<stringToInt("+") << endl;
return 0;
}
//stringToIntCore核心处理函数.
intstringToIntCore(const char* str, bool bMinus)
{
int num = 0;
while(*str!= '\0')
{
if(*str>= '0' && *str <='9')
{
num= num*10 + (*str -'0');
if((!bMinus && num >0x7FFFFFFF) || (bMinus && num < (signedint)0x80000000))
{
num= 0; //
break;
}
}
else
{
num= 0;
break;
}
str++;
}//endwhile
if(bMinus)
{
num= -num;
}
if(*str== '\0')
{
g_Status= kValid;
}
returnnum;
}
intstringToInt(const char* str)
{
boolbMinus = false;
intnum = 0;
if(str== NULL)
{
return0;
}
if(str!= NULL && *str != '\0')
{
if(*str== '+')
{
str++;
}
else if(*str == '-')
{
bMinus= true;
str++;
}
if(*str!= '\0')
{
num = stringToIntCore(str,bMinus);
}
}//endif
return num;
}
5.itoa的实现[考虑,进制转换,字符串翻转]
//itoa
char* itoa_t(intvalue, char* strSrc, int radix)
{
char strTmp[33]; //1.最大存储33个字节
int quotient = 0; //商数
int residue = 0; //余数
int isNegative = 0; //负数标识,1代表负数,0代表正数
int nCnt = 0; //统计字符个数
char* strQ = strTmp; //暂存strTmp.
if(radix < 2 || radix >36)
{
return NULL; //2.radix[2,36]基数的大小范围
}
isNegative = (value < 0&& radix == 10); //3.对于10进制的数,考虑负数的处理.
if(isNegative != 0)
{
value = -value;
}
else
{
value =(unsigned)value;
}
while(value != 0)
{
quotient = value/radix; //商
residue = value%radix; //余数
++nCnt;
if(residue <10)
{
*strQ++ =(residue+'0');
}
else
{
*strQ++ =(residue+'a'-10); //>=10 的特殊处理a-->f
}
value = quotient;
}
if(strSrc == NULL)
{
strSrc =(char*)malloc(nCnt+isNegative+1);
}
char* strP = strSrc; //strP暂存strSrc变化
if(isNegative != 0)
{
*strP++ = '-';
}
while(nCnt >= 0)
{
*strP++ = *--strQ;
--nCnt;
}
*strP = '\0';
return strSrc;
}
6. memcpy的实现——见另一篇博文
http://blog.csdn.net/wojiushiwo987/article/details/8020409
7. 字符串翻转.
char* strRvs(const char* strSrc)
{
intnLen = strlen(strSrc);
char*strRst = new char[nLen+1]; //另外开辟空间.
char*pRst = strRst;
const char* pLast = strSrc + nLen-1;
while(nLen>= 0)
{
*pRst++= *(pLast--);
nLen--;
}
return strRst;
}
8.strncat写法
char *strncat_t (char * front,const char * back,size_t count)
{
char *start = front;
while (*front++)
;
front--;
while (count--)
{
if (!(*front++ = *back++))
{
return(start); //拷贝至结尾需要返回!
}
}
*front = '\0';
return(start);
}
9.memset写法
//memset():把指定内存区域的前count个字节设置成字符c
void * memset_t(void* buffer, int c, size_t count)
{
assert(buffer != NULL);
char *p = (char *)buffer;
cout << p << endl;
while(count--)
{
*p++ = 'c';
cout << *p << endl;
}
return buffer;
}
10.strstr写法
/子串判定!
char *strstr_t(const char *strSrc, const char *str)
{
assert(strSrc != NULL && str != NULL);
const char *s = strSrc;
const char *t = str;
for (; *strSrc != '\0'; ++ strSrc)
{
for (s = strSrc, t = str; *t != '\0' && *s == *t; ++s, ++t)
{
NULL;
}
if (*t == '\0')
{
return (char *) strSrc; //存在子串
}
}
return NULL;
}
【总结】:字符串处理为什么常考的原因,主要它牵涉到指针处理、分支情况的处理,考量大家的思考能力、逻辑问题分析能力、变通能力等。
肯定还有这种常考的函数,希望与大家探讨!