目錄
?????????一,memcpy內存函數的介紹
二memmove函數的介紹
三,memset的函數使用
四,memcmp的介紹
五,內存函數的模擬實現,以及一個字符串函數strstr的模擬實現
????????5.1memcpy函數的實現
????????5.2memmove的模擬實現
????????5.3memcmp的模擬實現
????????5.4 strstrg函數的模擬實現(暴力實現以及KMP算法實現)
內存操作函數
memcpy的使用以及模擬實現
memmove的使用以及模擬實現
memcmp的使用以及模擬實現
memset的使用
strstr的模擬實現
一,memcpy內存函數的介紹
void* memcpy(void* destination, const void* source, size_t num);
這里我們可以看到,他是有三個參數的,其中有倆個無符號指針,還有一個是無符號的num
這倆個指針,一個指向目標要改變的數據的地址,另一個是源地址,要拷貝到我們目標中
當然這里使用無符號類型的指針,是為了我們能拷貝任何類型的數據,就不僅限于拷貝字符
解釋如下
函數 memcpy 從 source 的位置開始向后復制 num 個字節的數據到 destination 的內存位置。這個函數在遇到 '\0' 的時候并不會停下來,因為我們給了需要拷貝的字節大小,所以拷貝停止是由我們自己來控制的如果 source 和 destination 有任何的重疊,復制的結果都是未定義的。這個意思是所不能在自己的后面拷貝自己,因為可能會導致越界的風險,當然在VS下是可以自己拷貝自己的內容的,但是在不同的編譯器中,可能效果不同,有些編譯器是不允許這樣使用的
這便是在自己后面進行拷貝,然后出現越界的情況,例如在字符串中,他在拷貝的過程中,可以會將'\0'修改,導致字符串沒有結尾標志。
可以看到,我們將arr2中的數據拷貝到了arr1中去,而arr1和arr2中存放的都是int類型的變量,這就是和字符串拷貝的區別,當然我們也可以拷貝其他類型的變量,例如結構體
二memmove函數的介紹
void* memmove(void* destination, const void* source, size_t num);
這里我們可以看到,他是有三個參數的,其中有倆個無符號指針,還有一個是無符號的num
這倆個指針,一個指向目標要改變的數據的地址,另一個是源地址,要拷貝到我們目標中
當然這里使用無符號類型的指針,是為了我們能拷貝任何類型的數據,就不僅限于拷貝字符
和 memcpy 的差別就是 memmove 函數處理的源內存塊和目標內存塊是可以重疊的。如果源空間和目標空間出現重疊,就得使用 memmove 函數處理。


三,memset的函數使用
void *memset( void *dest, int c, size_t count );
memset函數,有三個參數,一個是無符號的指針,一個是你要設置的符號是什么,最后一個參數是無符號的整形,返回類型是無符號的指針類型,指向的是 *dest的首元素地址
memset函數的功能是,在dest里面設置count字節的符號C,并返回dest的地址
我們使用memset就能使我們想要的字節個數的內容變成我們需要的值。
四,memcmp的介紹
int memcmp(const void* ptr1, const void* ptr2, size_t num);
我們可以看到,他有三個參數,前倆個是我們要比較內容的地址,最后一個是我們要比較的個數,返回類型是int類型,如果ptr1 > ptr2 則返回 > 0 的數,如果ptr1 < ptr2 則返回 0? <?的數,如果 ptr1 == ptr2 則返回 0

五,內存函數的模擬實現,以及一個字符串函數strstr的模擬實現
5.1memcpy函數的實現
void* my_memcpy(void* dest, const void* cur, size_t num)
{//保留dest的地址void* ret = dest;while (num){*(char*)dest = *(char*)cur;dest = (char*)dest + 1;cur = (char*)cur + 1;num--;}//返回dest的地址return ret;
}
int main()
{char str1[] = "hello world .............";char str2[] = ".............";//將str2的前5個字節的內容拷貝到str1中my_memcpy(str1,str2,5sizeof(str2[0]));printf(str1);return 0;
}
5.2memmove的模擬實現
圖,一
圖,二
當然在拷貝自己時會出現上述倆種情況,如果我們一直只選一直拷貝方式,例如,從前向后拷貝因為cur和dest都是來自同一部分,那么會出現什么情況呢,我們在dest?> cur時,在拷貝的時候就已經將存儲在a ,b ,c 之后的數據d,e,f修改為 a,b,c 因為dest和cur指向的是同一塊內容,那么cur里面的數據 d,e,f也被修改了,那么就達不到我們需要的拷貝效果。這時我們就要進行一定的判斷,當dest > cur時,我們從后向前拷貝,當 cur > dest時我們從前向后拷貝。當然,如果不是相同地址塊的內容,如何拷貝不會影響他們的結果,這個判斷只對拷貝同一塊地址內容進行區分。
代碼如下
void* my_memmove(void* dest, const void* cur, size_t num)
{void* ret = dest;//為了防止自己拷貝自己導致的內容覆蓋//如果dest < curif (dest < cur){while (num){*(char*)dest = *(char*)cur;dest = (char*)dest + 1;cur = (char*)cur + 1;num--;}}else{dest = (char*)dest + num;cur = (char*)cur + num;while (num){*(char*)dest = *(char*)cur;dest = (char*)dest - 1;cur = (char*)cur - 1;num--;}}
}
int main()
{char str1[] = "hello world .............";char str2[] = ".............";my_memmove(str1,str1+5,6);printf(str1);return 0;
}

