網站首頁 編程語言 正文
一、memcpy()
函數原型
void * memcpy ( void * dest, const void * src, size_t num );
參數說明
- 函數 memcpy 從 src 位置開始向后復制 num 個字節的數據到 dest 的內存位置。
- void * dest 代表目標的內存地址,const void * src 代表源內存地址。其中二者的數據類型均為 void * 。void * 可以存儲任何類型地址的值。因此,該函數拷貝的內存數據可以是任意類型(如果int、float、double等均可)。
- size_t 即unsigned int類型。注意:num代表的是要拷貝的字節數,而非元素個數!如要從src拷貝一個int類型的數據到dest,則num應傳入4,而非1.
- 該函數的返回值類型也為 void * ,也即只返回目標地址的數值。后續如何按照基類型取出數據、使用數據,可以由調用方在調用函數后實現。
模擬算法
void* my_memcpy(void* dest, const void* src, size_t num) {
void *ret = dest; //保存dest,用于最終輸出
assert(dest);
assert(src);
while (num--) {
*(char*)dest = *(char*)src1; //(char*): 取出每一個字節(8 bit)的值,以單個字節為單位進行賦值
dest = (char*)dest + 1;
src = (char*)src + 1;
} //void * 類型是不能直接運算的,因為沒有步長。(char*)將dest與src轉換為以char為步長,再向后移動
return ret;
}
- 該函數用于實現沒有重疊部分內存的內存數據拷貝。如果source和destination有任何的重疊,復制的結果都是未定義的。
- 拷貝內存值按從低地址到高地址的順序進行,多用于數組。
根據memcpy()的模擬算法,如果src與dest的內存空間有重疊部分,則可能導致src中的內容被覆蓋,無法輸出正確的值。
src中元素3的位置恰好也是dest中首元素的位置。dest的首元素被更改的同時,src中的元素也被更改。因此,memcpy()是不可用于“自己拷貝到自己后面”這樣的操作的。這一問題留給了memmove()來解決。
使用示例
1.簡單
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 20); //拷貝20個字節,即5個int元素
float arr3[] = { 1.0f,2.0f,3.0f,4.0f };
float arr4[5] = { 0.0 };
memcpy(arr3, arr4, 8); //拷貝8個字節,即2個float元素
return 0;
}
2.進階
//示例來自cplusplus官網
/* memcpy example */
#include <stdio.h>
#include <string.h>
struct {
char name[40];
int age;
} person, person_copy;
int main ()
{
char myname[] = "Pierre de Fermat"; //定義一個字符串
/* 用 memcpy 拷貝字符串 */
//每個char類型占一個字節,因此要拷貝的字節數即strlen()+1,加一是因為要把'\0'也拷貝過去。
memcpy ( person.name, myname, strlen(myname)+1 );
person.age = 46;
/* 用 memcpy 拷貝結構體 */
//sizeof操作符,可以直接得到結構體變量在內存中所占的字節數。
memcpy ( &person_copy, &person, sizeof(person) );
//直接完成了結構體之間的數據拷貝:從person拷貝到person_cpy,不用手動轉義,非常方便
printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
return 0;
}
二、memmove()
函數原型
void * memmove ( void * dest, const void * src, size_t num );
參數說明
- 該函數的參數與返回值類型與memcpy()函數相同。該函數同樣用作 從 src 位置開始向后復制 num 個字節數據到 dest 的內存位置。
- memmove()函數與memcpy()函數主要的區別在于memmove()可以進行有內存重疊的數據拷貝,而memcpy()絕對不能。memmove()的功能比memcpy()更加完善。
- 可以理解為:如果memcpy()函數夠到了60分,那么memmove()函數卻能到達90分。
模擬算法
#include<stdio.h>
#include<string.h>
//模擬實現memmove()
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest; //保存結果用于輸出
//從前向后拷貝,也可以寫成if(dest <= src || (char*)dest >= (char*)src + num)
if (dest <= src)
{
while (num--) {
*(char*)dest = *(char*)src; //拷貝
dest = (char*)dest + 1;
src = (char*)src + 1; //指針從前向后移動(從低地址向高地址移動)
}
}
else //從前向后拷貝
{
dest = (char*)dest + num - 1;
src = (char*)src + num - 1; //初始化兩指針至各自范圍的最后
while (num--) {
*(char*)dest = *(char*)src; //拷貝
dest = (char*)dest - 1;
src = (char*)src - 1; //指針從后向前移動
}
}
return ret; //返回值為dest
}
//測試代碼///
int main() {
int arr1[] = { 2,3,4,5,6 };
my_memmove(arr1+2, arr1, 8);
//預計 2 3 2 3 6
for (int i = 0; i < 5; i++) {
printf("%d ", arr1[i]);
}
printf("\n-------------------\n");
int arr2[] = { 2,3,4,5,6 };
memmove(arr2 + 2, arr2, 8);
for (int i = 0; i < 5; i++) {
printf("%d ", arr2[i]);
}
return 0;
}
拷貝順序的結論如圖所示。上面提到,當有內存重疊時,拷貝的順序是有講究的。若不遵守下圖的結論, 仍將導致src中原來的值被覆蓋,無法輸出正確的結果。
***錯誤的模擬算法
如下代碼是錯誤的:
//錯誤的代碼
void* my_memmove(void* dest, const void* src, size_t num)
{
void * ret = dest;
while (num--) {
//從前向后拷貝
if (dest <= src)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
else
{
dest = (char*)dest + num - 1; //錯誤
src = (char*)src + num - 1; //錯誤
*(char*)dest = *(char*)src;
dest = (char*)dest - 1;
src = (char*)src - 1;
}
return ret;
}
有一些同學可能認為,while(num--)語句與if語句的順序可以調換。正確的代碼是先進行情況判斷,再進入while(num--)進行賦值與移動。將二者順序調換,乍一看沒有什么問題,是先設定一共要移動num次,再進入判斷進行具體的操作。但是這樣書寫是有問題的,因為在dest > src && dest < src+num時,需要從后向前拷貝,這意味著dest和src的起始位置要發生變化。
上述代碼中標記“錯誤”的語句便是dest和src初始化的語句。如果直接將while和if的位置調換,則每一次進入循環,都要初始化一遍dest和src。如此,dest與src的功能就被打亂了。
使用示例
//示例來自cplusplus官網
/* memmove example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "memmove can be very useful......";
memmove (str+20,str+15,11); //表示將包括str+15向后11個字節的內容移動到str+20位置
puts (str);
return 0;
}
輸出:
如下圖,將 src = str+11 位置開始,包括該位置共向后拷貝11字節。每個char占一個字節,因此拷貝了"very useful"這7個char字母至dest = str+20的位置。
三、memset()
函數原型
void * memset ( void * ptr, int value, size_t num );
參數說明
- 第一個參數 ptr 為指針類型,表示要進行操作的內存的地址。如要對數組arr進行內存內容設置,則該參數的值為arr。
- 第二個參數 value 為要設定的內存的值。該值的數據類型是int型,但char值也是可以的。
- 第三個參數 num 為要設置值的內存的字節數。注意:是字節數,而不是元素的個數。如要改變兩個int類型的值,num應為 8 ,而不是2.
使用說明
例如,有數組arr:[ 1 2 3 4 5 ]
要將其前兩個元素值設定為0,則可使用memset函數:memset(arr, 0 , 8)
輸出:[ 0 0 3 4 5 ].原理如下(以小端存儲的形式展現):
arr數組為int類型,一個int為4字節、32bit,內存中的存儲如下(二進制):
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
memset(arr, 0, 8),將前8個字節(2個int)置為0(其實每個比特位都被置為0了,但由于其它的比特位已經為0,故沒有標出來)
但若使用 memset(arr, 1, 8),并不是把數組前兩個元素置為 1 .因為memset()函數是針對內存中每個字節的,memset(arr, 1, 8)的實際作用是將前8個字節中的內容全部置為1.
00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01
00 00 00 01 00 00 00 01 00 00 00 01 00 00 00 01
03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
此時,前4字節按照int類型解析出來,結果為1000000010000000100000001,即16843009。
總結:
- int類型數組除了置0外,用memset置換成任何數都是錯的。
- memset只適用于每個元素只占1個字節的數組,比如char型數組。因為memset的操作單位就是每個字節。只有char類型的數組不會出現錯誤。
使用示例
//示例來自cplusplus官網
/* memset example */
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "almost every programmer should know memset!";
memset (str,'-',6); //表示將從str開始,包括str向后6個字節的內存內容置為'-'
puts (str);
return 0;
}
//輸出:------ every programmer should know memset!
四、memcmp()
函數原型
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
參數說明
- 比較ptr1和ptr2指針開始的num個字節。
- ptr1和ptr2分別是兩個代表要比較的內存空間(一般是數組)的指針。
- num是要比較的字節數。(注意:不是元素個數)。
使用說明
返回值為整型,若返回值>0,則ptr1的內存長度大于ptr2;若返回值==0,則二者相等;若返回值<0,則ptr1的內存長度小于ptr2
使用示例
//示例來自cplusplus官網
/* memcmp example */
#include <stdio.h>
#include <string.h>
int main ()
{
//創建兩個要用作比較的數組
char buffer1[] = "DWgaOtP12df0";
char buffer2[] = "DWGAOTP12DF0";
//接受比較的結果
int n;
//要比較的字節數為buffer1的長度
//兩字符串的比較可以用strcmp(buffer1,buffer2)函數實現,原理大致相同。
n = memcmp ( buffer1, buffer2, sizeof(buffer1) );
if (n>0) printf ("'%s' is greater than '%s'.\n",buffer1,buffer2);
else if (n<0) printf ("'%s' is less than '%s'.\n",buffer1,buffer2);
else printf ("'%s' is the same as '%s'.\n",buffer1,buffer2);
return 0;
}
原文鏈接:https://blog.csdn.net/wyd_333/article/details/126826890
相關推薦
- 2022-07-01 python神經網絡Densenet模型復現詳解_python
- 2023-02-01 特定用例下的Combine全面使用詳解_Swift
- 2023-05-21 使用react完成點擊返回頂部操作_React
- 2022-09-16 Go?WEB框架使用攔截器驗證用戶登錄狀態實現_Golang
- 2022-08-05 lambda表達式集合list根據某個屬性去重
- 2022-09-09 Go語言提升開發效率的語法糖技巧分享_Golang
- 2022-08-15 springboot切換為redis緩存
- 2023-12-10 啟動微服務,提示驅動程序無法通過使用安全套接字層(SSL)加密與 SQL Server 建立安全連接
- 最近更新
-
- 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同步修改后的遠程分支