前言:
? ? ? ? 在字符串和字符串函數(1)-CSDN博客中,已將將字符串和字符函數的使用了解,并且實現模擬了一些字符串的庫函數。????????
? ? ? ? 接下來將繼續深入學習字符串和字符串函數。并且模擬實現一些較為復雜的函數。
可控制字符長的的函數
介紹并使用strncpy函數
??? ? ?相同點:?strncpy函數和strcpy函數的形同點是都可以實現字符串的拷貝,可以將一個字符串中的內容拷貝到另一個字符串中。
? ?? ? 不同點:strnpy和strcpy函數的區別在于,strcpy只能將一個字符串中的所有內容全部拷貝到另一個字符串中,但是strnpy可以將一個字符串中的內容,有選擇的拷貝到另一個字符串當中,參數也從2個變成了3個。? ?
例如:我要將arr1[]中的前3個字符拷貝到arr2[]中:
? ? ? ? 可不可以這樣實現,其實問題就在于傳完3個字符之后,有沒有將\0也傳進去?
int main()
{char arr1[] = { "ab cd ef" };char arr2[20];strncpy(arr2, arr1, 3);printf("%s\n", arr2);return 0;
}
通過調試可以看到:
? ? ? ? 在arr1中:
拷貝之后,arr2中是這樣:
發現:并沒有將'\0'考進去!
所以,分兩種情況:
? ? 1、如果我想把一個字符串中幾個字符給拿出來,這時候需要注意我們要手動將拿出來的字符串中的最后一個字符后面的一個字符加上'\0'
? ? ?2、如果我只是想將一個字符串中的前幾個元素做一個替換,此時就不需要手動加上'\0'。
例1:拿出arr1中的3個字符。
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = { "ab cd ef" };char arr2[20];strncpy(arr2, arr1, 3);arr2[3] = '\0';printf("%s\n", arr2);return 0;
}
打印結果:
????????
例2:替換arr2中的前三個字符
????????
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = { "ab cd ef" };char arr2[20] = {"hjklo"};strncpy(arr2, arr1, 3);printf("%s\n", arr2);return 0;
}
模擬實現strncpy函數:
? ? ? ? 我們可以在strcpy的基礎上再進行一些修改:
#include<stdio.h>
#include<assert.h>
char* my_strnpy( char* arr2, const char* arr1, size_t num)
{assert(arr1 != NULL);assert(arr2 != NULL);char* start = arr2;while (num){*arr2++ = *arr1++;num--;}return start;
}int main()
{char arr1[] = {"love you"};char arr2[] = { "like me" };my_strnpy(arr2, arr1,4);printf("%s\n", arr2);return 0;
}
介紹并使用strncat函數:
與strcat相比較:
相同點:都可以實現對字符串的追加。
不同點:strncat按自我需求調整所追加的字符串的長度。
? ? ? ? ? ? ? ? strcat只能將一個字符串全部內容追加到另一個字符串當中。
追加的時候是從目的地字符數組的'\0'處開始追加。
看一組代碼:
#include<stdio.h>
#include<string.h>
int main()
{char arr1[] = {"abcd"};char arr2[20] = { "asdf " };strncat(arr2, arr1, 4);printf("%s\n", arr2);return 0;
}
通過調試來觀測一下:
? ? ? ? 初始化之后:
進入函數之后:
????????
注意:這是后也沒有主動追加'\0',可以最后手動追加。
模擬實現strncat函數:
????????
char* my_strncat(char* arr1, const char* arr2, size_t num)
{assert(arr1 != NULL);assert(arr2 != NULL);char* start = arr1;while (*arr1){arr1++;}while (num){*arr1++ = *arr2++;num--;}return start;
}
int main()
{char arr1[20] = {"love me "};char arr2[] = { "like you" };my_strncat(arr1, arr2, 4);printf("%s\n", arr1);return 0;
}
介紹并使用strncmp函數:
????????
相關介紹和返回值,發現和strcmp函數的返回值是一樣的。
區別:除了多了一個參數num,也就是你要比較幾個字符之外,其他的都是一樣的。
int main()
{char arr1[] = {"abcdef"};char arr2[] = { "abcds" };int a = strncmp(arr1, arr2,5);if (a == 0){printf("=");}else if (a < 0){printf("<");}else{printf(">");}return 0;
}
模擬實現strncmp函數
????????
int my_strncmp(const char* arr1, const char* arr2, size_t num)
{assert(arr1 != NULL);assert(arr2 != NULL);while ( *arr1 - *arr2 == 0){arr1++;arr2++;num--;if (num-1 == 0){break;}}return *arr1-*arr2;
}
int main()
{char arr1[] = {"abcd"};char arr2[] = {"abfde"};int a = my_strncmp(arr1,arr2,4);if (a == 0){printf("=");}else if (a < 0){printf("<");}else{printf(">");}return 0;
}
其他類型庫函數
介紹并使用strstr函數:
????????
返回指向?str1?中首次出現的?str2?的指針,如果?str2?不是?str1?的一部分,則返回空指針.
????????
int main() {char arr1[] = {"love you best"};char *arr = strstr(arr1, "you");printf("%s\n", arr);return 0; }
? ?在arr1中找you,找到第一次出現的地方返回第一次出現的首字符的地址。
打印結果:
如果找不到,就返回NULL;
例如:
int main() {char arr1[] = {"love you best"};char *arr = strstr(arr1, "yoo");printf("%s\n", arr);return 0; }
打印結果:
注:這里找相同的字符串必須完全相同,差一個字符都不行!!
模擬實現strstr函數:
? ? ? ? 首先通過畫圖來講解:
????????
char* my_strstr(const char* arr1, const char* arr2)
{char* cp = (char*)arr1;//表示arr1的紅色指針char* a;//表示arr1的綠色指針char* b;//表示arr2的綠色指針while (*cp){if (*cp == *arr2){a = cp;b = (char*)arr2;while (*cp == *b){cp++;b++;if (*b == '\0')return a;}}cp++;}return NULL;
}
int main()
{char arr1[] = { " welcom next new world" };char arr2[] = {"new"};char *str = my_strstr(arr1, arr2);printf("%s\n", str);return 0;
}
介紹并使用memcpy(內存拷貝函數)函數:
????????
之前探討了strcpy和strncpy,這兩個庫函數是用來拷貝字符串的,其它類型的不能拷貝,那么其他類型的數據該如何拷貝呢?
? ? ? ? 就需要用到memcpy函數,可以拷貝任意類型的數據!
例如:
int main()
{int arr1[] = {1,2,3,4,5};int arr2[20];memcpy(arr2,arr1, 20);return 0;
}
需要三個參數,目的地地址,源頭地址,需要拷貝的字節數,
? ? ? ? 通過調試發現:
拷貝成功!
模擬實現memcpy函數
void* my_memcpy(void* str1, const void *str2, size_t sz)
{void* start = str1;assert(str1 && str2);while (sz){*(char*)str1 = *(char*)str2;str1 = (char*)str1 + 1;str2 = (char*)str2 + 1;sz--;}return start;
}
int main()
{int arr1[] = { 1,2,3,4,5 };int arr2[10];my_memcpy(arr2,arr1,20);return 0;
}
拷貝完成!
但是,這回突發奇想,想將arr1中的前4個拷貝到從第二個整形開始。可不可以呢?
void* my_memcpy(void* str1, const void *str2, size_t sz) {void* start = str1;assert(str1 && str2);while (sz){*(char*)str1 = *(char*)str2;str1 = (char*)str1 + 1;str2 = (char*)str2 + 1;sz--;}return start; } int main() {int arr1[] = { 1,2,3,4,5,6,7,8,9 };my_memcpy(arr1+2,arr1,16);return 0; }
發現是不可以的,那么如果我要實現重疊部分的拷貝,應該怎么辦,可以使用庫函數中的memmove函數!
? ? ? ? 接下來就來探究一下memmove函數!
介紹并使用memmove函數
和memcopy相比,memmove可以完成重疊部分的拷貝工作!
int main() {int arr1[] = { 1,2,3,4,5,6,7,8,9 };memmove(arr1+2,arr1,16);return 0; }
發現可以進行重疊部分的拷貝!
模擬實現memmove函數:
? ? ? ? 那么應該怎樣拷貝,才不會出現在memcpy中的情況呢,這里分為三種情況:
1、源頭地址在目的地的左側,源頭低地址,目的地高地址。
此時不會再出現被覆蓋的情況。
2、源頭地址在目的地的左右側,源頭高地址,目的地低地址。
3、源頭和目的地沒有重疊部分
這種情況怎么拷貝都行,只要空間夠用即可。
以上這幾種情況就是memmove函數的思路!
以下是代碼:
void* my_memmove(void* str1, void* str2, size_t num)
{void* start = str1;int sz = num;if (str1 > str2){while (sz){str2 = (char*)str2+1;str1 = (char*)str1 + 1;sz--;}while (sz != num){*(char*)str1 = *(char*)str2;str1 = (char*)str1-1;str2 = (char*)str2 - 1;sz++;}}else {while (sz--){*(char*)str1 = *(char*)str2;str1 = (char*)str1 + 1;str2 = (char*)str2 + 1;}}return start;
}int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9 };my_memmove(arr1,arr1+2,12);return 0;
}
????????