首頁 > 軟體

C語言 模擬實現memcpy與memmove函數詳解

2022-04-08 19:00:10

一、memcpy函數的介紹

1.函數的宣告

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

2.函數功能與注意事項

  • 函數memcpy從source的位置開始向後複製num個位元組的資料到destination的記憶體位置。
  • 注意這個函數在遇到 '' 的時候並不會停下來。
  • 如果source和destination有任何的重疊,複製的結果都是未定義的。
  • memcpy函數可以拷貝任何的型別的資料,不像strcpy函數只能拷貝字串。

3.函數的使用

#include <stdio.h>
#include <string.h>//使用memcpy函數時記得參照它的標頭檔案
int main()
{
	int arr1[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int arr2[5] = { 0 };//總共大小為20位元組
	memcpy(arr1, arr2, 20//拷貝20個位元組的資料);//將arr2中的資料拷貝到arr1中
	int i = 0;
	printf("拷貝後arr1中的資料為:");
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

執行結果:

二、模擬實現memcpy函數

1.模擬分析

1.因為我們不知道我們要拷貝的是什麼型別的資料,可能是char型別的資料,也可能是int型別的資料,還有可能是double型別的資料,這些不同型別資料的大小是不同的。為了實現一個能拷貝所有型別資料的memcpy函數,我們就只能一個位元組一個位元組的拷貝,因為最小型別的大小是一個位元組,這樣就能將所有型別的資料都進行拷貝了。

2.因為我們不知道傳到memcpy函數的地址是什麼型別,所以我們在接收傳過來的地址時要用void*型別的指標來接收。

3.由於我們只需要將源地址儲存的資料拷貝到目標地址裡面,所以只需要改變目標地址處儲存的內容,而不需要改變源地址處儲存的地址。所以我們就需要用const void*型別的指標來接收源地址。

4.為了實現鏈式存取,我們要將傳進來的目標起始地址(destination)返回。由於這個函數在執行的時候會改變destination儲存的內容,所以我們要重新建立一個void*型別的指標來儲存這個地址。

5.為了避免傳進來的地址是空指標,我們需要用assert來斷言傳進來的地址不是空指標。

2.模擬實現

#include<stdio.h>
#include<assert.h>
//模擬實現memcpy
void* my_memcpy(void* dest, const void* scr, size_t count)
{
	assert(dest && scr);//斷言傳進來的地址不是空指標
	void* ret = dest;//儲存目標起始地址
	while (count--)//拷貝源地址儲存的資料
	{
		*(char*)dest = *(char*)scr;
		(char*)dest = (char*)dest + 1;
		(char*)scr = (char*)scr + 1;
	}
	return ret;//返回目標起始地址
}
 
 
 
 
//應用模擬實現的函數
int main()
{
	int arr1[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 };
	int arr2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memcpy(arr2, arr1, 24);//拷貝6個位元組的資料
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

執行結果:

三、memmove函數的介紹

1.函數的宣告

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

2.為什麼會有memmove函數

為什麼會有memmove這個函數呢,這個還要從上面的memcpy函數說起。因為memcpy函數不能將一個陣列的中的資料拷貝到自身(也就是目標資料是自己,源資料也是自己,只不過是一個陣列裡面不同的位置的資料拷貝到另外一個位置上),如果像這樣拷貝就會出現重疊拷貝,會導致結果不是我們預期的結果。

就像下面這個程式碼:

//應用模擬實現的memcpy函數
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memcpy(arr + 2, arr, 24);//預期出現結果為1 2 1 2 3 4 5 6 9 10
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);//實際出現結果
	}
	return 0;
}

執行結果:

 出現預期結果和實際結果不同的原因:

出現這種結果的原因就是因為memcpy函數將自身資料拷貝到自身不同位置的時候出現了重疊拷貝。源資料的起始地址為arr,目標資料的起始地址arr + 2,當我們一進來memcpy這個函數的時候,我們就先將arr處的資料拷貝到arr + 2處,將arr + 1處的資料拷貝到arr + 3處,當我們想要將arr + 2處的資料拷貝到arr + 4處的時候,我們發現arr + 2處的資料已經被替換成了arr處的資料(1),於是我們就只能將1拷貝到arr + 4處;當我們要將arr + 3處的資料拷貝到arr + 5處的時候,我們發現arr + 3處的資料早已被替換成了arr + 1處的資料(2),所以我們只能將2拷貝到arr + 5處,就像這樣反覆的重疊拷貝,拷貝的資料一直都是1/2/1/2/1/2,直到拷貝完我們想要拷貝的位元組數。

於是為了將自身的資料拷貝到自身不同的位置處,我們就需要用memmove函數來實現,memmove函數就是為了解決上面這種問題而被創造的。

3.函數功能與注意事項

  • memmove和memcpy的差別就是memmove函數處理的源記憶體塊和目標記憶體塊是可以重疊的。
  • 如果源空間和目標空間出現重疊,就得使用memmove函數處理。

4.函數的使用

#include<stdio.h>
#include<string.h>//使用memmove函數時記得參照它的標頭檔案
int main()                  
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	memmove(arr + 2, arr, 24);//預期出現結果為1 2 1 2 3 4 5 6 9 10
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);//實際出現結果
	}
	return 0;
}

 這次我們發現用memmove函數來拷貝的預期結果和實際結果就一樣了,下面我們就講講memmove函數的模擬實現。

四、模擬實現memmove函數

1.模擬分析

1.將地址傳進函數和函數接收地址的方法和上面的memcpy函數是一樣的,memcpy函數需要注意的地方memmove函數同樣需要注意,這裡就不重複講了,嘿嘿。

2.memmove函數還需要注意的一點就是需要分析該怎麼拷貝才不會重疊,下面為圖解:

情況一:dest小於等於src的地址

像下面這樣從前往後拷貝,這樣就不會重疊了。

 情況二:dest大於scr的地址

像下面這樣從後往前拷貝,這樣就不會重疊了。

2.模擬實現

#include<stdio.h>
#include<assert.h>
//模擬實現memmove
void* my_memmove(void* dest, const void* scr, size_t count)
{
	assert(dest && scr);//斷言傳進來的地址不是空指標
	void* ret = dest; //儲存目標起始地址
	if (dest <= scr)//從前往後拷貝
	{
		while (count--)
		{
			*(char*)dest = *(char*)scr;
			(char*)dest = (char*)dest + 1;
			(char*)scr = (char*)scr + 1;
		}
	}
	else//從後往前拷貝
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)scr + count);
		}
	}
	return ret;
}
 
 
 
 
 
//應用模擬實現的函數
int main()                  
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	my_memmove(arr + 2, arr, 24);//預期出現結果為1 2 1 2 3 4 5 6 9 10
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);//實際出現結果
	}
	return 0;
}

  執行結果

到此這篇關於C語言 模擬實現memcpy與memmove函數詳解的文章就介紹到這了,更多相關C語言 memcpy函數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com