C語(yǔ)言中字符串庫(kù)函數(shù)的實(shí)現(xiàn)及模擬
前言
我們已經(jīng)了解了C語(yǔ)言中很多數(shù)據(jù)類(lèi)型,比如int(整數(shù)類(lèi)型)、char(字符類(lèi)型)、以及浮點(diǎn)型的double(雙精度)、float(單精度),但是有一點(diǎn)就是我們發(fā)現(xiàn)這里并沒(méi)有提到我們常見(jiàn)的有關(guān)字符串的類(lèi)型。其實(shí)在C語(yǔ)言中,字符串通常是放在 常量字符串 中或者 字符數(shù)組 中的。(常量字符串是不可被修改的)
1.strlen的實(shí)現(xiàn)及模擬(求字符串長(zhǎng)度)
字符串是雙引號(hào)" "引起來(lái)的,以 ‘\0’ 作為結(jié)束標(biāo)志,strlen是專(zhuān)門(mén)用來(lái)求字符串長(zhǎng)度的,strlen函數(shù)返回的是在字符串中 ‘\0’ 前面出現(xiàn)的字符個(gè)數(shù)(不包含 ‘\0’ )。并且返回值必須是正數(shù),因?yàn)樽址麄€(gè)數(shù)不存在負(fù)數(shù)情況!

使用以及模擬實(shí)現(xiàn)
#include<stdio.h>//printf頭文件
#include<string.h>//strlen頭文件
#include<assert.h>//assert頭文件
//返回值為無(wú)符號(hào)類(lèi)型(正數(shù))
//模擬實(shí)現(xiàn),這里盡量做到還原原函數(shù)的形式
size_t my_strlen(const char* str)
{
assert(str);//斷言,如果str是NULL,編譯器報(bào)錯(cuò)
const char* start = str;//首字符地址賦給start以及end
const char* end = str;
while (*end != '\0')//當(dāng)end指向'\0'時(shí)結(jié)束
{
end++;
}
return end - start;//指針相減得到兩者之間元素個(gè)數(shù)
}
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//arr是數(shù)組名,表示數(shù)組首元素地址,即a的地址,返回值為\0之前的字符個(gè)數(shù),即6
int len=my_strlen(arr);
printf("%d\n", len);//6
return 0;
}
這里我是采用指針-指針=兩指針之間元素個(gè)數(shù)的方式,還有很多方式都可以模擬實(shí)現(xiàn),比如創(chuàng)建一個(gè)變量計(jì)數(shù)的方式等,如下:
size_t my_strlen(const char * str)
{
assert(str);//斷言
size_t count = 0;//無(wú)符號(hào)
while(*str)
{
count++;//指針從起始位置開(kāi)始往前走,每走一次count++一次
str++;
}
return count;//最后返回count
}
2.strcmp的實(shí)現(xiàn)及模擬 (字符串比較)
strcmp是用來(lái)比較兩個(gè)字符串的。兩個(gè)字符串比較的其實(shí)是各字符所對(duì)應(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)//如果兩個(gè)字符串中對(duì)應(yīng)的字符相等,則繼續(xù)往后找
{
if (*s1 == '\0')//當(dāng)s1到\0時(shí),說(shuō)明s2也是\0,字符串直到結(jié)束,兩者都相等
return 0;//返回0
//如果沒(méi)到\0,繼續(xù)往后走,進(jìn)行下一個(gè)字符比較
s1++;
s2++;
}
//當(dāng)跳出循環(huán)時(shí),說(shuō)明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兩個(gè)參數(shù),arr1,arr2數(shù)組名,即首元素地址,即字符串首字符地址
printf("%d\n", strcmp(arr1, arr2));//-5
printf("%d\n", len);//-5 a對(duì)應(yīng)ASCII:97 f:102
// 97-102 ==-5
return 0;
}
strncmp的實(shí)現(xiàn)及模擬
strncmp與strcmp很類(lèi)似,也是用來(lái)比較兩個(gè)字符串的,唯一的區(qū)別就是strcmp是比較整個(gè)字符串,而strncmp可以指定比較的第多少個(gè)字符,比如說(shuō)比較abcdef與abcdfg兩個(gè)字符串的前三個(gè)字符,結(jié)果返回值就是0。因?yàn)閮H僅比較的是前三個(gè)字符,即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個(gè)字符,兩者相等時(shí),進(jìn)入循環(huán),繼續(xù)往后比,num限制比較字符數(shù)
while (--num && (*str1) && (*str1 == *str2))
{
str1++;
str2++;
}
//跳出循環(huán),說(shuō)明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個(gè)字符
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是用來(lái)拷貝字符串的

