一起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字符串复制函数的例子咱们就说到这里。欲知后面还有什么例子,且听下回分解。
- 一起talk C栗子吧(第六十四回:C语言实例--DIY字符串复制函数)
- 一起talk C栗子吧(第六十五回:C语言实例--DIY字符串连接函数)
- 一起talk C栗子吧(第六十六回:C语言实例--DIY字符串比较函数)
- 一起talk C栗子吧(第六十七回:C语言实例--DIY字符串长度函数)
- 一起talk C栗子吧(第六十九回:C语言实例--DIY字符串查找函数)
- 一起talk C栗子吧(第六十八回:C语言实例--DIY字符串长度函数的小疑问)
- 一起talk C栗子吧(第七十四回:C语言实例--DIY cd命令)
- 一起talk C栗子吧(第六十三回:C语言实例--字符串查找)
- 一起talk C栗子吧(第六十回:C语言实例--字符串复制)
- 一起talk C栗子吧(第七十回:C语言实例--字符串初始化函数)
- 一起talk C栗子吧(第七十三回:C语言实例--DIY pwd命令)
- 一起talk C栗子吧(第七十五回:C语言实例--DIY ls命令)
- 一起talk C栗子吧(第七十六回:C语言实例--DIY cat命令)
- 一起talk C栗子吧(第七十七回:C语言实例--DIY ls命令续)
- 一起talk C栗子吧(第二十四回:C语言实例--顺序查找)
- 一起talk C栗子吧(第四十四回:C语言实例--深度优先遍历一)
- 一起talk C栗子吧(第九十四回:C语言实例--SystemV IPC结构概述)
- 一起talk C栗子吧(第一百二十四回:C语言实例--内置宏)
- opengl学习过程
- linux编程基础_04shell编程(基础表达式)
- 黑马程序员 — JAVA基础 — 内部类、异常
- opencv(C++)视频流读入-学习笔记3
- 第一次博客
- 一起talk C栗子吧(第六十四回:C语言实例--DIY字符串复制函数)
- 写一函数,将两个字符串连接
- linux常用命令
- Neural Networks and Deep Learning
- 记录一个可以上传图片的代码
- 16位汇编:文件名长度导致的LINK:Unresolved Externals错误
- Android AndroidProgressLayout:加载页面遮挡耗时操作任务页面
- JAVA创建XML文件(四)---JDOM方式创建XML
- git建立本地仓库