個人主頁:C++忠實粉絲
歡迎 點贊👍 收藏? 留言? 加關注💓本文由 C++忠實粉絲?原創C語言內存函數超詳細講解
收錄于專欄【C語言學習】
本專欄旨在分享學習C語言學習的一點學習筆記,歡迎大家在評論區交流討論💌
目錄
1. memcpy使?和模擬實現
2. memmove使?和模擬實現
3. memset函數的使?
4. memcmp函數的使??
1. memcpy使?和模擬實現
void * memcpy ( void * destination, const void * source, size_t num );
?參考:memcpy - C++ Reference (cplusplus.com)
? 函數memcpy從source的位置開始向后復制num個字節的數據到destination指向的內存位置。
? 這個函數在遇到 '\0' 的時候并不會停下來。
? 如果source和destination有任何的重疊,復制的結果都是未定義的。
對于重疊的內存,交給memmove來處理。?
示例:
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };memcpy(arr2, arr1, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;}
?輸出結果:
注意:
函數memcpy從source的位置開始向后復制num個字節的數據到destination指向的內存位置。單位是字節,所以20個字節就是5個整形
memcpy函數的模擬實現:
void* my_memcpy(void* dest, void* src, size_t num)
{void* ret = dest;int i = 0;assert(dest && src);while (num--){*(char*)dest = *(char*)src;((char*)src)++;((char*)dest)++;}return ret;
}
?因為memcpy拷貝的數據類型是不一樣的,這里我們使用void*去接收,使用char*去強轉類型完成一個一個字節的拷貝
這是一個簡單的
my_memcpy
函數,它的功能是將源內存塊src
的內容復制到目標內存塊dest
。這個函數接受三個參數:目標內存塊的指針dest
,源內存塊的指針src
,以及要復制的字節數num
。函數的主要步驟如下:
函數首先保存目標內存塊的初始地址
dest
到ret
,以便在復制完成后返回。使用
assert
函數檢查dest
和src
是否為NULL
。如果任一指針為NULL
,則assert
會終止程序。然后,函數進入一個循環,該循環將持續
num
次。在每次迭代中,它都會做以下操作:
使用
*(char*)dest = *(char*)src;
將src
指向的當前字節復制到dest
指向的當前字節。使用
((char*)src)++;
和((char*)dest)++;
將src
和dest
的地址分別增加 1,以便在下一次迭代中復制下一個字節。最后,函數返回
ret
,即目標內存塊的初始地址。這個函數的實現假設
src
和dest
指向的內存區域不會重疊。如果它們重疊,那么這個函數可能會導致未定義的行為。在這種情況下,應該使用memmove
函數,而不是memcpy
。此外,這個函數也沒有處理可能的內存對齊問題。在某些硬件和操作系統上,如果src
和dest
的地址不是某個特定值(如 4 或 8)的倍數,那么直接復制它們可能會導致性能下降或者錯誤。在這種情況下,應該使用更復雜的算法來處理對齊問題。但是,對于簡單的用途,這個my_memcpy
函數應該是足夠的。
測試代碼:
int main()
{//strcpy - 字符串的拷貝int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };// 0 1 2 3 4int arr2[20] = { 0 };//0 1 2 3 4//memcpy - 針對內存塊進行拷貝my_memcpy(arr2, arr1, 20);int i = 0;for (i = 0; i < 20; i++){printf("%d ", arr2[i]);}return 0;
}
?這里我們注意一下,strcpy是對字符串的拷貝,而我們的memcpy是對內存塊的拷貝?
測試輸出:
?
2. memmove使?和模擬實現
void * memmove ( void * destination, const void * source, size_t num );
?參考:memmove - C++ 參考 (cplusplus.com)
?和memcpy的差別就是memmove函數處理的源內存塊和?標內存塊是可以重疊的。
? 如果源空間和?標空間出現重疊,就得使?memmove函數處理。
?示例:
#include <stdio.h>
#include <string.h>
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1 + 2, arr1, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
輸出結果:
?
memmove的模擬實現:
//memmove函數拷貝完成后,會返回目標空間的起始地址void* my_memmove(void* dest, const void* src, size_t num)
{assert(dest && src);void* ret = dest;if (dest < src){//前->后while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}}else{//后->前while (num--){*((char*)dest+num) = *((char*)src + num);}}return ret;
}
注意:這里分為前后兩種情況
分析:
這段代碼是?
my_memmove
?函數的實現,它用于在內存中移動數據塊,并且可以正確處理源和目標內存區域重疊的情況。函數接收三個參數:目標內存地址?dest
,源內存地址?src
,以及要移動的字節數?num
。函數的工作原理如下:
- 參數檢查:使用?
assert
?確保?dest
?和?src
?都不是空指針。- 保存返回地址:將?
dest
?的初始地址保存到?ret
,以便函數結束時返回。- 前向拷貝:如果?
dest
?地址小于?src
?地址,說明沒有重疊或者?dest
?在?src
?的前面,可以從前向后拷貝。
- 在循環中,逐字節拷貝?
src
?到?dest
,然后將兩者的地址都向后移動一位。- 后向拷貝:如果?
dest
?地址大于或等于?src
?地址,說明?dest
?在?src
?的后面,可能會有重疊,需要從后向前拷貝。
- 在循環中,從最后一個字節開始拷貝,直到拷貝完所有字節。
這樣,即使?
src
?和?dest
?有重疊,數據也不會被錯誤地覆蓋。函數最后返回?ret
,即目標內存塊的起始地址。這個函數是?memcpy
?的一個安全替代品,特別是在處理可能重疊的內存區域時。
舉例:
例如,假設我們有一個數組?
123456789
,并且我們想要將從?3
?開始的部分復制到從?1
?開始的位置。如果我們從前向后拷貝,那么在拷貝?3
?到?1
?的位置后,原來?3
?的位置就變成了?1
,這樣當我們想要拷貝?5
?到?3
?的位置時,就會出現問題,因為此時?3
?的位置已經被改變了。為了解決這個問題,
memmove
?會檢查?dest
?和?src
?的關系。如果?dest
?在?src
?的后面,那么?memmove
?就會選擇從后向前拷貝。這樣,即使?dest
?和?src
?有重疊,也不會出現數據被提前覆蓋的問題。在上述例子中,memmove
?會先拷貝最后一個元素?9
,然后是?8
,7
,依此類推,直到拷貝到?3
。這樣,即使?dest
?和?src
?有重疊,也能保證數據的正確拷貝。所以,
memmove
?函數在處理可能有重疊的內存區域時,比?memcpy
?函數更安全。但是,如果確定?dest
?和?src
?不會重疊,那么?memcpy
?的效率通常會更高。因為?memcpy
?不需要檢查?dest
?和?src
?的關系,也不需要決定是從前向后拷貝還是從后向前拷貝,所以它的實現可以更簡單,運行速度也可以更快。但是,如果不能確定?dest
?和?src
?是否重疊,那么最好使用?memmove
。這就是為什么標準庫提供了兩個不同的函數來處理內存拷貝的原因。
測試數據:
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr, arr+2, 5 * sizeof(int));int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}
?輸出數據:
3. memset函數的使?
void * memset ( void * ptr, int value, size_t num );
?參考:?memset - C++ Reference (cplusplus.com)
memset是?來設置內存的,將內存中的值以字節為單位設置成想要的內容。
示例:
#include <stdio.h>
#include <string.h>
int main()
{char str[] = "hello world";memset(str, 'x', 6);printf(str);return 0;
}
?調試分析:
4. memcmp函數的使??
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
? ?較從ptr1和ptr2指針指向的位置開始,向后的num個字節
? 返回值如下:
示例:
#include <stdio.h>
#include <string.h>int main()
{char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";int n;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);elseprintf("'%s' is the same as '%s'.\n", buffer1, buffer2);return 0;
}
?輸出結果: