此篇介紹處理字元和字元串的庫函數和注意事項~
字元串
C語言字元串函數的功能非常單一
C語言沒有專門的字元串類型,C語言中字元串類型本質上是一個字元數組。
C字元串的結尾标志是“\0”,字元串本身就帶有一個“\0”。在計算字元串長度時,“\0”是結束标志,不算做字元串内容
以下所有函數使用時,都需包含頭檔案<string.h>
在頭檔案<string.h>中定義了兩組函數。第一組函數的名字以str開頭;第二組函數的名字以mem開頭,本篇隻講str開頭的字元串函數~
字元串函數介紹
1.求字元串長度 strlen
函數原型:
size_t strlen ( const char * str ),
此處的const限制的是位址對應的記憶體空間不能進行修改
從字元串起始位置開始向後找,找到’\0’就結束,傳回該C字元串的長度
strlen的注意事項
1.參數指向的字元串必須以 ’ \0 ‘結束
2.size_t是無符号類型的整數,在标準庫中用來表示"個數",在進行size_t進行相減時,特别容易溢出,故一般不适用size_t.
3.strlen函數傳回的是在字元串中 ‘\0’ 前面出現的字元個數(不包含 ‘\0’ )
4.使用strlen必須是字元串才可以
代碼實作
size_t myStrlen(const char* str) {
//校驗參數合法性
assert(str != NULL);
size_t count = 0;
while (str[count] != '\0') {
count++;
}
return count;
}
2.字元串拷貝 strcpy(比較麻煩)
函數原型:
char * strcpy ( char * destination, const char * source )
strcpy的注意事項:
1.source參數必須是字元串
2.destination參數空間必須足夠大,能夠容納下source,或者使用strncpy來取代
3.将source指向的C字元串複制到destination指向的數組中,包括終止空字元(’\0’),最後傳回destination
代碼實作
char* myStrcpy(char* dest, const char* source) {
//校驗參數合法性
assert(dest != NULL);
assert(source != NULL);
int i = 0;
while (source[i] != '\0') {
dest[i] = source[i];
i++;
}
dest[i] = '\0';
return dest;
}
3. 字元串拼接 strcat(比較麻煩)
函數原型:
char * strcat ( char * destination, const char * source )
strcat的注意事項:
1.目的地和來源不得重疊
2.destination的空間要足夠大,能夠容下拼接後的結果
3.destination最後的結束字元’\0’會被覆寫掉,并在連接配接後的字元串的尾部再增加一個’\0’
char dest[ ]=“hello”
char source[ ]=“world”

将源字元串拷貝追加(尾部增加)到目标字元串。destination 中的’\0’被source的第一個字元覆寫,并且在由destination 中的兩者連接配接形成的新字元串的末尾包含一個空字元,最後傳回destination
代碼實作
char* myStrcat(char* dest, const char* source) {
//校驗參數合法性
assert(dest != NULL);
assert(source != NULL);
//1.找到dest的結束位置
int destEnd = 0;
while (dest[destEnd] != '\0') {
destEnd++;
}
//while循環結束,下标為destEnd的dest便指向\0
//2.字元串拷貝
int i = 0;
while (source[i] != '\0') {
dest[i + destEnd] = source[i];
i++;
}
//3.把dest的最後位置設成\0
dest[i + destEnd] = '\0';
return dest;
}
4. 字元串比較 strcmp
函數原型:
int strcmp ( const char * str1, const char * str2 )
https://blog.csdn.net/m0_47988201/article/details/117201616?spm=1001.2014.3001.5501.
此篇文章的最後部分對strcmp進行了說明
代碼實作
int myStrcmp(const char* str1, const char* str2) {
//校驗參數合法性
assert(str1 != NULL);
assert(str2 != NULL);
const char* p1 = str1;
const char* p2 = str2;
//依次比較每個字元
while (*p1 !='\0'&& *p2 !='\0') {
if (*p1 < *p2) {
return -1;
}
else if (*p1 > *p2) {
return 1;
}
//相等時比較下一個字元
else {
p1++;
p2++;
}
}
if (*p1 < *p2) {
return -1;
}
else if (*p1 > *p2) {
return 1;
}
else {
return 0;
}
}
5.拷貝指定長度字元串 strncpy(比較麻煩)
函數原型:
char * strncpy ( char * destination, const char * source, size_t num )
strncpy誕生的目的是為了避免出現dest空間不夠的情況
将source的前num個字元拷貝到destination
strncpy注意事項
1.當num比source長的時候,拷貝完源字元串之後,在目标的後邊追加0,直到num個
2.當num比source短的時候,就隻拷貝source的前num個字元(不會拷貝\0)
3.num的數值,必須要保證dest能夠容納下(需考慮\0)
4.source和destination所指的記憶體區域不能重疊,且destination必須有足夠的空間放置num個字元
無論觸發strncpy的那種情況,都建議把dest初始化為全0,防止出現沒有\0的情況
一般把num寫成sizeof(dest)-1
代碼實作
char* myStrncpy(char* dest, const char* source, size_t num) {
//校驗參數合法性
assert(dest != NULL);
assert(source != NULL);
assert(num != 0);
int i = 0;
while (source[i] != '\0' && i < num) {
dest[i] = source[i];
i++;
}
while (i < num) {
dest[i] = '\0';
i++;
}
return dest;
}
6.追加指定長度字元串 strncat(比較麻煩)
函數原型:
char * strncat ( char * destination, const char * source, size_t num )
将source的前num 個字元追加到destination,再加上一個\0. 如果source 中C字元串的長度小于num,則隻複制\0之前的内容。
strncat注意事項
1.destination要有足夠大的空間來容納要拷貝的字元串
2.如果num大于字元串source的長度,那麼僅将source全部追加到destination的尾部
3.strncat會将destination字元串最後的’\0’覆寫掉,字元追加完成後,再追加’\0’
代碼實作
char* myStrncat(char* dest, const char* source, size_t num) {
//校驗參數合法性
assert(dest != NULL);
assert(source != NULL);
assert(num != 0);
//1.先找到dest的末尾位置
size_t destEnd = 0;
while (dest[destEnd] != '\0') {
destEnd++;
}
size_t i = 0;
while (source[i] != '\0'&& i < num) {
dest[destEnd + i] = source[i];
i++;
}
dest[destEnd + i] = '\0';
return dest;
}
7. 比較指定長度字元串 strncmp
函數原型:
int strncmp ( const char * str1, const char * str2, size_t num )
比較str1和str2的前num個字元
比較規則遵循字典序
代碼實作
int myStrncmp(const char* str1, const char* str2, size_t num) {
//校驗參數合法性
assert(str1 != NULL);
assert(str2 != NULL);
assert(num != 0);
size_t i = 0;
while (str1[i] != '\0' && str2[i] != '\0' && i < num) {
if (str1[i] < str2[i]) {
return -1;
}
else if (str1[i] > str2[i]) {
return 1;
}
else {
i++;
}
}
if (i == num) {
return 0;
}
return str1[i] - str2[i];
}
8. 查找字元串 strstr
函數原型:
const char * strstr ( const char * str1, const char * str2 )
判斷一個字元串是否包含另一個字元串(字元串子串)
舉例:
str1:helloworld
str2:llo
比較str1是否包含str2
分析過程:
代碼實作
const char* myStrstr(const char* str1, const char* str2) {
//校驗參數合法性
assert(str1 != NULL);
assert(str2 != NULL);
assert(*str1 != '\0');
assert(*str2 != '\0');
//建立指針black和sub,分别指向str1和str2的起始位置
const char* black = str1;
const char* sub = str2;
while (*black != '\0') {
const char* tmp = black;
//每次比對未成功就要從起始位置從新比對
char* sub = str2;
//比較結束的條件:black和sub指針當中有任意一個已經到達了\0 處
while (*sub != '\0' && *tmp != '\0') {
//若相等,sub和tmp繼續向後比較
if (*tmp == *sub) {
sub++;
tmp++;
}
//不相等,直接跳出目前循環
else {
break;
}
}
if (*sub == '\0') {
return black;
}
//比對未成功,從下一位置繼續進行比較
black++;
}
return NULL;
}
9. 字元串切分 strtok
函數原型:
char * strtok ( char * str, const char * delimiters )
此函數需多次調用
對此函數的調用序列将str拆分為标記,這些标記是由作為delimiters一部分的任何字元分隔的連續字元序列
舉例:
str為 aaa bbb ccc
-
第一次調用:strtok(str," “)
從str起始位置開始,向後找” “,當遇到” “時,就傳回第一個aaa,同時把剛才的” "改成’\0’
-
第二次調用:strtok(NULL," “)
此時第一個參數為NULL,表示從上次的切分結果位置繼續往下切分~當找到bbb後面的” “時,仍然把” "改成’\0’,并且傳回bbb
-
第三次調用:strtok(NULL," “)
第一個參數仍為NULL,繼續從上次的結束位置繼續往下切分,沒找到” ",找到ddd後的’\0’,函數傳回ddd
-
第四次調用:strtok(NULL," ")
第一個參數仍為NULL,繼續從上次的結束位置繼續往下切分,上次結束位置後沒有東西了,即找完了,說明整個字元串切分完畢,此時直接傳回NULL
代碼實作
char str[] = "aaa bbb ccc";
char* pch = strtok(str, " ");
while (pch != NULL) {
printf("%s\n", pch);
pch = strtok(NULL, " ");
}
strtok的缺點:
1.需要多次調用才能完成功能;
2.多次調用的過程中參數不同;
3.調用過程中會破壞原來的字元串;
4.strtok内部使用靜态變量記錄上次調用的位置,導緻線程不安全;
故實際開發中不建議使用strtok
參考文檔: http://cplusplus.com/.