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


各位看官们,大家好,上一回中咱们说的是字符串查找的例子,这一回咱们说的例子是: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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

talk_8

真诚赞赏,手有余香

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值