内存拷贝的陷阱
内存拷贝经常需要使用memcpy和strcpy:
;
二者主要的区别是strcpy将内存看作字符串,遇到0('')就结束。二者在使用时都存在一个不易发觉的陷阱。看下面这两行代码:
第一行代码尝试将内存往“后”移动,这里会出错很好理解,移动前面字节的时候可能会将后面的字节覆盖掉,只有当memcpy的实现是从最后一个字节开始拷贝才可能正确。第二行代码尝试将内存往“前”移动,初看会觉得没有问题,因为实现memcpy的常规想法就是会从第一个字节开始,一个个拷贝,这样第二行代码就是对的。然而根据我的测试,这个行为是未定义的。在ubuntu 12.04 的32位版本,第二行代码测试没有出现问题;在ubuntu 12.04的64位版本,却出现了错误。
在memcpy的文档里提到:
因此dest和src出现覆盖的情况,应该使用memmove函数:
glibc的实现都是汇编,基本看不懂。它根据num分成了3种情况,能被4整除、能被2整除、奇数。
[1] http://www.cplusplus.com/reference/clibrary/cstring/memcpy/
[2] http://www.cplusplus.com/reference/clibrary/cstring/strcpy/
[3] http://www.cplusplus.com/reference/clibrary/cstring/memmove/
/*both defined in string.h*/
void * memcpy ( void * destination, const void * source, size_t num );
char * strcpy ( char * destination, const char * source );
;
二者主要的区别是strcpy将内存看作字符串,遇到0('')就结束。二者在使用时都存在一个不易发觉的陷阱。看下面这两行代码:
memcpy(buf+off, buf, len);
memcpy(buf, buf+off, len);
第一行代码尝试将内存往“后”移动,这里会出错很好理解,移动前面字节的时候可能会将后面的字节覆盖掉,只有当memcpy的实现是从最后一个字节开始拷贝才可能正确。第二行代码尝试将内存往“前”移动,初看会觉得没有问题,因为实现memcpy的常规想法就是会从第一个字节开始,一个个拷贝,这样第二行代码就是对的。然而根据我的测试,这个行为是未定义的。在ubuntu 12.04 的32位版本,第二行代码测试没有出现问题;在ubuntu 12.04的64位版本,却出现了错误。
在memcpy的文档里提到:
…, and (the dest and src ) should not overlap (for overlapping memory blocks, memmove is a safer approach).
因此dest和src出现覆盖的情况,应该使用memmove函数:
/*defined in string.h*/
void * memmove ( void * destination, const void * source, size_t num );
glibc的实现都是汇编,基本看不懂。它根据num分成了3种情况,能被4整除、能被2整除、奇数。
参考文献
[1] http://www.cplusplus.com/reference/clibrary/cstring/memcpy/
[2] http://www.cplusplus.com/reference/clibrary/cstring/strcpy/
[3] http://www.cplusplus.com/reference/clibrary/cstring/memmove/
浙公网安备 33010602011771号