一起talk C栗子吧(第六十四回:C语言实例--DIY字符串复制函数)

来源:互联网 发布:锥度螺纹编程 编辑:程序博客网 时间:2024/04/29 21:29

各位看官们,大家好,上一回中咱们说的是字符串查找的例子,这一回咱们说的例子是:DIY字符串复制函数。闲话休提,言归正转。让我们一起talk C栗子吧!

我们在前面的章回中介绍过字符串复制函数,时间不长,但是有些看官已经忘记了,为了加深看官们对字符串复制函数的印象,我们准备DIY字符串复制函数。just do it by yourself!

我们在前面的章回中一共介绍了四个字符串复制函数:strcpy,strncpy,memcpy,memmove。接下来我们分别介绍如何DIY它们。

DIY strcpy函数:

char *diy_strcpy(char *s1,const char *s2)

  • 1.在s2所指的字符串中,从第一个字符开始,把s2中的字符,赋值给s1指向的字符;
  • 2.判断是不是s2的小尾巴,如果是小尾巴,那么停止复制操作;如果不是,进入下一步;
  • 3.重复步骤1和2,直到从步骤2中停止为止。

下面是我写的代码,其实核心代码只有一行:

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函数,两者在核心代码上是一致的。

DIY strncpy函数:

char *diy_strncpy(char *s1,const char *s2,int n)

  • 1.在s2所指的字符串中,从第一个字符开始,把s2中的字符,赋值给s1指向的字符;
  • 2.判断下面两个条件,只有满足了其中的任何一个条件 ,那么停止复制操作;如果两个条件都不满足,那么进入第三步:
    • 条件一:赋值的字符是不是s2的小尾巴;
    • 条件二:赋值的次数是不是超过了n次;
  • 3.重复步骤1和2,直到从步骤2中停止为止;
  • 4.判断步骤2中复制字符的数量,如果数量小于n,那么把空字符赋值给s1,赋值的次数为剩下的数量。

下面是我写的代码,大家可以参考:

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的时候,是专门进行补充操作。

DIY memcpy函数:

void *diy_memcpy(void *s1,const void *s2,int n)

  • 1.在s2所指的字符串中,从第一个字符开始,把s2中的字符,赋值给s1指向的字符;
  • 2.判断赋值的次数是不是超过了n次。如果是,那么停止复制操作;否则进入第三步:
  • 3.重复步骤1和2,直到从步骤2中停止为止;

与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函数是一样的。

DIY memmove函数:

void *diy_memmove(void *s1,const void *s2,int n)

  • 1.在s2所指的字符串中,从第一个字符开始,把s2中的字符,赋值给s1指向的字符;
  • 2.判断s1和s2的在内存空间中的位置,如果s1在s2后面或者说s1的内存地址比s2的大,并且s1和s2指 向的内存空间有重叠,那么从s1和s2指向的字符串n个字符后面开始复制操作,复制时从后向前进行复制操作;否则从前向后进行复制操作;当然了,这里的复制操作和memcpy的复制操作完全一致。

与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字符串复制函数的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解。


0 0