关于C函数memcpy的实现细节思考

前段时间和朋友讨论关于C基础函数memcpy的实现细节时,收获颇多。这个函数在C / C++编程领域中使用率是比较高的(可能排在前10左右)。但鲜有人去研究其实现原理。为了弄清楚其实现,我给自己出了一道题目,就是用C实现一个memcpy的函数。先看标准memcpy函数的参数和返回值:

void* memcpy(void* dst, void*  src, size_t size);
dst  - 目的内存地址,src - 源内出地址,size- 需要复制的长度。返回值为dst。


初想一下,这个函数很简单,就随手写了一个实现。如下:

void* my_memcpy(void *dst, const void* src, size_t size)
{
	if(dst == NULL || src == NULL || size <= 0)
		return dst;
	char* dst_pos = (char *)dst;
	char* src_pos = (char *)src;

	while(size > 0){
		*dst_pos++ = *src_pos++;
		size --;
	}

	return dst;
}
咋一看,这个函数是正确的。我使用了基本的测试代码如下:

int main(int argc, char* argv[])
{
	unsigned char buf[15] = {0};
	unsigned char dst_buf[15] = {0};

	for(unsigned char i = 3; i < 13; i ++)
		buf[i] = i;

	printf("src buf = ");
		for(int i = 0; i < 15; i ++)
		printf("%d ", buf[i]);

	printf("\r\n");
	//不同BUFF之间拷贝
	my_memcpy(dst_buf, buf, 15);

	printf("from buf copy to dst buf, dst_buf = ");
	for(int i = 0; i < 15; i ++)
		printf("%d ", dst_buf[i]);
	//同一个BUFF向后拷贝
	my_memcpy(buf + 4, buf + 3, 10);

	printf("\nfrom buf + 3 copy to buf + 5, buf =");
	for(int i = 0; i < 15; i ++){
		printf("%d ", buf[i]);
		buf[i] = 0;
	}
	//同一个BUFF向前拷贝
	for(unsigned char i = 3; i < 13; i ++)
		buf[i] = i;
	my_memcpy(buf, buf + 3, 10);
	printf("\nfrom buf + 5 coy to buf, buf = ");
	for(int i = 0; i < 15; i ++)
		printf("%d ", buf[i]);

	return 0;
}


如果用上测试用例来测试,上面写的my_memcpy是只能过不同buf之间的拷贝和同一BUF向前拷贝,同一buf向后拷贝的情况重叠内存是被覆盖了的。于是我进行了修改:

void* my_memcpy(void *dst, const void* src, size_t size)
{
	if(dst == NULL || src == NULL || size <= 0)
		return dst;
	char* dst_pos = (char *)dst + size;
	char* src_pos = (char *)src + size;

	while(size > 0){
		*dst_pos-- = *src_pos--;
		size --;
	}

	return dst;
}

从上可以看出,我做了向前拷贝兼容,防止向后拷贝重叠地址被覆盖的情况。但第三种情况向前拷贝的重叠内存还是被覆盖了。这就要思考根据参数判断是从前拷贝还是从后拷贝。于是修改为:

void* my_memcpy(void *dst, const void* src, size_t size)
{
	if(dst == NULL || src == NULL || size <= 0)
		return dst;

	char* dst_pos = (char *)dst;
	char* src_pos = (char *)src;
	if(dst_pos < src_pos + size && dst > src){ //DOWN COPY,向前拷贝
		dst_pos = dst_pos + size;
		src_pos = src_pos + size;

		while(size > 0){
			*dst_pos-- = *src_pos--;
			size --;
		}
	}
	else { //UP COPY,向后拷贝
		while(size > 0){
			*dst_pos++ = *src_pos++;
			size --;
		}
	}

	return dst;
}
这个实现是兼容向前拷贝和向后拷贝、不同BUF拷贝的情况,也近似C函数库的实现。后来我跟踪了下C函数库的实现,它是用ASM进行实现的。我贴出VC++中的实现:

ifdef MEM_MOVE
        _MEM_     equ <memmove>
else  ; MEM_MOVE
        _MEM_     equ <memcpy>
endif  ; MEM_MOVE

%       public  _MEM_
_MEM_   proc \
        dst:ptr byte, \
        src:ptr byte, \
        count:IWORD

              ; destination pointer
              ; source pointer
              ; number of bytes to copy

;       push    ebp             ;U - save old frame pointer
;       mov     ebp, esp        ;V - set new frame pointer

        push    edi             ;U - save edi
        push    esi             ;V - save esi

        mov     esi,[src]       ;U - esi = source
        mov     ecx,[count]     ;V - ecx = number of bytes to move

        mov     edi,[dst]       ;U - edi = dest

;
; Check for overlapping buffers:
;       If (dst <= src) Or (dst >= src + Count) Then
;               Do normal (Upwards) Copy
;       Else
;               Do Downwards Copy to avoid propagation
;
..........
后面的ASM代码太长,我没有全部给出。它也是做了情况判断,只是整个实现考虑的情况很多,细节考虑的很多。所以, 通过对memcpy函数的分析,我们可以很好的了解内存操作需要注意的地方,尤其是同一buf向前拷贝和向后拷贝的问题,我们不仅仅要会使用API,更需要弄清楚API背后的逻辑












评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值