zoukankan      html  css  js  c++  java
  • strcpy,memcpy,memmove和内存重叠分析

    一:strcpy函数用法和实现:

      /* 
    
     GNU-C中的实现(节选): 
    
     */
    
     char* strcpy(char *d, const char *s) 
    
     { 
    
       char *r=d; 
    
       while((*d++=*s++)); 
    
       return r; 
    
     } 


    有没有发现这个实现并不是很好。。至少没有判断是不是为NULL。一个更好的版本:

    	char *strcpy(char *strDest, const char *strSrc)
    	{
    		assert((strDest!=NULL) && (strSrc !=NULL));
    		char *address = strDest; 
    		while( (*strDest++ = * strSrc++) != '') 
    			 
    		return address ; 
    	}


    好好研究这段代码其实写的挺妙的。。(当然不是我写的)。。

    二:memcpy函数用法和实现:

    从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

    微软实现:

     void * __cdecl memcpy (void * dst,const void * src, size_t count ) 
     { 
    	 void * ret = dst; 
    #if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)         
    	 {         
    		 extern void RtlMoveMemory( void *, const void *, size_t count );
    		 RtlMoveMemory( dst, src, count );
    	 } 
    #else  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */         
    	 /*          * copy from lower addresses to higher addresses          */        
    	 while (count--) { 
    		 *(char *)dst = *(char *)src;
    		 dst = (char *)dst + 1; 
    		 src = (char *)src + 1;
    	 } 
    #endif  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */        
    	 return(ret);
     } 
    


    写的是不是也很好。。仿佛天衣无缝啊!。。但是你有没有注意到一个问题:那就是内存重叠。。

    什么叫内存重叠呢?就是两块内存区域有一个共同的部分。。是不是很SB的解释。。当存在这个问题的时候,那还和我们预期的那样拷贝吗?注意:理解了这个才是关键。。

    例子:(引用网上一个例子)

    int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
     memcpy(a+4, a, sizeof(int)*6) 

    如果照微软实现的话,那么输出应该是0123012301(理解这个再忘下看吧。。)

    而我们想要达到的目的是输出:0123012345。。如果你实验一下,确实也是输出0123012345。。(对于这个问题,我也解释不了)。为了避免这样的情况发生,我们找到了一个更好的函数:memmove。。

    三:memmove函数用法和实现

    用法和memcpy函数一样。。

    实现:

     void *memmove(void *dest, const void *source, size_t count)
     {
    	 assert((NULL != dest) && (NULL != source));
    	 char *tmp_source, *tmp_dest;
    	 tmp_source = (char *)source;
    	 tmp_dest = (char *)dest;
    	 if((dest + count<source) || (source + count) <dest))
    	 {// 如果没有重叠区域
    		 while(count--)
    			 *tmp_dest++ = *tmp_source++;
    	 }
    	 else
    	 { //如果有重叠(反向拷贝)
    		 tmp_source += count - 1;
    		 tmp_dest += count - 1;
    		 while(count--)
    			 *--tmp_dest = *--tmp;
    	 }
    	 return dest;
     }


    一开始看到这段代码的时候,觉得好神奇啊。后来发现,其实有个很大的漏洞(先不说那些小问题),那就是内存重叠有两种情况。其实我觉得该作者也想到了,而且给出了解决办法。,估计是粗心大意吧。
    :src<dest&&src+count>dest;  dest<src&&dest+count>src。。但是网上那位作者没有写清楚,下面我就重写一下上面的else部分:

     else
    	 { 
    		 //重叠1:(反向拷贝)
    		 if(tmp_source<=tmp_dest&&tmp_source+count>=tmp_dest)
    		 {
    			 tmp_source += count - 1;
    			 tmp_dest += count - 1;
    			 while(count--)
    				 *tmp_dest-- = *tmp_source--;
    		 }
    		  //重叠2:(正向拷贝)
    		 else
    		 {
    			 while (count--)
    			 {
    				 *tmp_dest++=*tmp_source++;
    			 }
    		 }
    	 }


    但写完之后发现傻逼了。。后面的那个else不可以和前面的那个if写在一起么?所以优化后的完整代码:

     void *memmove(void *dest, const void *source, size_t count)
     {
    	 assert((NULL != dest) && (NULL != source));
    	 char *tmp_source, *tmp_dest;
    	 tmp_source = (char *)source;
    	 tmp_dest = (char *)dest;
    
    	 if(tmp_source<=tmp_dest&&tmp_source+count>=tmp_dest)
    	 {
    		 tmp_source += count - 1;
    		 tmp_dest += count - 1;
    		 while(count--)
    			 *tmp_dest-- = *tmp_source--;
    	 }
    	 else
    	 {
    		 while (count--)
    		 {
    			 *tmp_dest++=*tmp_source++;
    		 }
    	 }
    	
    	 return tmp_dest;
     }

    此时就加上了内存重叠的判断。如果你理解了上面的那个(输出应该是0123012301),一切都明白了。。还有:别忘了那个问题哟,运行的时候还是输出:0123012345。。和我们的理论值有冲突。。希望大神发现问题能告知,一起学习,交流。。

    此时才发现,平时写的那些代码,我们能懂他们多少呢?

  • 相关阅读:
    Java中替换字符串中特定字符,replaceAll,replace,replaceFirst的区别
    牛客剑指offer 67题(持续更新~)
    从尾到头打印链表
    字符串变形
    缩写
    删除公共字符
    替换空格
    二维数组的查找
    acm博弈论基础总结
    acm模板总结
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3262718.html
Copyright © 2011-2022 走看看