注意事項(xiàng)
- !!! 源字符串必須以 ‘\0’ 結(jié)束。
- !!! 會(huì)將源字符串中的 ‘\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;//用來(lái)記住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的使用相類(lèi)似,但是strcpy是拷貝整個(gè)字符串,而strncpy是可以指定拷貝,即只拷貝num個(gè)字符,如果源字符串的長(zhǎng)度小于num,則拷貝完源字符串之后,在目標(biāo)的后邊追加0,直到num個(gè)。

使用及模擬實(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++)
{
//開(kāi)始拷貝,num次,每次拷貝一個(gè)字符,拷貝完后,兩指針往后走,繼續(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個(gè)字符到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ù),顧名思義就是在一個(gè)字符串后面再增加另外一個(gè)字符串。
注意事項(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處,從這里開(kāi)始,把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很類(lèi)似,只不過(guò)strcat是追加整個(gè)字符串,而strncat是追加指定的字符串,比如說(shuō)可以給arr1追加4個(gè)字符

模擬實(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++;
}
//此時(shí)已經(jīng)指向arr1中的\0
//把num個(gè)字符賦給*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)字符串查找的一個(gè)函數(shù),即在一個(gè)字符串中查找該字符串是不是含有另一個(gè)字符串

使用及模擬
#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中的下一個(gè)標(biāo)記,并將其用 \0 結(jié)尾,返回一個(gè)指向這個(gè)標(biāo)記的指針。(注:strtok函數(shù)會(huì)改變被其操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都是臨時(shí)拷貝的內(nèi)容并且可修改。)
2、strtok函數(shù)的第一個(gè)參數(shù)不為 NULL ,函數(shù)將找到str中第一個(gè)標(biāo)記,strtok函數(shù)將保存它在字符串中的位置。
3、strtok函數(shù)的第一個(gè)參數(shù)為 NULL ,函數(shù)將在同一個(gè)字符串中被保存的位置開(kāi)始,查找下一個(gè)標(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ù)會(huì)改變?cè)址膬?nèi)容,所以一般都會(huì)拷貝一個(gè)用來(lái)使用
strcpy(buf, arr);
char* str = NULL;
for (str=strtok(buf,p); str != NULL; str=strtok(NULL,p) )
{
//用來(lái)做分隔符的字符都被改成了\0,下一次調(diào)用函數(shù)時(shí),會(huì)從這個(gè)\0開(kāi)始繼續(xù)往后查找分隔符,再次改為\0,一直到找完所有分隔符,返回NULL
printf("%s\n", str);//zinuo
//qq
//com
//qdy
}
return 0;
}
7.strerror函數(shù) (錯(cuò)誤碼轉(zhuǎn)換錯(cuò)誤信息)
#include<stdio.h>
#include<errno.h>//errno包含頭文件
#include<string.h>//strerror包含頭文件
int main()
{
//打開(kāi)文件操作
FILE* pf = fopen("test.txt", "r");
//打開(kāi)文件失敗,返回空指針,strerror可以將錯(cuò)誤碼轉(zhuǎn)換成讓我們看得懂的錯(cuò)誤信息
if (pf == NULL)
{
perror("lzn");//相當(dāng)于printf+strerror
//lzn: No such file or directory
//沒(méi)有這個(gè)文件,錯(cuò)誤碼轉(zhuǎn)換為錯(cuò)誤信息打印出來(lái)
printf("%s\n", strerror(errno));
//No such file or directory
return 1;
}
//關(guān)閉文件
fclose(pf);
pf = NULL;
return 0;
}
以上就是C語(yǔ)言中字符串庫(kù)函數(shù)的實(shí)現(xiàn)及模擬的詳細(xì)內(nèi)容,更多關(guān)于C語(yǔ)言字符串庫(kù)函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言學(xué)習(xí)之鏈表的實(shí)現(xiàn)詳解
鏈表是一種物理存儲(chǔ)結(jié)構(gòu)上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。這篇文章主要介紹了C語(yǔ)言中鏈表的實(shí)現(xiàn),需要的可以參考一下2022-11-11
Visual Studio 2019下配置 CUDA 10.1 + TensorFlow-GPU 1.14.0
這篇文章主要介紹了Visual Studio 2019下配置 CUDA 10.1 + TensorFlow-GPU 1.14.0,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03
C++靜態(tài)成員變量和靜態(tài)成員函數(shù)的使用方法總結(jié)
下面小編就為大家?guī)?lái)一篇C++靜態(tài)成員變量和靜態(tài)成員函數(shù)的使用方法總結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
C語(yǔ)言return知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家整理的是關(guān)于C語(yǔ)言return知識(shí)點(diǎn)總結(jié)內(nèi)容,需要的朋友們可以學(xué)習(xí)參考下。2020-02-02

