1、如题,编写字符串的拷贝函数,下面就一些答案,分析出完美的解答。
2、strcpy实现1:
void MyStrcpy1(char *strDest, char *strSrc)
{
if
(strDest == NULL || strSrc == NULL)
{
return
;
}
int i = 0;
while
(*(strSrc + i) !=
'\0'
)
{
*(strDest + i) = *(strSrc + i);
i++;
}
}
|
调用1:
int _tmain(int argc, _TCHAR* argv[])
{
char str1[12] = {0};
char str2[] =
"abcdef"
;
MyStrcpy1(str1, str2);
printf(
"%s\n"
, str1);
getchar();
return
0;
}
|
结果1:正确
调用2:目的字符数组不初始化
int _tmain(int argc, _TCHAR* argv[])
{
char str1[12];
char str2[] =
"abcdef"
;
MyStrcpy1(str1, str2);
printf(
"%s\n"
, str1);
getchar();
return
0;
}
|
结果2:错误
调用3:
int _tmain(int argc, _TCHAR* argv[])
{
char str1[1] = {0};
char str2[] =
"abcdef"
;
MyStrcpy1(str1, str2);
printf(
"%s\n"
, str1);
getchar();
return
0;
}
|
结果3:错误
分析:这个函数写的应该比较清晰了,而且也考虑到了传入参数(指针)可能为空的情况,但是有以下几点问题:
(1)源字符串的末尾的'\0'没有拷贝到目标中,如果传入的strDest原来就是字符串,那么OK;但是如果传入的strDest只是一个字符数组的地址,或者说一块内存空间的地址(内存空间又未初始化为NULL),那么就会出问题了。
(2)传入的strSrc参数可能被更改,应加const限定。
(3)为了能够使用链式表达式,应该加上返回值(返回目的指针)。
(4)最好,能用断言,检测参数的合法性。
优化实现1如下:
char* MyStrcpy1(char *strDest, const char *strSrc)
{
if
(strDest == NULL || strSrc == NULL)
{
return
strDest;
}
int i = 0;
char *pCh = strDest;
while
(*(strSrc + i) !=
'\0'
)
{
*(strDest + i) = *(strSrc + i);
i++;
}
*(strDest + i) =
'\0'
;
return
pCh;
}
|
或用for循环:
char* MyStrcpy1(char *strDest, const char *strSrc)
{
if
(strDest == NULL || strSrc == NULL)
{
return
strDest;
}
int i = 0;
char *pCh = strDest;
for
(i = 0; *(strSrc + i) !=
'\0'
; i++)
{
*(strDest + i) = *(strSrc + i);
}
*(strDest + i) =
'\0'
;
return
pCh;
}
|
或直接用下标操作:
char* MyStrcpy1(char *strDest, const char *strSrc)
{
if
(strDest == NULL || strSrc == NULL)
{
return
strDest;
}
int i = 0;
char *pCh = strDest;
for
(i = 0; strSrc[i] !=
'\0'
; i++)
{
strDest[i] = strSrc[i];
}
strDest[i] =
'\0'
;
return
pCh;
}
|
最后,可以在最前面加上断言:
assert( (strDest != NULL) && (strSrc != NULL) );
|
但是,assert只在Debug下有用,在Release下是无效的。
3、更进一步,因为strDest是指针而不是数组名,所以可以直接进行自增、自减运算:
char* MyStrcpy1(char *strDest, const char *strSrc)
{
assert( (strDest != NULL) && (strSrc != NULL) );
if
(strDest == NULL || strSrc == NULL)
{
return
strDest;
}
char *pCh = strDest;
while
(*strSrc !=
'\0'
)
{
*(strDest++) = *(strSrc++);
}
*strDest =
'\0'
;
return
pCh;
}
|
这样,你会发现代码还可以精简:
char* MyStrcpy1(char *strDest, const char *strSrc)
{
assert( (strDest != NULL) && (strSrc != NULL) );
if
(strDest == NULL || strSrc == NULL)
{
return
strDest;
}
char *pCh = strDest;
while
(( *(strDest++) = *(strSrc++) ) !=
'\0'
)
{
NULL;
}
return
pCh;
}
|
4、说明:
(1)vs2010中strcpy的定义如下:
/***
*char *strcpy(dst, src) - copy one string over another
*
*Purpose:
* Copies the string src into the spot specified by
* dest; assumes enough room.
*
*Entry:
* char * dst - string over which "src" is to be copied
* const char * src - string to be copied over "dst"
*
*Exit:
* The address of "dst"
*
*Exceptions:
*******************************************************************************/
char * __cdecl strcpy(char * dst, const char * src)
{
char * cp = dst;
while
( *cp++ = *src++ )
;
/* Copy src over dst */
return
( dst );
}
|
(2)另外,关于函数中的assert和if判断,都是加强代码安全的一种手段,对于功能本身并不是必须的,assert出错表示发生了不该发生的事,这时就需要检查代码。另外,我们一直说在使用指针之前,要判断是否为空,这也是为了避免可能出现的错误。