[C Language] Implementation of memmove function

Function introduction

memmoveIt is a function used to copy the memory of one area to another area. It is defined as follows

void * memcpy ( void * destination, const void * source, size_t num );

Return value: returndestination

Parameter introduction:destination, points to the destination to which the data is to be copied

source, where the data is copied from, pointing to the source of the data

num, the number of bytes you want to copy

The following is an example of using memmove, which copies the first 12 bytes in arr1 to arr2 The first 12 bytes in it are printed at the end

int main() {
    
    
	int arr1[] = {
    
     1,5,6,4,8,9 };
	int arr2[20] = {
    
     0 };
	memmove(arr2, arr1, 12);
	for (int i = 0; i < 3; i++) {
    
    
		printf("%d", arr2[i]);
	}
	return 0;
}

memmovesimulation implementation

The first thing is to copy one byte, then we convert the pointer to char* and then execute a loop of num times, Then copy and paste in sequence. At the same time, in order to be able to return ourdest position, we can store it in a pointer variable at the beginning and return it at the end

void* my_memmove(void* dest, const void* src, size_t num) {
    
    
	void* ret = dest;
	for (int i = 0; i < num; i++) {
    
    
		*((char*)dest) = *((char*)src);
		(char*)src = (char*)src + 1;
		(char*)dest = (char*)dest + 1;
	}
	return ret;
}

But when we use the above code, we will find a problem: if the copied and pasted memory has overlapping parts, problems will occur.

So will the above code cause such a problem? Let us take the following code as an example to analyze

void* my_memcpy(void* dest, const void* src, size_t num) {
    
    
	assert(dest && src);
	void* ret = dest;
	for (int i = 0; i < num; i++) {
    
    
		*((char*)dest) = *((char*)src);
		(char*)src = (char*)src + 1;
		(char*)dest = (char*)dest + 1;
	}
	return ret;
}
int main() {
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr+2, arr, 20);
	for (int i = 0; i < 10; i++) {
    
    
		printf("%d ", arr[i]);
	}
	return 0;
}

The ideal output of our above string of code should be1 2 1 2 3 4 5 8 9 10, but the actual output is1 2 1 2 1 2 1 8 9 10

So why? In fact, because the third element has been modified to 1 before we transferred it, the same problem will happen later.

But if we exchange arr+2 and arr in the above code and put the latter one in the front, no error will occur even if the memory overlaps. This is why?

It is precisely because of the copy order problem

Insert image description here

We can find that when the overlapping part is the front of dest and the back of src. Then when we copy from front to back, we will replace the 3 4 that is about to be copied with 1 2, and then we will continue to copy in a loop1 2

But if dest and src are exchanged, then the overlapping part is the back of , then when copying from front to back, the non-overlapping part of will be replaced first, which will not cause the content of our to be in It was changed before it was copieddestsrcdestsrc

So is there any way to complete the copying task even when the overlapping part is in front of dest and behind src? We might as well think about it, can we reverse the copy direction of arr->arr+2 from back to front, so that when executing the copy process Isn't it equivalent to replacing the non-overlapping parts of dest first?

So in fact, we only need to let the function copy from back to front when encountering the situation of dest before src Can complete the task perfectly. So how do we differentiate between these two situations?

In fact, we can distinguish the two situations by comparing the positions pointed to by dest and src. When srcWhen it is on the left side of dest (that is, when src<dest), copy from back to front. . Otherwise, use front-to-back copy

When there is no overlap, it doesn't matter which method is used, then just let it continue to use the copy method when it overlaps.

void* my_memmove(void* dest, const void* src, size_t num) {
    
    
	assert(dest && src);
	void* ret = dest;
	if (dest < src) {
    
    
		for (int i = 0; i < num; i++) {
    
    
			*((char*)dest) = *((char*)src);
			(char*)src = (char*)src + 1;
			(char*)dest = (char*)dest + 1;
		}
	}//这种情况用原来的函数就可以,直接复制粘贴
	else {
    
    
		while (num--) {
    
    
			*((char*)dest + num) = *((char*)src + num);
		}
		//让他+num 然后我们只需num--就能实现从后往前的复制
	}
	return ret;
}

Then there will be no problem of incorrect copy results due to memory overlap.

Guess you like

Origin blog.csdn.net/qq_42150700/article/details/130042738