函数实现-memcpy-strcpy-memmove-memset

1. 函数原型(c++ reference)

    void * memcpy ( void * destination, const void * source, size_t num );  不能解决某些覆盖问题。
    char * strcpy ( char * destination, const char * source ); 不能解决某些覆盖问题,根据'\0'表示字符串复制终止。
    void * memmove ( void * destination, const void * source, size_t num );  能够解决所有覆盖问题。
    void * memset ( void * ptr, int value, size_t num );  其中value使用int传入,但是被解释为unsigned char。 

 2. 重叠问题

    ·dest == src 不需要拷贝
    ·dest<src,无论有没有重叠,正向拷贝即可。
    ·dest>src,且dest>=src+len,没有重叠,正向拷贝即可。
    ·dest>src,且dest<src+len,有重叠,需要逆向拷贝。
     对于memcpy和strcpy,都是正向拷贝,不能处理第四种情况。
     对于memmove,考虑到所有的情况即可。
     对于memset,相对简单,就是赋值就可以了。

3. 代码

void* memcpy(void* dest,const void* src, size_t num) { // const
  assert((dest != NULL) && (src != NULL) && (dest!=src)); // assert
  char* p_dest = (char*)dest; // copy
  const char* p_src = (const char*)src;
  
while(num-- > 0)
    
*p_dest++ = *p_src++;
  
return dest; // address
}
char* strcpy(char* dest, const char* src) {
  assert((dest
!=NULL) && (src!=NULL) && (dest!=src));
  
char* address = dest;
  
while((*dest++ = *src++!= '\0')
    NULL;
  
return address; 
}
void* memmove(void* dest, const void* src,size_t num) {
  assert((dest
!=NULL) && (src!=NULL) && (dest!=src));  
  
char* p_dest = (char*)dest;
  
const char* p_src = (const char*)src;
  
if(p_dest>p_src && p_dest<p_src+num) { //p_dest>p_src,逆向拷贝
    p_dest += num - 1;
    p_src 
+= num - 1;
    
while(num--)
      
*p_dest-- = *p_src--;     
  }
  
else { //p_dest<=p_src,此时无论是否重叠都可以使用正向拷贝
    while(num--)
      
*p_dest++ = *p_src++;
  }
  
return dest;
}
void* memset(void* dest, int value, size_t num) {
  assert(dest 
!= NULL);
  unsigned 
char* p_dest = (unsigned char*)dest;
  
while(num-- > 0
    
*p_dest++ = (unsigned char)value;
  
return dest;
}

 4. 其他

    这几个函数常被用来测试编程人员的编程风格。实际上,函数库中的这些函数实现都是汇编搞定的,要更多的考虑机器指令方面的问题,来使用汇编,效率会更高,这就先到这里了。

5. 关于size_t

    size_t是标准C库中定义的,应为unsigned int。 实际上,对于本文的一系列函数,msdn的函数原型都是用size_t,而linux内核往往使用int。
    原因如下吧:
  “数据类型"socklen_t"和int应该具有相同的长度。否则就会破坏 BSD套接字层的填充.POSIX开始的时候用的是size_t, Linus Torvalds(他希望有更多的人,但显然不是很多) 努力向他们解释使用size_t是完全错误的,因为在64位结构中 size_t和int的长度是不一样的,而这个参数(也就是accept函数的第三参数)的长度必须和int一致,因为这是BSD套接字接口标准.最终POSIX的那帮家伙找到了解决的办法,那就是创造了一个新的类型"socklen_t".Linux Torvalds说这是由于他们发现了自己的错误但又不好意思向大家伙儿承认,所以另外创造了一个新的数据类型 。
    在C++中,size_t的引入增强了程序在不同平台上的可移植性,经测试发现,在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的,这样利用该类型可以增强程序的可移植性。”

6. 参考资料

    C++ Reference    http://www.cplusplus.com/reference/clibrary/cstring/
    strcpy()、memcpy()、memmove()、memset()的实现    http://www.cnitblog.com/guopingleee/archive/2009/02/15/54581.html
    size_t 百度百科    http://baike.baidu.com/view/3236587.htm

posted @ 2011-06-16 15:50  xiaodongrush  阅读(775)  评论(3编辑  收藏  举报