做的好的學校網(wǎng)站電話營銷銷售系統(tǒng)
目錄
1、求字符串長度函數(shù)
1.1、strlen
2、字符串拷貝(cpy)、拼接(cat)、比較(cmp)函數(shù)
2.1、長度不受限制的字符串函數(shù)
2.1.1、strcpy
2.1.2、strcat
2.1.3、strcmp
2.2、長度受限制的字符串函數(shù)
2.2.1、strncpy
2.2.2、strncat
2.2.3、strncmp
3、字符串查找函數(shù)
3.1、strstr
3.2、strtok
4、錯誤信息報告函數(shù)
4.1、strerror
4.2、perror
5、字符函數(shù)
5.1、字符分類函數(shù)
5.2、字符轉換函數(shù)
5.2.1、tolower
5.2.2、toupper
6、內存操作函數(shù)
6.1、memcpy
6.2、memmove
6.3、memset
6.4、memcmp
1、求字符串長度函數(shù)
1.1、strlen
- strlen用于求字符串長度。
- 包含頭文件<string.h>。
- 字符串已經(jīng) '\0' 作為結束標志,strlen函數(shù)返回的是在字符串中 '\0' 前面出現(xiàn)的字符個數(shù)(不包含 '\0' )。
- 參數(shù)指向的字符串必須要以 '\0' 結束。
注意:
1、函數(shù)的返回值為size_t,是無符號的( 易錯 )
2、因為strlen返回的是?'\0' 前面的字符個數(shù),如果字符串中間本身就一個'\0',那么返回的值就會返回字符串中的'\0'之前的字符個數(shù)。
例如:"abc\0def" 這個字符串,使用strlen函數(shù)會返回3。
【使用方式】?
int main()
{char arr[] = "Hello hacynn";int ret = strlen(arr);printf("%d\n", ret);return 0;
}
【運行結果】
【易錯提醒】
請問ret的值是多少?
int ret = strlen("abc") - strlen("abcdef");
答案是3,因為函數(shù)的返回值為size_t,是無符號的整型。
【模擬實現(xiàn)strlen】
int my_strlen(char* arr)
{int count = 0;while (*arr != '\0'){count++;arr++;}return count;
}int main()
{char arr[] = "Hello hacynn";int ret = my_strlen(arr);printf("%d\n", ret);return 0;
}
2、字符串拷貝(cpy)、拼接(cat)、比較(cmp)函數(shù)
2.1、長度不受限制的字符串函數(shù)
2.1.1、strcpy
- strcpy用于拷貝字符串,將字符串2拷貝到字符串1當中。
- 包含頭文件<string.h>。
- 源字符串必須以 '\0' 結束。
- 會將源字符串中的 '\0' 拷貝到目標空間。
- 目標空間必須足夠大,以確保能存放源字符串。
- 目標空間必須可變。
【使用方法】?
int main()
{char arr1[] = "Hello hacynn";char arr2[20] = { 0 };strcpy(arr2,arr1);printf("%s\n", arr2);return 0;
}
【運行結果】?
【模擬實現(xiàn)strcpy】
char* my_strcpy(char* dest,const char* src)
{char* ret = dest;while (*dest = *src){dest++;src++;}return ret;
}int main()
{char arr1[] = "Hello hacynn";char arr2[20] = { 0 };my_strcpy(arr2,arr1);printf("%s\n", arr2);return 0;
}
2.1.2、strcat
- ?strcat用于拼接兩個字符串,將字符串2拼接到字符串1末尾。
- 包含頭文件<string.h>。
- 源字符串必須以 '\0' 結束(保證找得到目標空間的末尾),在拷貝時會把源字符串的 '\0 '也拷貝過去。
- 目標空間必須有足夠的大,能容納下源字符串的內容,并且還可以被修改。
注意:
????????不能字符串自己追加自己,因為當自己追加自己的時候,追加的過程中會將目標字符串的 '\0' 覆蓋掉,而有因為此時目標字符串就是源字符串,就會導致源字符沒有 '\0' ,將會一直拼接下去導致死循環(huán)。
????????雖然有些環(huán)境中該函數(shù)可以完成自己拼接自己,但是C語言的標準中并未規(guī)定strcat可以自己拼接自己,所以這個函數(shù)最好不要使用在自己拼接自己的情況下。如果真有自己追加自己的場景,建議使用strncat函數(shù),這個函數(shù)將在下文進行講解。
【使用方式】
int main()
{char arr1[20] = "Hello ";char arr2[] = "hacynn" ;strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}
?【運行結果】
【模擬實現(xiàn)strcat】
char* my_strcat(char* dest, const char* src)
{char* ret = dest;//找到目標空間的末尾while (*dest != '\0'){dest++;}//數(shù)據(jù)追加while (*dest = *src){dest++;src++;}return ret;
}int main()
{char arr1[20] = "Hello ";char arr2[] = "hacynn" ;my_strcat(arr1, arr2);printf("%s\n", arr1);return 0;
}
2.1.3、strcmp
- strcmp用于比較兩個字符串。
- 包含頭文件<string.h>。
- 誤區(qū):該函數(shù)不是比較字符串長度的,而是比較對應位置上字符的大小(ASCII)。
- 標準規(guī)定:
第一個字符串大于第二個字符串,則返回大于0的數(shù)字
第一個字符串等于第二個字符串,則返回0
第一個字符串小于第二個字符串,則返回小于0的數(shù)字
【使用方式】
int main()
{char arr1[] = "abcdef";char arr2[] = "abz";if (strcmp(arr1, arr2) > 0)printf(">\n");else if (strcmp(arr1,arr2) < 0)printf("<\n"); elseprintf("=\n");return 0;
}
【運行結果】?
【模擬實現(xiàn)strcmp】
int my_strcmp(const char* str1, const char* str2)
{while (*str1 == *str2){if (*str1 == '\0')return 0;str1++;str2++;}if (*str1 > *str2)return 1;elsereturn -1;
}int main()
{char arr1[] = "abcdef";char arr2[] = "abz";if (my_strcmp(arr1, arr2) > 0)printf(">\n");elseprintf("<=\n"); return 0;
}
2.2、長度受限制的字符串函數(shù)
- 就是可以限制操作個數(shù)的字符串函數(shù)。
- 包含頭文件<string.h>。
2.2.1、strncpy
- 區(qū)別僅與strcpy差一個參數(shù),記錄要操作的個數(shù)。?
- 拷貝num個字符從源字符串到目標空間。
- 如果源字符串的長度小于num,則拷貝完源字符串之后,在目標的后邊追加0,直到num個。
- 因為拷貝個數(shù)由用戶自己決定,因此\0沒有被拷貝過來的可能性也是有的。
【使用方式】?
int main()
{char arr1[] = "Hello hacynn";char arr2[20] = { 0 };strncpy(arr2, arr1, 5); //拷貝前五個字符 ,此時拷貝\0后arr2中并不會有\(zhòng)0printf("%s\n", arr2);return 0;
}
【運行結果】
【特殊情況】
如果源字符串的長度小于num,則拷貝完源字符串之后,在目標的后邊追加0,直到num個。如下:
int main()
{char arr1[] = "Hello";char arr2[20] = "xxxxxxxxxxxxxxxxx";strncpy(arr2, arr1, 10); //此時10大于arr1的元素個數(shù),就會在后添加0直至夠10個printf("%s\n", arr2);return 0;
}
2.2.2、strncat
- 區(qū)別也僅與strcat差一個參數(shù),記錄要操作的個數(shù)。
- 使用strncat追加,當結束追加時,就算沒到\0,也會在末尾追加一個\0。
- 如果源字符串的長度小于num,則追加完源字符串之后,會自動停止追加。注意此處與strncpy的區(qū)別。
- 包含頭文件<string.h>。
【使用方式】
int main()
{char arr1[20] = "Hello ";char arr2[] = "hacynn" ;strncat(arr1, arr2, 3);printf("%s\n", arr1);return 0;
}
【運行結果】?
2.2.3、strncmp
- 區(qū)別也僅與strcmp差一個參數(shù),記錄要操作的個數(shù)。
- 包含頭文件<string.h>。
【使用方式】
int main()
{char arr1[] = "abcdef";char arr2[] = "abcz";if (strncmp(arr1, arr2, 3) > 0) //只比較前三個字符printf(">\n");else if (strncmp(arr1, arr2, 3) == 0)printf("=\n");elseprintf("<\n");return 0;
}
【運行結果】
?
3、字符串查找函數(shù)
3.1、strstr
- 查找一個字符串中是否存在與另一個字符串當中,即找子串。
- 返回一個指向str1中第一個出現(xiàn)str2的指針,如果str2不是str1的一部分,則返回一個空指針NULL。
- 包含頭文件<string.h>。
【使用方式】
可以看到,即使是有兩個字串 ,也只會返回第一次出現(xiàn)的地址。
int main()
{char arr1[] = "abcdefghidef"; //def出現(xiàn)了兩次char arr2[] = "def";char* ret = strstr(arr1, arr2);if (ret == NULL)printf("找不到\n");elseprintf("%s\n", ret);return 0;
}
【運行結果】?
【模擬實現(xiàn)strstr】
const char* my_strstr(const char* str1, const char* str2)
{if (*str2 == '\0')return str1;char* pc = str1; //pc用于記錄開始匹配的位置while (*pc){char* s1 = pc; //遍歷str1指向的字符串char* s2 = str2; //遍歷str2指向的字符串while (*s1 && *s2 && (*s1 == *s2)){s1++;s2++;}if (*s2 == '\0')return pc;pc++;}return NULL;
}int main()
{char arr1[] = "abcdefghidef";char arr2[] = "def";char* ret = my_strstr(arr1, arr2);if (ret == NULL)printf("找不到\n");elseprintf("%s\n", ret);return 0;
}
【圖解】?
3.2、strtok
?
比較奇葩的一個函數(shù)
char * strtok ( char * str, const char * delimiters );
- 切割字符串函數(shù),例如hacynn@nash.com,當切割標記是@和 . 時,通過三次合理的使用可以切割出三個字符串:hacynn? nash? com
- 包含頭文件<string.h>。
- delimiters參數(shù)是個字符串,定義了用作分隔符的字符集合。
- 第一個參數(shù)指定一個字符串,它包含了0個或者多個由delimiters字符串中一個或者多個分隔符分割的標記。
- strtok函數(shù)找到str中的下一個標記,并將其用 \0 結尾,返回一個指向這個標記的指針。(注:strtok函數(shù)會改變被操作的字符串,所以在使用strtok函數(shù)切分的字符串一般都是臨時拷貝的內容并且可修改。)
- strtok函數(shù)的第一個參數(shù)不為 NULL ,函數(shù)將找到str中第一個標記,strtok函數(shù)將保存它在字符串中的位置。
- strtok函數(shù)的第一個參數(shù)為 NULL ,函數(shù)將在同一個字符串中被保存的位置開始,查找下一個標記。
- 如果字符串中不存在更多的標記,則返回 NULL 指針
【使用方式】
int main()
{char arr[] = "hacynn@nash.com";char buf[200] = { 0 }; //因為strtok會改變被操作字符串,//所以拷貝一個臨時變量來操作strcpy(buf, arr);char* p = "@.";char* s = strtok(buf, p); //參數(shù)不為NULL,找到第一個標記printf("%s\n", s);s = strtok(NULL, p); //參數(shù)為NULL,找到下一個標記printf("%s\n", s);s = strtok(NULL, p); 參數(shù)為NULL,找到下一個標記printf("%s\n", s);return 0;
【運行結果】
【使用方式優(yōu)化 】
在實際開發(fā)中,我們不一定知道這個字符串是怎樣的,這個字符串需要切割幾次的,因此手動設置切割幾次將代碼寫死的方式是不可取,而應該使用以下的方式進行自動切割。
int main()
{char arr[] = "hacynn@nash.com.hahaha@abcd";char buf[200] = { 0 };strcpy(buf, arr);char* p = "@.";char* s = NULL;for (s = strtok(buf, p); s != NULL; s = strtok(NULL, p)){printf("%s\n", s);}return 0;
}
這里巧妙的運用了for函數(shù)的初始化部分只執(zhí)行一次的特點,而strtok也只需要第一次傳地址,其他時候都只需要傳NULL就行。
【優(yōu)化后的運行結果】?
4、錯誤信息報告函數(shù)
4.1、strerror
- strerror函數(shù)是將錯誤碼翻譯成錯誤信息,返回錯誤信息的字符串起始地址。
- 包含頭文件<string.h>。
- C語言中使用庫函數(shù)的時候,如果發(fā)生錯誤,就會將錯誤碼放在errno的變量中,errno是一個 全局變量,可以直接使用。
?【錯誤碼舉例】
int main()
{int i = 0;for ( i = 0; i < 10; i++){printf("%d: %s\n", i, strerror(i));}return 0;
}
每一個錯誤碼都對應一個錯誤信息?
【使用方式】
以打開文件為例子,fopen以讀的形式打開文件,當文件存在時打開成功,文件不存在時打開失敗,并返回空指針??梢岳眠@個來設置一個打開失敗時的錯誤信息告知。
int main()
{FILE* pf = fopen("add.txt", "r"); //當前文件路徑中并沒有add.txt文件,打開失敗if (pf == NULL){printf("打開文件失敗,原因是:%s\n", strerror(errno));return 1;}else{printf("打開文件成功\n");}return 0;
}
【運行結果】
4.2、perror
- perror也是用于翻譯錯誤信息 ,但與strerror不同的是,perror會直接打印錯誤碼所對應的錯誤信息。而perror中傳遞的字符串參數(shù)就是自定義顯示信息的部分,打印的結果就是?自定義顯示信息:錯誤信息
- 包含頭文件<stdlib.h>
- 可以簡單理解為:perror = printf + strerror 即翻譯又打印
【使用方式】
int main()
{FILE* pf = fopen("add.txt", "r");if (pf == NULL){perror("打開文件失敗"); //注意:此處是perror,不是printf。return 1;}else{printf("打開文件成功\n");}return 0;
}
【運行結果】?
5、字符函數(shù)
5.1、字符分類函數(shù)
字符分類函數(shù)使用非常簡單,由于篇幅受限,在這里不就一一列舉了?,只需要把下面的圖看懂就行。
5.2、字符轉換函數(shù)
5.2.1、tolower
這個函數(shù)聽名字就知道是用于將大寫字母轉換成小寫字母,而這類函數(shù)唯一需要注意的就是函數(shù)有返回值,返回類型為int,因此在使用的時候最好使用一個int ret接收返回值。
int main()
{int ret = tolower('A');printf("%c\n", ret);
}
5.2.2、toupper
小寫字母轉大寫字母,其他注意點與tolower一致。
6、內存操作函數(shù)
上文講到的字符串函數(shù)只適用于字符串,但是內存中的數(shù)據(jù)不僅僅只有字符,這就導致這些函數(shù)有很大的局限性。因此需要有一個能夠對所有類型的數(shù)據(jù)都適用的函數(shù),這就是內存操作函數(shù)的出現(xiàn)的原因。下面我們來學習一下內存操作函數(shù)。
6.1、memcpy
- 函數(shù)memcpy從source的位置開始向后拷貝num個字節(jié)的數(shù)據(jù)到destination的內存位置。
- 包含頭文件<string.h>
- 這個函數(shù)在遇到 '\0' 的時候并不會停下來。
- 如果source和destination有任何的重疊,復制的結果都是未定義的。
- 因為C語言標準中并未規(guī)定memcpy能適用于重疊內存的拷貝,因此不重疊內存的拷貝才使用memcpy,而重疊內存的拷貝使用接下來講解的memmove函數(shù)。
【使用方式】?
使用memcpy拷貝整型數(shù)據(jù)。
int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5 };memcpy(arr1, arr2, sizeof(int) * 5);int i = 0;for ( i = 0; i < 5; i++){printf("%d ", arr1[i]);}return 0;
}
【運行結果】?
?
【模擬實現(xiàn)memcpy】
void* my_memcpy(void* dest, const void* src, size_t sz)
{void* ret = dest;while (sz){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;sz--;}return ret;
}int main()
{int arr1[10] = { 0 };int arr2[] = { 1,2,3,4,5 };my_memcpy(arr1, arr2, sizeof(int) * 5);int i = 0;for ( i = 0; i < 5; i++){printf("%d ", arr1[i]);}return 0;
}
6.2、memmove
- ?memmove的參數(shù)和功能與memcpy完全一致。
- 包含頭文件<string.h>
- 唯一有區(qū)別的就是memmove函數(shù)處理的源內存塊和目標內存塊是可以重疊的。
- 因此當出現(xiàn)重疊內存的拷貝時,就使用memmove函數(shù)處理。
?【模擬實現(xiàn)memmove】
void* my_memmove(void* dest, const void* src, size_t sz)
{void* ret = dest;if (dest < src){while (sz){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;sz--;}}else{while (sz--){*((char*)dest + sz) = *((char*)dest + sz);}}return ret;
}int main()
{int arr1[] = { 1,2,3,4,5 ,6,7,8,9,10 };my_memmove(arr1, arr1+2, sizeof(int) * 5);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;
}
6.3、memset
- 將ptr所指向空間的前num個字節(jié)設置為指定值value。
- 包含頭文件<string.h>
【使用方式】?
int main()
{char arr[] = "hello world";memset(arr + 6, 'x', 3);printf("%s\n", arr);return 0;
}
?【運行結果】
6.4、memcmp
- 比較ptr1和ptr2前num個字節(jié)的內容。
- 包含頭文件<string.h>
- 標準規(guī)定:
ptr1大于ptr2,則返回大于0的數(shù)字。
ptr1等于ptr2,則返回0。
ptr1小于ptr2,則返回小于0的數(shù)字。
?【使用方式】
int main()
{int arr1[] = { 1,2,3,4,5,6,7 };int arr2[] = { 1,2,3,7 };int ret = memcmp(arr1, arr2, sizeof(int) * 3);printf("%d\n", ret);
}
【運行結果】?
如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!
如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!
如果覺得作者寫的不錯,求給博主一個大大的點贊支持一下,你們的支持是我更新的最大動力!