網站首頁 編程語言 正文
1.memcpy
與字符串函數 strcpy 類似,也是進行拷貝。但是需要注意的是,strcpy 是針對字符串進行拷貝,而 memcpy 是針對內存進行拷貝。
如何理解呢?strcpy 進行拷貝的時候,只能一個字節一個字節的拷貝,但要實現 整型、浮點型等數據類型拷貝的時候,就不得不用到 memcpy 了。
我們觀察 strcpy 的函數聲明:
char * strcpy ( char * destination, const char * source );
再觀察 memcpy 的函數聲明:
void * memcpy ( void * destination, const void * source, size_t num );
可以看到 strcpy 的局限在于只能接收 字符型 的指針,但 memcpy 對于類型可以在函數內部實現自定義。
我們瀏覽cplusplus對參數作出的解釋:
現在我們要研究如何使用:
#include <stdio.h>
#include <string.h>
int main()
{
int dest[10] = { 0 };
int src[10] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(dest, src, 20);//拷貝 20 個字節
return 0;
}
我們從 src 數組中,拷貝 20 個字節的數據放到 dest 數組中。那么我們通過 調試—窗口—監視來觀察 dest 數組中的變化:
未經 memcpy 前:
經 memcpy 后:
可以直觀地看到, dest 數組中前 20 個字節的內容發生了改變。
使用起來倒是不復雜,那我們能不能用自己的代碼去模擬實現一個 memcpy 函數?
#include <stdio.h>
void* AnalogMemcpy(void* dest, const void* src, unsigned int num)
{
void* start = dest;//定義一個記錄 dest 初始地址的指針
while (num--)//一個字節一個字節拷貝
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return start;//返回此指針
}
int main()
{
int dest[10] = { 0 };
int src[10] = { 1,2,3,4,5,6,7,8,9,10 };
AnalogMemcpy(dest, src, 20);
return 0;
}
我們的實現思路非常簡單,因為官方給出第三個參數的定義是:要復制的字節數。那我們順水推舟一個字節一個字節的拷貝。
但是到這里,可能會存在這樣一個問題: char 類型是一個字節,我們使用 memcpy 時也是一個字節一個字節的拷貝,那為什么 strcpy 不能拷貝整型?
在我們自己模擬實現 strcpy 的時候,拷貝的停止條件是什么?是 src 字符串碰到 '\0' 。默認我們的設備是小端存儲模式,那么我們使用 strcpy 進行拷貝的時候就會出現這種情況:
現在我們使用模擬實現的 memcpy 函數來執行這段代碼:
#include <stdio.h>
void* AnalogMemcpy(void* dest, const void* src, unsigned int num)
{
void* start = dest;//定義一個記錄 dest 初始地址的指針
while (num--)//一個字節一個字節拷貝
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return start;//返回此指針
}
int main()
{
int dest[10] = { 1,2,3,4,5,6,7,8,9,10 };
AnalogMemcpy(dest + 3, dest, 20);
return 0;
}
這段代碼的意思是這樣的:
但事實上真正的結果是:
這該如何解釋呢?其實不難:
那么我們暫且定下一個結論:
memcpy 只能處理空間不重疊的數據拷貝。
2.memmove
這個函數與 memcpy 的功能相同,都是進行數據拷貝。但是不同的點是:memmove 是用來處理空間重疊的數據拷貝的。
這是 memmove 的函數聲明:
void * memmove ( void * destination, const void * source, size_t num );
既然是處理空間重疊的情況,那我們直接對上一個代碼進行處理:
可以看到, memmove 符合我們的預期實現了效果。
我們現在來挑戰一下如何模擬實現一個 memmove 函數:
#include <stdio.h>
void* AnalogMemmove(void* dest, const void* src, unsigned int num)
{
void* start = dest;//定義一個記錄 dest 初始地址的指針
if (src < dest)//如果 src 在 dst 的左邊
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
else
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
return start;//返回這個指針
}
int main()
{
int dest[10] = { 1,2,3,4,5,6,7,8,9,10 };
AnalogMemmove(dest + 3, dest, 20);
return 0;
}
乍一看,這個代碼比較復雜,事實上,只有兩種情況。
我們來分析一下:
但是!這里要注意了,上面使使用自己模擬的 memcpy 不能實現空間重疊拷貝的,但庫函數原裝的 memcpy 能夠實現空間重疊拷貝嗎?
我們來看庫函數 memcpy 能否實現空間重疊拷貝:
#include <stdio.h>
#include <string.h>
int main()
{
int dest[10] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(dest + 3, dest, 20);
return 0;
}
這就奇了怪了!既然庫函數 memcpy 能夠實現空間重疊的拷貝,那還需要 memmove 做什么?
事實上,對于我使用的 Visual Studio 2022 這款編譯器來說,memcpy 是可以實現空間重復拷貝的。也就是說可能在其他的編譯器上實現不了空間重疊拷貝,但 Visual Studio 2022 這款編譯器賦予了 memcpy 這項功能。
3.memcmp
與 strcmp 類似的,這個函數也是進行比較的函數,我們觀察一下它的函數聲明:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
那么對于這些參數以及返回值的解釋是:
因為我們學習過 strcmp ,那我們現在直接使用它:
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 1,2,3,3,4,5,6,7,8,9 };
int ret=memcmp(arr1, arr2, 20);
if (ret > 0)
printf("arr1 > arr2\n");
else if (ret < 0)
printf("arr1 < arr2\n");
else
printf("arr1 == arr2\n");
return 0;
}
與 strcmp 的原理是一致的,這里就不贅述了。
4.memset
直譯過來就是內存設置。事實上也是這么回事。
觀察它的函數聲明:
void * memset ( void * ptr, int value, size_t num );
它的作用是以及參數的意義:
說白了就是:你需要提供一個指針,這個指針指向的要被填充的內存。然后提供一個值,這個值決定了每個字節要被填充為什么內容。最后提供一個數,這個數指的是你要填充多少個字節。
我們寫一個例子:
#include <stdio.h>
#include <string.h>
int main()
{
int arr[10] = { 0 };
memset(arr, 1, 4);
return 0;
}
這就奇了怪了,我們不是設置四個字節為 1 嗎?千萬不要么想。我們上面強調過了,這個函數是對每個字節填充。
我們分析一下我們寫的例子:
原文鏈接:https://blog.csdn.net/weixin_59913110/article/details/125334830
相關推薦
- 2022-08-20 Python為什么要保留顯式的self_python
- 2022-10-29 C#?CLR學習?C++使用namespace實例詳解_C 語言
- 2022-07-27 Python中的sys模塊、random模塊和math模塊_python
- 2022-09-27 Golang?Mutex互斥鎖深入理解_Golang
- 2022-10-03 正則表達式中^和$的含義與實例代碼_正則表達式
- 2022-10-11 MybatisPlus的UpdateWrapper和QueryWrapper的區別
- 2022-09-17 Makefile構建Golang項目示例詳解_Golang
- 2023-04-01 Android自定義View?Material?Design理念詳解_Android
- 最近更新
-
- 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同步修改后的遠程分支