各位看官们,大家好,上一回中咱们说的是字符串查找的例子,这一回咱们说的例子是:DIY字符串复制函数。闲话休提,言归正转。让我们一起talk C栗子吧!
我们在前面的章回中介绍过字符串复制函数,时间不长,但是有些看官已经忘记了,为了加深看官们对字符串复制函数的印象,我们准备DIY字符串复制函数。just do it by yourself!
我们在前面的章回中一共介绍了四个字符串复制函数:strcpy,strncpy,memcpy,memmove。接下来我们分别介绍如何DIY它们。
char *diy_strcpy(char *s1,const char *s2)
下面是我写的代码,其实核心代码只有一行:
char *diy_strcpy(char *s1,const char *s2)
{
char *pStr = NULL;
if(NULL == s1 || NULL == s2)
return NULL;
pStr = s1;
while((*pStr++ = *s2++) != '\0') // 核心代码
;
return s1;
}
其实,在Linux系统中也提供了标准C库,文件路径/lib/string.c。下面我们看看该文件中是如何实现字符串复制函数:strcpy的。
/** * strcpy - Copy a %NUL terminated string * @dest: Where to copy the string to * @src: Where to copy the string from */
char *strcpy(char *dest, const char *src)
{
char *tmp = dest;
while ((*dest++ = *src++) != '\0')
/* nothing */;
return tmp;
}
对比一下,我们DIY的strcpy函数,两者在核心代码上是一致的。
char *diy_strncpy(char *s1,const char *s2,int n)
下面是我写的代码,大家可以参考:
char *diy_strncpy(char *s1,const char *s2,int n)
{
char *pStr = NULL;
if(NULL == s1 || NULL == s2)
return NULL;
pStr = s1;
while(*s2!='\0' && n-->0) // \0 is not copyed to s1
*pStr++ = *s2++;
while(n-- > 0) // if the size of s2 is less then n, copy \0 to s1.
*pStr++ = '\0';
return s1;
}
大家想想 ,我们在给字符赋值过程中,能不能使用像strcpy中的语句进行赋值操作:
while ((*dest++ = *src++) != '\0')
;
其实是不行的,这样会把小尾巴也复制到s1中,下面是标准库中strncpy的实现,请大家和咱们DIY的strncpy进行对比:
/**
* strncpy - Copy a length-limited, C-string
* @dest: Where to copy the string to
* @src: Where to copy the string from
* @count: The maximum number of bytes to copy
*
* The result is not %NUL-terminated if the source exceeds
* @count bytes.
*
* In the case where the length of @src is less than that of
* count, the remainder of @dest will be padded with %NUL.
*
*/
char *strncpy(char *dest, const char *src, size_t count)
{
char *tmp = dest;
while (count) {
if ((*tmp = *src) != 0) src++; tmp++; count--; } return dest; }
大家对比一下就会发现,我们DIY的strncpy函数和标准库中的strncpy函数不一样,但是功能是相同的。最大
的区别在于标准库在不足n个字符的时候使用空字符进行补充。标准库使用:
if ((*tmp = *src) != 0)
src++;
这个判断巧妙地进行补充空字符的操作,而我们DIY的时候,是专门进行补充操作。
void *diy_memcpy(void *s1,const void *s2,int n)
与strncpy相比,memcpy完全按照n进行复制操作,它不管s2中字符数量与n的关系,既不会像strncpy那样补充小尾巴,也不会像strcpy那样复制字符串的小尾巴。通过源代码,可以体现出memcpy和strcpy以及strncpy函数的区别,下面是我写的代码,大家可以参考:
void *diy_memcpy(void *s1,const void *s2,int n)
{
char *pStr = NULL;
const char *pCStr = NULL;
if(NULL == s1 || NULL == s2)
return NULL;
pStr = (char*)s1; // change the void * to char *
pCStr = (char*)s2;
while(n-- > 0) // \0 is not copyed to s1
*pStr++ = *pCStr++;
return s1;
}
下面是标准库中memcpy的实现,请大家和咱们DIY的memcpy函数进行对比:
/** * memcpy - Copy one area of memory to another * @dest: Where to copy to * @src: Where to copy from * @count: The size of the area. * * You should not use this function to access IO space, use memcpy_toio() * or memcpy_fromio() instead. */
void *memcpy(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
大家可以发现,我们DIY的函数和标准库的memcpy函数是一样的。
void *diy_memmove(void *s1,const void *s2,int n)
与memcpy相比,memmove增加了对内存空间重叠的判断,这点我们在介绍memmove函数时强调过。其它的操作,它就和memcpy完全相同。memmove和memcpy函数一样,完全按照n进行复制操作,它不管s2中字符数量与n的关系,既不会像strncpy那样补充小尾巴,也不会你strcpy那样复制字符串的小尾巴。通过源代码,可以体现出memmove和strcpy以及strncpy函数的区别,下面是我写的代码,大家可以参考:
void *diy_memmove(void *s1,const void *s2,int n)
{
char *pStr = NULL;
const char *pCStr = NULL;
if(NULL == s1 || NULL == s2)
return NULL;
pStr = (char*)s1; // change the void * to char *
pCStr = (char*)s2;
if((pStr > pCStr) && (pStr < pCStr+n)) // if s1 and s2 have some same memory
{
pStr += n;
pCStr +=n;
while(n-->0)
*--pStr = *--pCStr;// \0 is not copyed to s1
}
else
{
while(n-->0) // \0 is not copyed to s1
*pStr++ = *pCStr++;
}
return s1;
}
下面是标准库中memmove的实现,请大家和咱们DIY的memmove函数进行对比:
/** * memmove - Copy one area of memory to another * @dest: Where to copy to * @src: Where to copy from * @count: The size of the area. * * Unlike memcpy(), memmove() copes with overlapping areas. */
void *memmove(void *dest, const void *src, size_t count)
{
char *tmp;
const char *s;
if (dest <= src) {
tmp = dest;
s = src;
while (count--)
*tmp++ = *s++;
} else {
tmp = dest;
tmp += count;
s = src;
s += count;
while (count--)
*--tmp = *--s;
}
return dest;
}
通过对比,我们可以发现标准库中的函数在判断内存空间重叠时比我们DIY的函数要简洁一些。除此之外, 它和我们DIY的函数是相同的。
看官们,我把这四个DIY函数整理成了一个文件,并且添加了详细的注释,除此之外,我还使用了前面章回中的测试case进行测试。正文中就不写代码了,详细的代码放到了我的资源中,大家可以点击这里下载使用。前面章回中的程序可以点击这里下载。
下面是程序运行的结果,大家可以和前面章回中的程序结果进行对比:
-----------------------------------
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-1
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strcpy(s1,s3) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : AB
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strcpy(s1,s2) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2and123
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : 123
addr: 0xbf980b77 | s4 : ABstring
-----------------------------------
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-1
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strncpy(s1,s3,SIZE) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : AB
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strncpy(s1,s2,SIZE) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2andAB
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strncpy(s1,s2,SIZE-1) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2an
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strncpy(s1,s2,SIZE+3) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2and123
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : 123
addr: 0xbf980b77 | s4 : ABstring
-----------------------------------
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-1
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running memcpy(s1,s3,SIZE) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : AB
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running memcpy(s1,s2,SIZE) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2andAB
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running memcpy(s1,s2,SIZE-1) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2an
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running memcpy(s1,s2,SIZE+3) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-2and123
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : 123
addr: 0xbf980b77 | s4 : ABstring
-----------------------------------
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : str-1
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running memmove(s1,s3,SIZE) -----
addr: 0xbf980b79 | s0 : string
addr: 0xbf980b81 | s1 : AB
addr: 0xbf980b91 | s2 : str-2and123
addr: 0xbf980b89 | s3 : AB
addr: 0xbf980b77 | s4 : ABstring
----- after running strcpy(s2,s2+1) -----
addr: 0xbf980b91 | s2 : tr-2and123
----- after running strncpy(s2,s2+1,SIZE) -----
addr: 0xbf980b91 | s2 : tr-2and1123
----- after running memcpy(s2,s2+1,SIZE) -----
addr: 0xbf980b91 | s2 : tr-2and1123
----- after running memmove(s2,s2+1,SIZE) -----
addr: 0xbf980b91 | s2 : tr-2and1123
各位看官,关于DIY字符串复制函数的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解。