目錄
前言:
一、函數(shù)介紹:
(一)求字符串長(zhǎng)度
(1)strlen
(二)長(zhǎng)度不受限制的字符串函數(shù)
(2)strcpy
(3)strcat
(4)strcmp
(三)長(zhǎng)度受限制的字符串函數(shù)
(5)strncpy
(6)strncat
(7)strncmp
(四)字符串查找
(8)strstr
(9)strtok
(五)錯(cuò)誤信息報(bào)告
(10)strerror
(六)字符操作
(11)字符分類函數(shù)
(12)字符轉(zhuǎn)換
(七)內(nèi)存操作函數(shù)
(13)memcpy
(14)memmove
(15)memcmp
(16)memset
二、模擬實(shí)現(xiàn)
(一)strlen
(1)計(jì)數(shù)器
(2)遞歸
(3)指針-指針
(二)strcpy
(三)strcat
(四)strcmp
(五)strstr
(六)memcpy
(七)memmove
前言:
本篇重點(diǎn)圍繞字符和字符串的部分庫函數(shù)展開講解,并進(jìn)行模擬實(shí)現(xiàn),下一篇內(nèi)容會(huì)運(yùn)用本篇所學(xué)進(jìn)行左旋右旋字符串的判斷及實(shí)現(xiàn)(三步翻轉(zhuǎn)法)。
一、函數(shù)介紹:
(一)求字符串長(zhǎng)度
(1)strlen
size_t? strlen( const char* str );
該函數(shù)的作用是統(tǒng)計(jì)字符串中'\0'之前的字符個(gè)數(shù)(不含'\0')。
- 參數(shù)指向的字符串必須以'\0'結(jié)束。
- 函數(shù)的返回值為無符號(hào)整型size_t。
利用strlen函數(shù)進(jìn)行字符串長(zhǎng)度比較時(shí),要注意得到的結(jié)果是無符號(hào)整型,所以不能用相減為正負(fù)數(shù)為依據(jù)進(jìn)行比較。
(二)長(zhǎng)度不受限制的字符串函數(shù)
長(zhǎng)度不受限制的意思是指本分類下的庫函數(shù)沒有設(shè)置長(zhǎng)度限制,他們都是根據(jù)'\0'的位置來自行判斷是否停止的,所以'\0'對(duì)于此類目下的庫函數(shù)非常重要,所以你可以發(fā)現(xiàn)此類目下的庫函數(shù)都要求源字符串必須以'\0'結(jié)束。
(2)strcpy
char*? strcpy( char* destination, const char* source );
該函數(shù)會(huì)將source指針指向的字符串拷貝到destination指針指向的字符串中,注意包含'\0'。?
- 源字符串必須以'\0'結(jié)束。
- 目標(biāo)空間必須有足夠大,確??梢匀菁{源字符串內(nèi)容。
- 目標(biāo)空間必須可修改。
- 返回值為目標(biāo)空間的首元素地址(初始狀態(tài)下destination的地址)
(3)strcat
char*? strcat( char*?destination, const char* source );
該函數(shù)會(huì)將source指針指向的字符串追加到destination指針指向的字符串后面,注意包含'\0'。
- ?源字符串必須以'\0'結(jié)束。
- 目標(biāo)空間必須有足夠大,確保可以容納源字符串內(nèi)容。
- 目標(biāo)空間必須可修改。
問:該函數(shù)是否可以運(yùn)用到字符串自己給自己追加的情況?
當(dāng)destination指針指向了'\0'時(shí),就做好了追加的準(zhǔn)備,下一步就是將'\0'替換為source指向的元素,并依次向后進(jìn)行,直到檢測(cè)到source指向'\0'時(shí)結(jié)束,但當(dāng)兩指針指向同一塊區(qū)域時(shí),明顯source要尋找的'\0'已經(jīng)被destination修改了,就導(dǎo)致越界出現(xiàn)錯(cuò)誤。?
問:那么如何利用庫函數(shù)使字符串自己給自己追加呢?
我們可以利用另一個(gè)庫函數(shù)strncat就可以解決這一問題,因?yàn)閟trncat是長(zhǎng)度受限制的字符串函數(shù),他停止的依據(jù)取決于參數(shù)num,而不是'\0'的位置。
(4)strcmp
int? strcmp( const char* str1, const char* str2 );
?該函數(shù)會(huì)將兩個(gè)字符串進(jìn)行比較,比較的依據(jù)是兩者第一次出現(xiàn)不同字符的ASCII碼值,如果前者大于后者返回一個(gè)正數(shù),反之返回一個(gè)負(fù)數(shù),全部相等返回0。
(三)長(zhǎng)度受限制的字符串函數(shù)
與長(zhǎng)度不受限制的字符串函數(shù)的區(qū)別在于,該種函數(shù)需要設(shè)置需要操作的字符個(gè)數(shù),也就是參數(shù)num,執(zhí)行結(jié)束的依據(jù)取決于num,而不取決于'\0'的位置,長(zhǎng)度受限制的字符串函數(shù)相較于長(zhǎng)度不受限制的字符串函數(shù)相對(duì)更安全。
(5)strncpy
char* strncpy( char* destination, const char* source, size_t num );
?該函數(shù)的作用是拷貝num個(gè)字符從源字符串到目標(biāo)空間。
- 如果源字符串長(zhǎng)度小于num,則拷貝完源字符串之后,在目標(biāo)的后面追加'\0',直到num個(gè)。
(6)strncat
char* strncat( char* destination, const char* source, size_t num );
?該函數(shù)將源字符串num個(gè)字符追加到目標(biāo)字符串后,并加入結(jié)束標(biāo)志'\0'。
- strncat可以用于字符串自己給自己追加,具體解釋見strcat。
(7)strncmp
int? strncmp( const char* str1, const char* str2, size_t num );
該函數(shù)的作用是比較到字符不一樣或者一個(gè)字符串結(jié)束或者num個(gè)字符全部比較完,前者大于后者返回正數(shù),反之返回負(fù)數(shù),全部相等返回0。?
(四)字符串查找
(8)strstr
char* strstr( const char*str1, const char* str2);
該函數(shù)的作用是返回str2指向的字符串在str1指向的字符串中第一次出現(xiàn)的位置。?
- 如果str2指向的字符串不是str1指向的字符串的一部分,那么返回NULL。
(9)strtok
char* strtok( char* str, const char* sep );
該函數(shù)用作分割字符串,簡(jiǎn)單的說就是根據(jù)分隔符的字符集合sep分割該字符串。
- sep參數(shù)是個(gè)字符串,定義了用作分隔符的字符集合
- 第一個(gè)參數(shù)指定一個(gè)字符串,它包含了0個(gè)或者多個(gè)由sep字符串中一個(gè)或者多個(gè)分隔符分割的標(biāo)記。?
- strtok函數(shù)找到str中的下一個(gè)標(biāo)記,并將其用 \0 結(jié)尾,返回一個(gè)指向這個(gè)標(biāo)記的指針。(注: strtok函數(shù)會(huì)改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都是臨時(shí)拷貝的內(nèi)容并且可修改。)
- strtok函數(shù)的第一個(gè)參數(shù)不為NULL,函數(shù)將找到str中第一個(gè)標(biāo)記,strtok函數(shù)將保存它在字符串中的位置。
- strtok函數(shù)的第一個(gè)參數(shù)為NULL,函數(shù)將在同一個(gè)字符串中被保存的位置開始,查找下一個(gè)標(biāo)記。
- 如果字符串中不存在更多的標(biāo)記,則返回 NULL 指針。
下面給出一段代碼助理解:
假設(shè)我們想要得到一個(gè)郵箱mrfanf@csdn.tech中的去掉符號(hào)后的字符串,即mrfanf、csdn、tech,我們就可以利用strtok函數(shù),首先聲明分隔符集合“@.”。
#include<stdio.h>
#include<string.h>
int main()
{
char* p = "mrfanf@csdn.tech";
const char* sep = "@.";
char arr[30];
char* str = NULL;
strcpy(arr, p);//將數(shù)據(jù)拷貝一份,處理arr數(shù)組的內(nèi)容
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
}
(五)錯(cuò)誤信息報(bào)告
(10)strerror
char* strerror( int errnum );
該函數(shù)的作用是返回錯(cuò)誤碼errnum所對(duì)應(yīng)的錯(cuò)誤信息字符串的地址。
- 庫函數(shù)在執(zhí)行時(shí)如果發(fā)生錯(cuò)誤,會(huì)將一個(gè)錯(cuò)誤碼存放在errno這個(gè)變量中,errno是C語言提供的一個(gè)全局變量。
- 使用該庫函數(shù)需要額外引用頭文件errno.h。
?應(yīng)用:
#include<errno.h>
int main()
{
//C語言中可以操作文件
//操作文件的步驟
//1. 打開文件
//2. 讀/寫
//3. 關(guān)閉文件
FILE* pf = fopen("data.txt", "r");//在當(dāng)前路徑下,打開data.txt文件并讀取
if (pf == NULL)//打開或讀取失敗
{
printf("fopen: %s\n", strerror(errno));
perror("fopen");
//fopen: xxxxxx
return 1;
}
//讀文件
//...
//關(guān)閉文件
fclose(pf);
return 0;
}
perror("fopen");
printf("fopen: %s",strerror(errno));?
以上代碼效果相同(%s前有一個(gè)空格字符),perror的作用就是直接打印錯(cuò)誤信息,""內(nèi)為自定義信息,因?yàn)殄e(cuò)誤是默認(rèn)已經(jīng)存儲(chǔ)的,不需要手動(dòng)輸入,perror自動(dòng)接收。
(六)字符操作
(11)字符分類函數(shù)
函數(shù) | 如果他的參數(shù)符合下列條件就返回真 |
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,換頁‘\f’,換行'\n',回車‘\r’,制表符'\t'或者垂直制表符'\v' |
isdigit | 十進(jìn)制數(shù)字 0~9 |
isxdigit | 十六進(jìn)制數(shù)字,包括所有十進(jìn)制數(shù)字,小寫字母a~f,大寫字母A~F |
islower | 小寫字母a~z |
isupper | 大寫字母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 | 任何可打印字符,包括圖形字符和空白字符 |
不可打印字符:ASCII碼值為0~31的字符都是不可打印字符。
#include <ctype.h>
int main()
{
printf("%d\n", isupper('a'));
printf("%d\n", isdigit('2'));
return 0;
}
?注:有關(guān)字符分類函數(shù)和字符轉(zhuǎn)換函數(shù)都需要引用頭文件ctype.h。
(12)字符轉(zhuǎn)換
int tolower ( int c );轉(zhuǎn)換為小寫字符
int toupper ( int c );轉(zhuǎn)換為大寫字符
?應(yīng)用:
#include <ctype.h>
int main()
{
char arr[20] = { 0 };
gets(arr);//遇到空格繼續(xù)讀
char* p = arr;
while (*p)
{
if (isupper(*p))// *p>='A' && *p<='Z'
{
*p = tolower(*p);//*p = *p+32;
}
p++;
}
printf("%s\n", arr);
return 0;
}
(七)內(nèi)存操作函數(shù)
(13)memcpy
void* memcpy( void* destination, const void* source, size_t num );
該函數(shù)從source的位置開始向后復(fù)制num個(gè)字節(jié)的數(shù)據(jù)到destination的內(nèi)存位置。
- 該函數(shù)在遇到'\0'的時(shí)候并不會(huì)停下來。
- 如果source和destination有任何的重疊,復(fù)制的結(jié)果都是未定義的,即memcpy是用來處理不重疊的內(nèi)存拷貝的。
請(qǐng)參考模擬實(shí)現(xiàn)部分方便理解。?
(14)memmove
void* memmove( void* destination, const void* source, size_t num );
該函數(shù)的功能與memcpy相近,差別在memmove函數(shù)處理的源內(nèi)存塊和目標(biāo)內(nèi)存塊是可以重疊的。
- 如果源空間和目標(biāo)空間出現(xiàn)重疊,就得使用memmove函數(shù)處理,當(dāng)然不重疊拷貝也可以使用memmove,在未來應(yīng)用時(shí),不管兩空間重不重疊都是用memmove即可。?
那么為什么memmove可以處理重疊內(nèi)存的數(shù)據(jù)呢,請(qǐng)以下圖參考模擬實(shí)現(xiàn)代碼理解:
(15)memcmp
int?memcmp( const void* ptr1, const void* ptr2, size_t num );
該函數(shù)作用為比較從ptr1和ptr2指針開始的num個(gè)字節(jié),返回值同strncmp。?
(16)memset
void* memset( void* ptr, int value, size_t num);
該函數(shù)的作用為將ptr指針開始的num個(gè)字節(jié)的數(shù)據(jù)設(shè)置為value。?
二、模擬實(shí)現(xiàn)
(一)strlen
(1)計(jì)數(shù)器
//計(jì)數(shù)器方式
int my_strlen(const char* str)
{
int count = 0;
while (*str)
{
count++;
str++;
}
return count;
}
(2)遞歸
//不能創(chuàng)建臨時(shí)變量計(jì)數(shù)器
int my_strlen(const char* str)
{
if (*str == '\0')
return 0;
else
return 1 + my_strlen(str + 1);
}
(3)指針-指針
//指針-指針的方式
int my_strlen(char* s)
{
char* p = s;
while (*p != '\0')
p++;
return p - s;
}
(二)strcpy
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
char* ret = dest;
assert(dest && src);//斷言兩個(gè)指針都為非空指針
while (*dest++ = *src++)
{
;
}
return ret;
}
(三)strcat
#include<assert.h>
char* my_strcat(char*dest, const char *src)
{
assert(dest && src);//斷言兩個(gè)指針都為非空指針
char* ret = dest;
//1. 找目標(biāo)空間中的\0
while (*dest)
{
dest++;
}
//2. 賦值
while (*dest++ = *src++)
{
;
}
return ret;
}
(四)strcmp
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
//斷言兩個(gè)指針為非空指針
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return (*str1 - *str2);
}
(五)strstr
char* my_strstr(char *str1, char* str2)
{
char* cp = str1;
char* s1 = cp;
char* s2 = str2;
if (*str2 == '\0')
return str1;
while (*cp)
{
//開始匹配
s1 = cp;
s2 = str2;
while (*s1 && *s2 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return cp;
cp++;
}
return NULL;
}
(六)memcpy
#include<assert.h>
void* memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dst && src);//斷言兩個(gè)指針為非空指針
while (num--)
{
*(char*)dest = *(char*)src;//強(qiáng)制轉(zhuǎn)化為char*為一個(gè)字節(jié)方便逐個(gè)賦值
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
注意:
由于dest與src為void*類型,所以不能直接++或--,可以使用
dest = (char*)dest + 1;
src = (char*)src + 1;而不是dest++;? ?src++文章來源:http://www.zghlxwxcb.cn/news/detail-551168.html
(七)memmove
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);//斷言兩個(gè)指針為非空指針
if (dest < src)
{
//前->后
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//后->前
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
本篇內(nèi)容就到這里,下一篇文章仍然是對(duì)字符串的討論,我會(huì)引入旋轉(zhuǎn)字符串的兩種巧妙方法,關(guān)注博主不迷路??????文章來源地址http://www.zghlxwxcb.cn/news/detail-551168.html
到了這里,關(guān)于【C語言】字符函數(shù)和字符串函數(shù)(一)—>庫函數(shù)的介紹與模擬實(shí)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!