C語言全部內(nèi)存操作函數(shù)的實現(xiàn)詳細講解
memcpy內(nèi)存拷貝函數(shù)
void* memcpy(void* destination, const void* source, size_t num);
- memcpy函數(shù)從source的位置開始向后拷貝num個字節(jié)的數(shù)據(jù)到destination的內(nèi)存位置
- 這個函數(shù)在遇到
\0
的時候并不會停下來 - 如果source和destination有任何的重疊,復(fù)制的結(jié)果都是未定義的
使用方法:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> int main() { char arr1[20] = { 0 }; char arr2[] = "hello world!"; int arr3[10] = { 0 }; int arr4[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int i = 0; memcpy(arr1, arr2, 12); memcpy(arr3, arr4, 16); printf("%s\n", arr1); for (i = 0; i < 10; i++) { printf("%d ", arr3[i]); } return 0; }
輸出結(jié)果:
如果源頭和目的地是同一塊內(nèi)存它進行拷貝的時候會出現(xiàn)覆蓋的情況。
如:
#include <stdio.h> #include <string.h> int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int i = 0; memcpy(arr + 2, arr, 16); for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
可以看到它并沒有如我們預(yù)期的輸出來輸出結(jié)果,我們預(yù)期的結(jié)果應(yīng)該是:1 2 1 2 3 4 7 8 9 10
可是memcpy拷貝的時候會覆蓋,而C語言對memcpy的標準是只要能實現(xiàn)拷貝即可,不考慮同一塊內(nèi)存拷貝會覆蓋的情況,這種情況是由另一個函數(shù)來處理。
當(dāng)然有些編譯器對memcpy函數(shù)的實現(xiàn)是有優(yōu)化過的,目前我個人知道的編譯器是VS它是對memcpy有優(yōu)化的,如果拷貝的是同一塊內(nèi)存它不會覆蓋,而是如預(yù)期的那樣進行拷貝。
memcpy函數(shù)的實現(xiàn)
#include <assert.h> void* my_memcpy(void* dest, const void* src, unsigned int count) { assert(dest && src);//斷言 void* temp = dest;//temp保存dest的起始地址 while (count--) { *(char*)dest = *(char*)src;//復(fù)制src的內(nèi)容到dest ++(char*)dest;//下一個字節(jié)的拷貝 ++(char*)src; } return temp;//返回dest起始地址 }
void* my_memcpy(void* dest, const void* src, unsigned int count);
參數(shù)一:void* dest
- dest設(shè)置成空類型,因為空類型可以接收任何大小的數(shù)據(jù),但是有一個缺陷它不能自增或者自減,也不能直接解引用因給它空類型是一個沒有具體類型,它不知道它能訪問多少個字節(jié),所以使用空類型的時候我們需要強制類型轉(zhuǎn)換。
- dest是緩沖區(qū)
參數(shù)二:void* src
- 它的類型和dest一樣不過,它和參數(shù)一不同的是它被const保護起來了,因為它只是被復(fù)制也就是說我們只是訪問它里面的內(nèi)容并不需要修改它,所以我們就加一個const把它保護起來,防止我們不小心對它進行修改
參數(shù)三:unsigned int counst
- counst是我們要修改多少字節(jié)的參數(shù),修改是以字節(jié)為單位的,它的類型是unsigned int (無符號整整形)也就是說不能出現(xiàn)負數(shù)
返回類型:void*
- 返回dest的首地址
assert(dest && src)
這個是用來保證代碼的健壯性,assert()
函數(shù)是斷言,如果傳過來的是空指針,那么就是假因為NULL的值是0,只有兩邊都為真才不會有提示。
*(char*)dest = *(char*)src
因為是void*
類型所以我們要強制轉(zhuǎn)換才能解引用進行拷貝操作,而我們要操作的是一個字節(jié)所以轉(zhuǎn)為字符型指針最合適。
++(char*)dest;
和上面的同理,要強制類型轉(zhuǎn)換才能進行++和–操作。
memmvoe函數(shù)
void* memmove(void* destination, const void* source, size_t num);
- 和memcpy的差別就是memmove函數(shù)處理的源內(nèi)存塊和目標內(nèi)存塊是可以重疊的。
- 如果源空間和目標空間出現(xiàn)重疊,就得使用memmove函數(shù)處理
使用方法:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <assert.h> int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int i = 0; memmove(arr + 2, arr, 16); for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
memmove
和memcpy
的使用方法一樣,沒什么大區(qū)別。
memmove函數(shù)的實現(xiàn)
#include <assert.h> void* my_memmove(void* dest, const void* src, unsigned int count) { assert(dest && src);//斷言 void* temp = dest; if (dest < src)//小于src從前向后 { while (count--) { *(char*)dest = *(char*)src; ++(char*)dest; ++(char*)src; } } else//大于從后向前 { while (count--) { *((char*)dest + count) = *((char*)src + count); } } return temp; }
為了處理同塊內(nèi)存的拷貝,這里我們分為了兩種方法。
- 從前向后拷貝
- 從后向后拷貝
memcpy用的是從前向后拷貝,所以會出現(xiàn)被覆蓋的情況。
那么問題來了,我們什么情況才從前向后拷貝和從后向前拷貝呢?
我們可以以src為分界線,如果dest小于src我們就從前向后,這樣就避免了src的內(nèi)容被覆蓋之后被拷貝到dest里面去,如果dest大于src,我們就從后向前。
那有人問如果等于呢?等于的話你從前向后還是從后向前不都一樣?
所以按照這個思路我們寫成兩個拷貝順序,從后向前我們不用思考了,想在我們只需要思考從后向前拷貝。
while (count--) { *((char*)dest + count) = *((char*)src + count); }
從后向前我們只需要先得到dest和src末尾的地址就能進行從后向前操作了,count + dest
不就得到了末尾了嗎?counst + dest
得到末尾的\0
的地址,但是我們不需要修改\0
所以count + dest
之前我們對count
自減。
后面就不需要dest自減的操作了,因為count
每次減一我們就得到前面一個的地址,當(dāng)count
減完了,我們也拷貝完了。
memcmp內(nèi)存塊比較函數(shù)
int memcmp(const void* ptr1, const void* ptr2, size_t num);
- 比較從ptr1和ptr2指針開始的num個字節(jié)
- 返回值,當(dāng)ptr1大于ptr2就返回大于1的值,當(dāng)ptr1小于ptr2就返回小于0的值,當(dāng)?shù)扔诘臅r候返回0
使用案列:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> #include <assert.h> int main() { char arr1[] = "abcz"; char arr2[] = "abcd"; if ( 0 < memcmp(arr1, arr2, 4)) { printf("大于\n"); } else if(0 > memcmp(arr1, arr2, 4)) { printf("小于\n"); } else { printf("等于\n"); } return 0; }
memcpy函數(shù)的實現(xiàn)
#include <assert.h> int my_memcmp(void* dest, const void* src, unsigned int count) { assert(dest && src);//斷言 if (!count) { return 0; } while (--count && *(char*)dest == *(char*)src) { ++(char*)dest; ++(char*)src; } return *(char*)dest - *(char*)src; }
if (!count) { return 0; }
如果count
是0的話就直接返回0
while (count-- && *(char*)dest == *(char*)src) { ++(char*)dest; ++(char*)src; }
當(dāng)count
個數(shù)比較完或者dest不等于src,我們就停止循環(huán)。
return *(char*)dest - *(char*)src;
直接返回dest - src,如果它們兩相等一定返回0,dest小于src返回的是小于0的值,大于則返回大于0的值。
memset修改內(nèi)存塊
void *memset( void *dest, int c, size_t count )
- dest是目的
- 地第二個修改成什么?
- 第三個修改內(nèi)存的個數(shù)
memset是以1字節(jié)為單位來修改,第二個參數(shù)是要修改成什么字符,第三個參數(shù)是修改內(nèi)存?zhèn)€數(shù)以1字節(jié)為單位
使用案列:
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include <string.h> int main() { char arr[10] = { 0 }; int i = 0; memset(arr, '6', 10); for (i = 0; i < 10; i++) { printf("arr[%d]=%c\n", i, arr[i]); } return 0; }
memset函數(shù)實現(xiàn)
#include <assert.h> void* my_memset(void* dest, int a, unsigned int count) { assert(dest);//斷言 void* temp = dest;//記錄dest的首地址 while (count--) { *(char*)dest = a; ++(char*)dest; } return temp;//返回dest的首地址 }
while (count--) { *(char*)dest = a; ++(char*)dest; }
把a的值給dest,來進行修改,每次修改一個字節(jié)就自增一修改下個字節(jié)。
到此這篇關(guān)于C語言全部內(nèi)存操作函數(shù)的實現(xiàn)詳細講解的文章就介紹到這了,更多相關(guān)C語言內(nèi)存操作函數(shù)的實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言超詳細講解結(jié)構(gòu)體與聯(lián)合體的使用
結(jié)構(gòu)體和聯(lián)合體用于描述事物的屬性,如一只鳥的信息,可能包括它的品種,體重,顏色,年齡等,接下來大家一起來詳細看看吧2022-05-05C++中的三種繼承public,protected,private詳細解析
我們已經(jīng)知道,在基類以private方式被繼承時,其public和protected成員在子類中變?yōu)閜rivate成員。然而某些情況下,需要在子類中將一個或多個繼承的成員恢復(fù)其在基類中的訪問權(quán)限2013-09-09C++實現(xiàn)LeetCode(48.旋轉(zhuǎn)圖像)
這篇文章主要介紹了C++實現(xiàn)LeetCode(48.旋轉(zhuǎn)圖像),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07