5.3memcmp的模擬實現
int my_memcmp(void* pstr1, char* pstr2, size_t num)
{while (num){//如果某個字節的值不相等,則中斷循環if (*(char*)pstr1 != *(char*)pstr2){break;}//相等這繼續向下走pstr1 = (char*)pstr1 + 1;pstr2 = (char*)pstr2 + 1;num--;}//直接返回pstr1 和 pstr2對應的差值即可return (*(char*)pstr1 - *(char*)pstr2);
}int main()
{char str1[] = "hello world .............";char str2[] = "hello word";int ret = my_memcmp(str1,str2,5);printf("ret = %d",ret);return 0;
}
可以看到,直接返回了不相等部分的差值,大于0 說明前num個字節 str1大于str2
可以看到,直接返回了相等部分的差值,等于0 說明前num個字節 str1等于str2
5.4 strstrg函數的模擬實現(暴力實現以及KMP算法實現)
暴力實現:
char* my_strstr(const char* ps1, const char* ps2)
{char* p1 = ps1;char* p2 = ps2;char* p3 = ps1;while (*p3 != '\0'){//每次循環一次,將p3指向的下一個內容給p1,p2回到子集的起點p1 = p3;p2 = ps2;while (*p2 != '\0'){if (*p2 == *p1){//相等就p1和p2向后走,一直比較到 p2指向'\0'為止p2++;p1++;}else{//如果在p2到'\0'之前不相等,這直接跳出循環break;}}//如果*p2 == '\0' 那么說明以及在ps1中的子集里找到了與ps2相等的部分,那么就可以返回p3所指向的內容,if (*p2 == '\0'){return p3;}//如果不相等,p3向后走一位p3++;}//如果一直到最后都沒有找到那么將返回空return NULL;
}int main()
{char str1[] = "abcdeffghjkln";char str2[] = "ffg";char* ret = my_strstr(str1,str2);printf(ret);return 0;
}


void GetNext(char* ps2,int* next,int len2)
{next[0] = -1;next[1] = 0;int i = 2;int k = 0;//一直插入數據到next的最后一個位置while(i < len2){// 如果 k 等于 -1 那么說明已經回退到原點,或者 第 i 的前一項與k位置的字符相等 if (k == -1 ||ps2[i - 1] == ps2[k]){next[i] = k + 1;i++;k++;}//不相等這 k 回退到next[k]對應的值else{k = next[k];}}
}
char* KMP( char* ps1, char* ps2,int pos)
{int len1 = strlen(ps1);int len2 = strlen(ps2);//我們要找的子串,和需要的主串不能為空if (len1 == 0 || len2 == 0){return NULL;}//我們希望開始比較的位置不能大于主串的長度,也不能小于零if (pos<0 || pos > len1){return NULL;}//數組這里我們選擇動態開辟在堆上建next數組int* next = (int*)malloc(sizeof(int) * len2);//如果malloc失敗這直接返回if (next == NULL){return;}//函數得到我們的next數組GetNext(ps2, next,len2);int i = pos;//遍歷主串int j = 0;//遍歷子串//在主串中尋找子串while (i < len1 && j < len2){//如果 j = -1;這說明需要比較下一個,相等這比較下一個if (j == -1||ps1[i] == ps2[j]){i++;j++;}//不相等將 j 更新為next[k] 對應的值在重新去比較else{j = next[j];}}//釋放我們在堆上申請的空間free(next);// 如果j >= len2的長度則說明已經找到主串中的子串,返回對應位置的地址if (j >= len2){return &ps1[i - j];}//沒有找到返回空return NULL;
}
KMP整體代碼如下
#include<stdio.h>
#include<stdlib.h>
void GetNext(char* ps2,int* next,int len2)
{next[0] = -1;next[1] = 0;int i = 2;int k = 0;while(i < len2){if (k == -1 ||ps2[i - 1] == ps2[k]){next[i] = k + 1;i++;k++;}else{k = next[k];}}
}
char* KMP( char* ps1, char* ps2,int pos)
{int len1 = strlen(ps1);int len2 = strlen(ps2);if (len1 == 0 || len2 == 0){return NULL;}if (pos<0 || pos > len1){return NULL;}int* next = (int*)malloc(sizeof(int) * len2);if (next == NULL){return;}GetNext(ps2, next,len2);int i = pos;//遍歷主串int j = 0;//遍歷子串while (i < len1 && j < len2){if (j == -1||ps1[i] == ps2[j]){i++;j++;}else{j = next[j];}}if (j >= len2){free(next);return &ps1[i - j];}free(next);return NULL;
}int main()
{char str1[] = "abcdeffghjkln";char str2[] = "ffghjk";char* ret = KMP(str1,str2,0);printf(ret);return 0;
}