自定义内存复制函数memcpy()

题目:编写memcpy 函数

代码:

[cpp]  view plain  copy
  1. void* Memcpy(void* dst,const void* src,size_t count)  
  2. {  
  3.     assert(dst != NULL && src != NULL && count > 0);  
  4.     char* pDst = static_cast<char*>(dst);  
  5.     const char* pSrc = static_cast<const char*>(src);  
  6.     while (count--)  
  7.     {  
  8.         *pDst = *pSrc;  
  9.         pDst++;  
  10.         pSrc++;  
  11.     }  
  12.     return dst;  
  13. }  

说明:考察难点:指针的++、内存重叠问题。

注意:

(1)这里不考虑内存重叠的情况,即源地址和目的地址指向的内存可能是有重叠的情况。在标准库中,memcpy()考虑了地址重叠的情况,在下面的程序中讨论了这个问题。

(2)之所以要进行强制类型转换,是因为指向空的指针在执行++时,系统是不知道往前走几个字节的。所以强制转换为char*后,在++时,就会自动往前走一个字节。

(3)(一个知识盲点)这里还有一个问题要注意,由于变量src是const void* 类型,即指向的内容是不可变的。但是当存在内存重叠时,其值会发生改变,不知道这是为什么?

答:const只是在C++语法层面来检查是否对只读内存进行修改,但是在程序中对只读内存隐含的修改是没有办法的

所以,在程序中,由于src指向的内容是只读的,编译器只会检测是否通过src对其指向的内存进行修改。此时在程序中没有通过src对src指向的内存进行修改,所以不会报错。但是由于当内存重叠时,指针dst可能修改src指向那块内存,此时这算是隐含的改变,编译器是没办法解决的。

(4)在VS2008编译器下,调用系统提供的函数memcpy和自己手写函数的比较:

[cpp]  view plain  copy
  1. #include <iostream>  
  2. #include <assert.h>  
  3. using namespace std;  
  4.   
  5. void* Memcpy(void* dst,const void* src,size_t count)  
  6. {  
  7.     assert(dst != NULL && src != NULL && count > 0);  
  8.     char* pDst = static_cast<char*>(dst);  
  9.     const char* pSrc = static_cast<const char*>(src);  
  10.     while (count--)  
  11.     {  
  12.         *pDst = *pSrc;  
  13.         pDst++;  
  14.         pSrc++;  
  15.     }  
  16.     return dst;  
  17. }  
  18.   
  19. int main()  
  20. {  
  21.     char arrOne[10] = {'1','2','3','4','5'};  
  22.     char arrTwo[10] = {'1','2','3','4','5'};  
  23.     char arrTh[10] = {'1','2','3','4','5'};  
  24.     char arrFor[10] = {'1','2','3','4','5'};  
  25.   
  26.     cout<<"--调用系统的函数---"<<endl;  
  27.     cout<<arrOne<<endl; //12345  
  28.     memcpy(arrOne + 1,arrOne,4);  
  29.     cout<<arrOne<<endl; //11234  
  30.     cout<<arrOne + 1<<endl;//1234  
  31.     /*分析:此时应该从目的地址的尾部开始拷贝,一直到首地址结束,从而得到我们想要的结果*/  
  32.   
  33.     cout<<"--调用系统的函数---"<<endl;  
  34.     cout<<arrTwo<<endl; //12345  
  35.     memcpy(arrTwo,arrTwo + 1,4);  
  36.     cout<<arrTwo + 1<<endl;//3455  
  37.     cout<<arrTwo<<endl; //23455  
  38.     /*分析:此时应该从目的地址的首地址开始拷贝,一直到尾部结束,从而得到我们想要的结果*/  
  39.   
  40.   
  41.     cout<<"--调用手写的函数---"<<endl;  
  42.     cout<<arrTh<<endl; //12345  
  43.     Memcpy(arrTh + 1,arrTh,4);  
  44.     cout<<arrTh<<endl;  //11111  
  45.     cout<<arrTh + 1<<endl; //1111  
  46.     /*分析:这个结果和我们想要的结果不同,算是错误结果。*/  
  47.     /*原因:手写的字符串是从目的地址的首地址开始拷贝的,一直到尾部结束。这样的思路会导致结果不正确。*/  
  48.   
  49.     cout<<"--调用手写的函数---"<<endl;  
  50.     cout<<arrFor<<endl; //12345  
  51.     memcpy(arrFor,arrFor + 1,4);  
  52.     cout<<arrFor + 1<<endl;//3455  
  53.     cout<<arrFor<<endl; //23455  
  54.     //分析:这个结果是我们的想要的结果,是正确结果  
  55.     /*原因:要想得到我们想要的结果,就必须是从目的地址的首地址开始拷贝的,一直到尾部结束。这和手写算法的思路是一样的,因此是正确的。*/  
  56.   
  57.     system("pause");  
  58.     return 1;  
  59. }  

分析:

(1)从结果看,在VS2008编译器下,调用系统提供的函数memcpy其实是做了内存重叠检测了。否则在前两个例子不会都正确的。

(2)在单步执行看代码时,memcpy直接优化成汇编代码了。由于看不到源码,只能猜测是做了内存重叠优化了

(3)注意,内存拷贝时,可能会出现源地址内容改变的情况。系统提供的函数memcpy也是不能解决这个问题的。这会是一个隐含的问题,也就是说,执行过这个函数memcpy后,只能保证目的地址执行的内容是正确的,但是源地址执行的内容可能会改变,此时写程序要注意下。

(4)在面试写这个代码时,还是要问清楚是否要考虑内存重叠以及源地址是否能够改变的问题

(5)另外,在 http://www.cplusplus.com/reference/cstring/memcpy/ 看到的内容:
To avoid overflows, the size of the arrays pointed by both the destination and source parameters, shall be at least num bytes, and should not overlap (for overlapping memory blocks, memmove is a safer approach).
是不是说,在使用函数memcpy时,其源地址指向的内存和目的地址指向的内存应该是不重叠的。即使不用做内存重叠检测的。
这貌似和VS下的memcpy函数实现思想是不同的。难道是标准不同?路过的大牛可以围观下。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值