strlen:計(jì)算字符串長(zhǎng)度函數(shù)size_t strlen ( const char * str );
注意事項(xiàng):
'\0'
作為結(jié)束標(biāo)志,strlen
函數(shù)返回值是在字符串中'\0'
前面出現(xiàn)的字符個(gè)數(shù)(不包含'\0'
)'\0'
結(jié)束,否則計(jì)算出的長(zhǎng)度是隨機(jī)值size_t
,是無(wú)符號(hào)的?因?yàn)榉祷刂凳?code>size_t,所以就要避免出現(xiàn)下面這樣的代碼:strlen("abc") - strlen("abcde")
,strlen("abc")
算出的結(jié)果是3,strlen("abcde")
算出的結(jié)果是5,可能想著3-5得到-2,實(shí)際上并不是這樣的,這里算出的3和5都是無(wú)符號(hào)整型,算出的-2也是一個(gè)無(wú)符號(hào)整型,-2在內(nèi)存中以補(bǔ)碼的形式存儲(chǔ),從無(wú)符號(hào)整型的視角看去,這串補(bǔ)碼就表示一個(gè)很大的正數(shù)。
//常規(guī)實(shí)現(xiàn)
int my_strlen1(const char* arr)
{assert(arr != NULL);
int count = 0;
while (*arr)
{count++;
arr++;
}
return count;
}
//遞歸實(shí)現(xiàn)
int my_strlen2(const char* arr)
{assert(arr!=NULL);
if (*arr == '\0')
{return 0;
}
else
{return 1 + my_strlen2(arr + 1);
}
}
//指針減指針實(shí)現(xiàn)
int my_strlen3(const char* arr)
{assert(arr != NULL);
const char* start = arr;
while (*arr)
{arr++;
}
return arr - start;
}
int main()
{char arr[] = "abcdef";
int len = my_strlen3(arr);
printf("%d\n", len);
return 0;
}
二:strcpystrcpy:字符串拷貝函數(shù),把源字符串拷貝到目標(biāo)空間char * strcpy ( char * destination, const char * source );
注意事項(xiàng):
source
指向待拷貝的字符串,也叫做源字符串,destination
是目標(biāo)空間的地址char* my_strcpy(char* dest, const char* src)
{assert(dest && src);
char* ret = dest;
while (*dest++ = *src++)
{;
}
return ret;
}
int main()
{char arr1[20] = {0 };
char arr2[] = "hello world";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
三:strcatstrcat:字符串追加函數(shù),將源字符串追加到目標(biāo)字符串后面,目標(biāo)中的終止字符’\0’會(huì)被源字符串的第一個(gè)字符覆蓋char * strcat ( char * destination, const char * source );
注意事項(xiàng):
source
指向要追加的字符串,也叫做源字符串,destination
是目標(biāo)空間的地址'\0'
,作為追加的起始地址'\0'
作為追加的結(jié)束標(biāo)志char* my_strcat(char* dest, const char* src)
{assert(dest && src);
char* ret = dest;
//找目標(biāo)空間的'\0'
while (*dest != '\0')
{dest++;
}
//追加
while (*dest++ = *src++)
{;
}
return ret;
}
int main()
{char arr1[20] = "hello ";
char arr2[] = "world";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
四:strcmpstrcmp:字符串比較函數(shù)int strcmp ( const char * str1, const char * str2 );
注意事項(xiàng):
int my_strcmp(const char* str1, const char* str2)
{assert(str1 && str2);
while (*str1 == *str2)//如果相等就進(jìn)去,兩個(gè)指針加加,但是可能會(huì)出現(xiàn)兩個(gè)字符串相等的情況,兩個(gè)指針都指向'\0',此時(shí)比較就結(jié)束了
{if (*str1 == '\0')
{ return 0;
}
str1++;
str2++;
}
if (*str1 >*str2)
{return 1;
}
else
{return -1;
}
}
int main()
{char arr1[] = "abq";
char arr2[] = "abq";
int ret=my_strcmp(arr1, arr2);
printf("%d\n", ret);
return 0;
}
五:strncpystrncpy:長(zhǎng)度受限的字符串拷貝函數(shù)char * strncpy ( char * destination, const char * source, size_t num );
注意事項(xiàng):
char* my_strncpy(char* dest, const char* src, int num)
{assert(dest && src);
char* ret = dest;
while (num)
{if (*src == '\0')//此時(shí)說(shuō)明src指針已經(jīng)指向了待拷貝字符串的結(jié)束標(biāo)志'\0'處,src指針就不用再++了
{ *dest = '\0';
dest++;
}
else
{ *dest = *src;
dest++;
src++;
}
num--;
}
return ret;
}
int main()
{char arr1[20] = "xxxxxxxxxxxxxxxxxxx";
my_strncpy(arr1, "abcdef", 10);
printf("%s\n", arr1);
return 0;
}
六:strncatstrncat:長(zhǎng)度受限的字符串追加函數(shù)char * strncat ( char * destination, const char * source, size_t num );
注意事項(xiàng):
char* my_strncat(char* dest, const char* src, int sz)
{assert(dest && src);
char* ret = dest;
//找目標(biāo)空間的\0
while (*dest != '\0')
{dest++;
}
//追加
while (sz)
{*dest++ = *src++;
sz--;
}
*dest = '\0';
return ret;
}
int main()
{char arr1[20] = "abc\0xxxxxxxxxxx";
my_strncat(arr1, "defjhigk", 3);
printf("%s\n", arr1);
return 0;
}
七:strncmpstrncmp:長(zhǎng)的受限的字符串比較函數(shù)int strncmp ( const char * str1, const char * str2, size_t num );
注意事項(xiàng):
int my_strncmp(const char* str1, const char* str2, int sz)
{assert(str1 && str2);
while (sz)
{if (*str1< *str2)
{ return -1;
}
else if (*str1 >*str2)
{ return 1;
}
else if(*str1 == '\0'||*str2 =='\0')//當(dāng)有一個(gè)為'\0',說(shuō)明比較就可以結(jié)束了
{ if (*str1 == '\0' && *str2 == '\0')//如果二者都是'\0',說(shuō)明兩個(gè)字符串相等
{ return 0;
}
else if(*str1 =='\0')//如果str1為'\0',說(shuō)明str1小,str2大
{ return -1;
}
else//如果src為'\0',說(shuō)明str1大,str2小
{ return 1;
}
}
sz--;
str1++;
str2++;
}
}
int main()
{int ret = my_strncmp("abcdef", "abcd", 5);
printf("%d\n", ret);
return 0;
}
八:strstrstrstr:字符串查找函數(shù)const char * strstr ( const char * str1, const char * str2 );
注意事項(xiàng):
char* my_strstr(char* str1, char* str2)
{assert(str1 && str2);
if (*str2 == '\0')
{return str1;
}
char* s1 = str1;
char* s2 = str2;
char* cp = str1;
while (*cp)
{s1 = cp;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{ s1++;
s2++;
}
if (*s2 == '\0')
{ return cp;
}
cp++;
}
return NULL;
}
int main()
{char arr1[] = "abbvcbcbbdbbvbnui";
char arr2[] = "bbvb";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{printf("找不到\n");
}
else
{printf("%s\n", ret);
}
return 0;
}
九:strtokstrtok:字符串拆分函數(shù)char * strtok ( char * str, const char * sep );
注意事項(xiàng):
int main()
{char arr[] = "konglong@qq.com";
char* p = "@.";
char buf[20] = {0 };
strcpy(buf, arr);
char* ret=NULL;
for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
{printf("%s\n", ret);
}
return 0;
}
九:strerrorstrerror:把錯(cuò)誤碼轉(zhuǎn)換成錯(cuò)誤信息的函數(shù)char * strerror ( int errnum );
注意事項(xiàng):
一些錯(cuò)誤碼對(duì)應(yīng)的錯(cuò)誤信息:
int main()
{printf("%s\n", strerror(0));
printf("%s\n", strerror(1));
printf("%s\n", strerror(2));
printf("%s\n", strerror(3));
printf("%s\n", strerror(4));
printf("%s\n", strerror(5));
return 0;
}
//結(jié)果:
No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
strerror函數(shù)的一般用法:
int main()
{//打開(kāi)文件
FILE* pf = fopen("test.c", "r");
if (pf == NULL)
{printf("%s\n", strerror(errno));//需要包含頭文件#includereturn 1;
}
//讀文件
//關(guān)閉文件
fclose(pf);
return 0;
}
//打開(kāi)失敗時(shí)屏幕顯示:
No such file or directory
?fopen
是一個(gè)打開(kāi)文件的函數(shù),第一參數(shù)是文件名,第二個(gè)參數(shù)是打開(kāi)方式,如果打開(kāi)成功會(huì)返回一個(gè)有限的指針,打開(kāi)失敗會(huì)返回一個(gè)空指針,此時(shí)我們就可以通過(guò)strerror
函數(shù)去查看為什么打開(kāi)失敗
perror:直接打印錯(cuò)誤信息的函數(shù)void perror ( const char * str );
注意事項(xiàng):
str
指向一個(gè)字符串,這個(gè)字符串包含要在錯(cuò)誤消息本身之前打印的自定義消息。如果是空指針,則不會(huì)打印前面的自定義消息,但仍會(huì)打印錯(cuò)誤消息str
不是空指針,則打印str
后跟冒號(hào) (:
)和一個(gè)空間。然后,無(wú)論str
是否為空指針,都會(huì)打印生成的錯(cuò)誤描述,后跟換行符 ('\n'
)perror函數(shù)的一般用法:
int main()
{//打開(kāi)文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{perror("fopen");
return 1;
}
//讀文件
//關(guān)閉文件
fclose(pf);
return 0;
}
//打開(kāi)失敗時(shí)顯示:
fopen: No such file or directory
十一:一些字符函數(shù)字符分類函數(shù):函數(shù) | 如果它的參數(shù)復(fù)合下列條件就返回真(一個(gè)非零數(shù)字) |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格’ ‘,換頁(yè)’\f’,換行’\n’,回車(chē)’\r’,制表符’\t’或者垂直制表符’\v’ |
isdigit | 十進(jìn)制數(shù)字0~9 |
isxdigit | 十六進(jìn)制數(shù)字,包括所有十進(jìn)制數(shù)字,小寫(xiě)字母a~f,大寫(xiě)字母A~F |
islower | 小寫(xiě)字母a~z |
isupper | 大寫(xiě)字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者數(shù)字,a~z,A~Z ,0~9 |
ispunct | 標(biāo)點(diǎn)符號(hào),任何不屬于數(shù)字或者字母的圖形字符(可打?。?/td> |
isgraph | 任何圖形字符 |
isprint | 任何可打印字符,包括圖形字符和空白字符 |
tolower:將大寫(xiě)字母轉(zhuǎn)換為小寫(xiě)字母int tolower ( int c );
注意事項(xiàng):
toupper:將小寫(xiě)字母轉(zhuǎn)換成大寫(xiě)字母int toupper ( int c );
注意事項(xiàng):
應(yīng)用:
int main()
{char arr[] = "I Have An Apple";
int i = 0;
while (arr[i])
{if (isupper(arr[i]))
{ printf("%c", tolower(arr[i]));//把轉(zhuǎn)換的小寫(xiě)字母直接打印出來(lái)
}
else
{ printf("%c", arr[i]);
}
i++;
}
return 0;
}
//結(jié)果:
i have an apple
int main()
{char arr[] = "I Have An Apple";
int i = 0;
while (arr[i])
{if (isupper(arr[i]))
{ arr[i] = tolower(arr[i]);//用轉(zhuǎn)換后的小寫(xiě)字符替換原有的大寫(xiě)字符
}
printf("%c", arr[i]);
i++;
}
return 0;
}
?以上介紹的這些函數(shù)都是針對(duì)字符串或者字符的,那如果我們要拷貝其他類型的數(shù)據(jù)呢?用上面這些函數(shù)自然就行不通了,此時(shí)就要用到即將介紹的內(nèi)存函數(shù)了。
十二:memcpymemcpy:內(nèi)存拷貝函數(shù)void * memcpy ( void * destination, const void * source, size_t num );
注意事項(xiàng):
?這里的destination
指向要在其中賦值內(nèi)容的目標(biāo)數(shù)組,source
指向要復(fù)制的數(shù)據(jù)源,num
是要復(fù)制的字節(jié)數(shù),注意這里前兩個(gè)指針的的類型還有函數(shù)返回值都是void*,這是因?yàn)?,memcpy這個(gè)函數(shù)是內(nèi)存拷貝函數(shù),它有可能拷貝整型,浮點(diǎn)型,結(jié)構(gòu)體等等各種類型的數(shù)據(jù)……雖然返回類型是void*,但他也是必不可少的,void*也表示一個(gè)地址,用戶可以把它強(qiáng)制轉(zhuǎn)換成自己需要的類型去使用。
應(yīng)用:
//把a(bǔ)rr1中的1、2、3、4、5拷貝到arr2數(shù)組中
int main()
{int arr1[] = {1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = {0 };
memcpy(arr2, arr1, 20);//拷貝5個(gè)整型,就是20個(gè)字節(jié)
return 0;
}
//把a(bǔ)rr1中的3、4、5、6拷貝到arr2數(shù)組中
int main()
{int arr1[] = {1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = {0 };
memcpy(arr2, arr1+2, 16);//此時(shí)只要改變參數(shù)中數(shù)據(jù)源的地址就可以,把3的地址傳過(guò)去就行,復(fù)制4個(gè)整型就是1個(gè)字節(jié)
return 0;
}
模擬實(shí)現(xiàn):void* my_memcpy(void* dest, const void* src, size_t num)
{assert(dest && src);
void* ret = dest;
while (num)
{*(char*)dest = *(char*)src;
((char*)dest)++;
((char*)src)++;
num--;
}
return ret;
}
int main()
{int arr1[] = {1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = {0 };
my_memcpy(arr2, arr1+2, 16);
return 0;
}
?上面代碼中模擬出來(lái)的my_memcpy函數(shù)已經(jīng)可以實(shí)現(xiàn)把a(bǔ)rr1數(shù)組中的元素拷貝到arr2數(shù)組里面去。但當(dāng)我們想把a(bǔ)rr1數(shù)組中的1、2、3、4、5拷貝到arr1數(shù)組中的3、4、5、6、7上去時(shí),就會(huì)發(fā)生錯(cuò)誤,如下圖:
?原因是:當(dāng)1拷貝到3上時(shí),原來(lái)的3已經(jīng)被1替換,當(dāng)2拷貝到4上的時(shí)候,原來(lái)的4已將被2替換。所以當(dāng)拷貝arr[2]到arr[4]上的時(shí)候,原本arr[2]里面存放的3已將被1替換了,同理,所以才得出了不符合我們預(yù)期的結(jié)果。那如何解決這個(gè)問(wèn)題呢?先來(lái)分析這個(gè)問(wèn)題產(chǎn)生的原因,這是因?yàn)樵纯臻g與目標(biāo)空間之間有重疊,這里的arr[2]、arr[3]、arr[4]既是源空間也是目標(biāo)空間,當(dāng)拷貝1和2的時(shí)候把源空間中開(kāi)沒(méi)有拷貝的3和4就給覆蓋了,此時(shí)源空間arr[2]和arr[3]里面存的就不再是3和4了,而是1和2,所以此時(shí)拷貝arr[2]和arr[3]里面的數(shù)據(jù),其實(shí)拷貝的就是1和2。為了解決這個(gè)問(wèn)題,我們可以從后往前拷貝,此時(shí)就不會(huì)出現(xiàn)這樣的問(wèn)題
?解決的思路就是:先拷貝重疊區(qū)域的元素,避免其還沒(méi)拷貝就被覆蓋掉。但并不是一遇到源空間和目標(biāo)空間重疊就從后王前拷貝,比如下面這樣,此時(shí)要把3、4、5、6、7拷貝到1、2、3、4、5里面
?此時(shí)就不能用上面的方法從后往前拷貝了,但是可以根據(jù)上面提到的解決思路:先拷貝重疊區(qū)域的元素,所以此時(shí)就應(yīng)該從前往后拷貝,即按照3、4、5、6、7的順序去拷貝
?因此在拷貝的時(shí)候,是按照從前往后的順序還是從后往前的順序得看具體的情況。當(dāng)目標(biāo)空間的起始位置在源空間的前面時(shí),就得從前往后進(jìn)行拷貝。當(dāng)目標(biāo)空間的起始位置在源空間的后面的時(shí)候,就要從后往前拷貝。當(dāng)源空間和目標(biāo)空間沒(méi)有任何重疊的時(shí)候,不管是從前往后拷貝還是從后往前拷貝效果都一樣。
?接下來(lái)介紹的memmove
函數(shù)就可以完成重疊空間的元素拷貝。
memmove:內(nèi)存拷貝函數(shù)void * memmove ( void * destination, const void * source, size_t num );
?它的參數(shù)、返回值與memcpy函數(shù)一模一樣。這里就不過(guò)多介紹。對(duì)于這兩個(gè)函數(shù)來(lái)說(shuō),目標(biāo)空間必須足夠大,不然就會(huì)發(fā)生越界訪問(wèn)。
void* my_memmove(void* dest, const void* src, int num)
{assert(dest && src);
void* ret = dest;
if (dest< src)
{//從前向后
while (num--)
{ *(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{//從后往前
while (num--)//num為1的時(shí)候,下面的num就是0
{ *((char*)dest + num) = *((char*)src + num);//通過(guò)num的減減就可以實(shí)現(xiàn)對(duì)每一個(gè)字節(jié)的訪問(wèn)
}
}
return ret;
}
int main()
{int arr1[] = {1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr1+2, arr1, 20);
return 0;
}
十四:memcmpmemcmp:內(nèi)存比較函數(shù)int memcmp ( const void * ptr1, const void * ptr2, size_t num );
注意事項(xiàng):
應(yīng)用
int main()
{int arr1[] = {1,2,6 };//01 00 00 00 02 00 00 00 06 00 00 00
int arr2[] = {1,2,5 };//01 00 00 00 02 00 00 00 05 00 00 00
int ret = memcmp(arr1, arr2, 9);
printf("%d\n", ret);
return 0;
}//結(jié)果:
1
十四:memsetmemset:內(nèi)存設(shè)置函數(shù)void * memset ( void * ptr, int value, size_t num );
注意事項(xiàng):
應(yīng)用:
int main()
{char arr[] = "hello word";
memset(arr, 'x', 5);//先把"hello"改成"xxxxx"
printf("%s\n", arr);
memset(arr + 6, 'y', 4);//再把"word"改成"yyyy"
printf("%s\n", arr);
return 0;
}
//
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