C語言中字符串庫函數(shù)的實(shí)現(xiàn)及模擬
前言
我們已經(jīng)了解了C語言中很多數(shù)據(jù)類型,比如int(整數(shù)類型)、char(字符類型)、以及浮點(diǎn)型的double(雙精度)、float(單精度),但是有一點(diǎn)就是我們發(fā)現(xiàn)這里并沒有提到我們常見的有關(guān)字符串的類型。其實(shí)在C語言中,字符串通常是放在 常量字符串 中或者 字符數(shù)組 中的。(常量字符串是不可被修改的)
1.strlen的實(shí)現(xiàn)及模擬(求字符串長度)
字符串是雙引號" "引起來的,以 ‘\0’ 作為結(jié)束標(biāo)志,strlen是專門用來求字符串長度的,strlen函數(shù)返回的是在字符串中 ‘\0’ 前面出現(xiàn)的字符個數(shù)(不包含 ‘\0’ )。并且返回值必須是正數(shù),因?yàn)樽址麄€數(shù)不存在負(fù)數(shù)情況!
使用以及模擬實(shí)現(xiàn)
#include<stdio.h>//printf頭文件 #include<string.h>//strlen頭文件 #include<assert.h>//assert頭文件 //返回值為無符號類型(正數(shù)) //模擬實(shí)現(xiàn),這里盡量做到還原原函數(shù)的形式 size_t my_strlen(const char* str) { assert(str);//斷言,如果str是NULL,編譯器報(bào)錯 const char* start = str;//首字符地址賦給start以及end const char* end = str; while (*end != '\0')//當(dāng)end指向'\0'時結(jié)束 { end++; } return end - start;//指針相減得到兩者之間元素個數(shù) } int main() { char arr[] = "abcdef"; printf("%d\n", strlen(arr));//arr是數(shù)組名,表示數(shù)組首元素地址,即a的地址,返回值為\0之前的字符個數(shù),即6 int len=my_strlen(arr); printf("%d\n", len);//6 return 0; }
這里我是采用指針-指針=兩指針之間元素個數(shù)的方式,還有很多方式都可以模擬實(shí)現(xiàn),比如創(chuàng)建一個變量計(jì)數(shù)的方式等,如下:
size_t my_strlen(const char * str) { assert(str);//斷言 size_t count = 0;//無符號 while(*str) { count++;//指針從起始位置開始往前走,每走一次count++一次 str++; } return count;//最后返回count }
2.strcmp的實(shí)現(xiàn)及模擬 (字符串比較)
strcmp是用來比較兩個字符串的。兩個字符串比較的其實(shí)是各字符所對應(yīng)的ASCII碼值,如下:
使用以及模擬實(shí)現(xiàn)
#include<string.h>//strcpy頭文件包含 #include<stdio.h>//printf頭文件包含 #include<assert.h>//斷言 int my_strcmp(const char* s1, const char* s2) { assert(s1 && s2);//斷言 while (*s1 == *s2)//如果兩個字符串中對應(yīng)的字符相等,則繼續(xù)往后找 { if (*s1 == '\0')//當(dāng)s1到\0時,說明s2也是\0,字符串直到結(jié)束,兩者都相等 return 0;//返回0 //如果沒到\0,繼續(xù)往后走,進(jìn)行下一個字符比較 s1++; s2++; } //當(dāng)跳出循環(huán)時,說明s1與s2指向的字符不相等,直接返回兩者之差即可 return *s1 - *s2; //*s1 -*s2>0 即 *s1>*s2,即字符數(shù)組arr1中的字符串>arr2中的 //反之則小于 } int main() { //字符串存放在字符數(shù)組 char arr1[] = "abcdeaf"; char arr2[] = "abcdef"; //模擬實(shí)現(xiàn) int len=my_strcmp(arr1, arr2); //strcmp兩個參數(shù),arr1,arr2數(shù)組名,即首元素地址,即字符串首字符地址 printf("%d\n", strcmp(arr1, arr2));//-5 printf("%d\n", len);//-5 a對應(yīng)ASCII:97 f:102 // 97-102 ==-5 return 0; }
strncmp的實(shí)現(xiàn)及模擬
strncmp與strcmp很類似,也是用來比較兩個字符串的,唯一的區(qū)別就是strcmp是比較整個字符串,而strncmp可以指定比較的第多少個字符,比如說比較abcdef與abcdfg兩個字符串的前三個字符,結(jié)果返回值就是0。因?yàn)閮H僅比較的是前三個字符,即abc。
模擬實(shí)現(xiàn)
#include<string.h> #include<stdio.h> #include<assert.h> int my_strncmp(const char* str1, const char* str2, size_t num) { assert(str1 && str2);//斷言 //比較前num個字符,兩者相等時,進(jìn)入循環(huán),繼續(xù)往后比,num限制比較字符數(shù) while (--num && (*str1) && (*str1 == *str2)) { str1++; str2++; } //跳出循環(huán),說明num比完了,或者有一方遇到\0,或者兩字符不相等 //直接返回兩者之差 return *str1 - *str2; } int main() { char arr1[] = "abcdef"; char arr2[] = "abcbefgg"; int num = 0; scanf("%d", &num); //比較字符串a(chǎn)rr1與arr2的前num個字符 int len=my_strncmp(arr1, arr2,num); //printf("%d\n", strncmp(arr1, arr2,3));//0 printf("%d\n", len);// num=3,len=0;num=4,len>0 return 0; }
3.strcpy的實(shí)現(xiàn)及模擬(字符串拷貝)
strcpy是用來拷貝字符串的
注意事項(xiàng)
- !!! 源字符串必須以 ‘\0’ 結(jié)束。
- !!! 會將源字符串中的 ‘\0’ 拷貝到目標(biāo)空間。
- !!! 目標(biāo)空間必須足夠大,以確保能存放源字符串。
- !!! 目標(biāo)空間必須可變。
使用及模擬實(shí)現(xiàn)
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcpy(char*dest,char*src) { //斷言 assert(dest && src); char* ret = dest;//用來記住arr的起始地址 while (*dest++ = *src++)//不斷地賦值,arr不斷地被覆蓋 { ; } //返回起始地址 return ret; } int main() { char arr[20] = "hello"; char* p = "abcdef"; //strcpy(arr, p); //模擬實(shí)現(xiàn) my_strcpy(arr, p); printf("%s", arr);//abcdef return 0; }
strncpy的使用及模擬實(shí)現(xiàn)
strncpy與strcpy的使用相類似,但是strcpy是拷貝整個字符串,而strncpy是可以指定拷貝,即只拷貝num個字符,如果源字符串的長度小于num,則拷貝完源字符串之后,在目標(biāo)的后邊追加0,直到num個。
使用及模擬實(shí)現(xiàn)
#include<stdio.h> #include<assert.h> char* my_strncpy(char* dest, const char* sor, size_t num) { //斷言 assert(dest && sor); char* s = dest;//起始地址記住 int i = 0; for (i = 0; i < (int)num; i++) { //開始拷貝,num次,每次拷貝一個字符,拷貝完后,兩指針往后走,繼續(xù)拷貝 *dest++ = *sor++; } //返回起始地址 return s; } int main() { char arr1[20] = "abcdefghi"; char arr2[20] = "xxxx"; size_t n = 0; scanf("%d", &n); my_strncpy(arr1, arr2, n); //從arr2中拷貝n個字符到arr1去 printf("%s", arr1);//n=4,xxxxefghi\0 ; n=5,xxxx\0fghi\0,但是打印的是xxxx,因?yàn)橛龅絓0字符串停止 return 0; }
4.strcat的實(shí)現(xiàn)及模擬(字符串追加)
strcat是字符串追加函數(shù),顧名思義就是在一個字符串后面再增加另外一個字符串。
注意事項(xiàng)
- 源字符串必須以 ‘\0’ 結(jié)束。
- 目標(biāo)空間必須有足夠的大,能容納下源字符串的內(nèi)容。
- 目標(biāo)空間必須可修改。
- 不能自己給自己追加
實(shí)現(xiàn)及模擬
#include<stdio.h> #include<string.h> #include<assert.h> char* my_strcat(char* dest, const char* src) { //斷言 assert(dest && src); char* sur = dest;//記住起始地址 while (*sur != '\0') { sur++; } //走到這里,sur已經(jīng)指向了arr1的\0處,從這里開始,把src指向的字符即arr2的字符賦給sur while (*sur++ = *src++) { ; } //最后返回arr1追加后的起始空間地址 return dest; } int main() { char arr1[20] = "hello "; char arr2[] = "world"; //在arr1后面增加arr2 //strcat(arr1, arr2); my_strcat(arr1, arr2); printf("%s\n", arr1);//hello world return 0; }
strncat的使用及模擬實(shí)現(xiàn)
strncat與strcat很類似,只不過strcat是追加整個字符串,而strncat是追加指定的字符串,比如說可以給arr1追加4個字符
模擬實(shí)現(xiàn)及使用
#include<stdio.h> #include<assert.h> char* my_strncat(char* dest, const char* sor, size_t num) { assert(dest && sor); char* p = dest; while (*p != '\0') { p++; } //此時已經(jīng)指向arr1中的\0 //把num個字符賦給*p while (num--) { *p++ = *sor++; } return dest; } int main() { char arr1[20] = "hello "; char arr2[20] = "world!!!!!"; size_t n = 0; scanf("%d", &n); my_strncat(arr1, arr2, n); printf("%s", arr1);//n=5,hello world return 0; }
5.strstr的實(shí)現(xiàn)及模擬(字符串查找)
strstr是實(shí)現(xiàn)字符串查找的一個函數(shù),即在一個字符串中查找該字符串是不是含有另一個字符串
使用及模擬
#include<stdio.h> #include<string.h> #include<assert.h> //char* strstr(const char*str1,const char*str2) char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* p = str1; while (*p) { s1 = p; s2 = str2; while (*s1 && *s2 && *s1 == *s2) { s1++; s2++; } if (*s2 == '\0') return (char*)p; p++; } return NULL; } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbpc"; //char* p = strstr(arr1, arr2); char* p = my_strstr(arr1, arr2); if (p == NULL) printf("不存在"); else printf("%s\n", p);//不包含,輸出結(jié)果為不存在 return 0; }
6.strtok函數(shù)(在字符串中定義用作分隔符的集合)
注意事項(xiàng)
1、strtok函數(shù)找到str中的下一個標(biāo)記,并將其用 \0 結(jié)尾,返回一個指向這個標(biāo)記的指針。(注:strtok函數(shù)會改變被其操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都是臨時拷貝的內(nèi)容并且可修改。)
2、strtok函數(shù)的第一個參數(shù)不為 NULL ,函數(shù)將找到str中第一個標(biāo)記,strtok函數(shù)將保存它在字符串中的位置。
3、strtok函數(shù)的第一個參數(shù)為 NULL ,函數(shù)將在同一個字符串中被保存的位置開始,查找下一個標(biāo)記。
4、如果字符串中不存在更多的標(biāo)記,則返回 NULL 指針。
舉例使用
#include<stdio.h> #include<string.h> int main() { char arr[] = "zinuo@qq.com.qdy"; char buf[200] = { 0 }; //p是用作分隔符的字符串首字符地址 const char* p = "@.."; //由于strtok函數(shù)會改變原字符串的內(nèi)容,所以一般都會拷貝一個用來使用 strcpy(buf, arr); char* str = NULL; for (str=strtok(buf,p); str != NULL; str=strtok(NULL,p) ) { //用來做分隔符的字符都被改成了\0,下一次調(diào)用函數(shù)時,會從這個\0開始繼續(xù)往后查找分隔符,再次改為\0,一直到找完所有分隔符,返回NULL printf("%s\n", str);//zinuo //qq //com //qdy } return 0; }
7.strerror函數(shù) (錯誤碼轉(zhuǎn)換錯誤信息)
#include<stdio.h> #include<errno.h>//errno包含頭文件 #include<string.h>//strerror包含頭文件 int main() { //打開文件操作 FILE* pf = fopen("test.txt", "r"); //打開文件失敗,返回空指針,strerror可以將錯誤碼轉(zhuǎn)換成讓我們看得懂的錯誤信息 if (pf == NULL) { perror("lzn");//相當(dāng)于printf+strerror //lzn: No such file or directory //沒有這個文件,錯誤碼轉(zhuǎn)換為錯誤信息打印出來 printf("%s\n", strerror(errno)); //No such file or directory return 1; } //關(guān)閉文件 fclose(pf); pf = NULL; return 0; }
以上就是C語言中字符串庫函數(shù)的實(shí)現(xiàn)及模擬的詳細(xì)內(nèi)容,更多關(guān)于C語言字符串庫函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言學(xué)習(xí)之鏈表的實(shí)現(xiàn)詳解
鏈表是一種物理存儲結(jié)構(gòu)上非連續(xù)、非順序的存儲結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的。這篇文章主要介紹了C語言中鏈表的實(shí)現(xiàn),需要的可以參考一下2022-11-11Visual Studio 2019下配置 CUDA 10.1 + TensorFlow-GPU 1.14.0
這篇文章主要介紹了Visual Studio 2019下配置 CUDA 10.1 + TensorFlow-GPU 1.14.0,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03C++靜態(tài)成員變量和靜態(tài)成員函數(shù)的使用方法總結(jié)
下面小編就為大家?guī)硪黄狢++靜態(tài)成員變量和靜態(tài)成員函數(shù)的使用方法總結(jié)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01