網站首頁 編程語言 正文
一、memcpy函數的介紹
1.函數的聲明
void * memcpy ( void * destination, const void * source, size_t num );
2.函數功能與注意事項
- 函數memcpy從source的位置開始向后復制num個字節的數據到destination的內存位置。
- 注意這個函數在遇到 '\0' 的時候并不會停下來。
- 如果source和destination有任何的重疊,復制的結果都是未定義的。
- memcpy函數可以拷貝任何的類型的數據,不像strcpy函數只能拷貝字符串。
3.函數的使用
#include#include //使用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#include //模擬實現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#include //使用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#include //模擬實現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; }
? 運行結果
原文鏈接:https://blog.csdn.net/qq_64042727/article/details/123714965
相關推薦
- 2022-07-29 python保存字典數據到csv文件的完整代碼_python
- 2022-05-20 MybatisCodeHelpPro生成持久層代碼
- 2023-06-17 python?__init__與?__new__的區別_python
- 2022-05-25 Flutter實現倒計時功能_Android
- 2022-06-21 Android幀式布局實現自動切換顏色_Android
- 2022-12-23 Kubernetes應用服務質量管理詳解_云其它
- 2022-07-10 linux 目錄和文件管理
- 2023-06-17 C#中Stopwatch的使用及說明_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支