C語言的字符串函數(shù),內(nèi)存函數(shù)筆記詳解
strlen
此函數(shù)接收一個(gè)char*類型參數(shù),返回字符串\0前字符數(shù),注意返回類型是size_t型的
//關(guān)于strlen返回值的一個(gè)易錯(cuò)點(diǎn) int main() { const char* str1 = "abcdef"; const char* str2 = "bbb"; if (strlen(str2) - strlen(str1) > 0) { printf("str2>str1\n"); } else { printf("srt1>str2\n"); } return 0; }
strlen模擬實(shí)現(xiàn)
法一
使用計(jì)數(shù)器
size_t my_strlen1(const char* str) { assert(str); int count = 0; while (*str++) { count++; } return count; }
法二
指針相減
size_t my_strlen2(const char* str) { assert(str); char* start = str; while (*str!='\0')//注意這種寫法不能寫*str++;這里要先判斷再++;*str++的寫法在'\0'的地方也+1了 { str++; } return str - start;//\0與起始位置的差就是字符數(shù) }
法三
遞歸,不適用臨時(shí)變量
size_t my_strlen3(const char* str) { if ('\0' == *str) return 0; else return 1 + my_strlen3(str + 1); }
strcpy
此函數(shù)接收兩個(gè)char*類型參數(shù),把后一個(gè)字符串拷貝到前一個(gè)字符串中,包括\0,注意前一個(gè)指針指向的數(shù)組空間要足夠大,被拷貝的內(nèi)容必須包含\0
strcpy的模擬實(shí)現(xiàn)
char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello underworld";//注意寫成數(shù)組 char arr2[20] = "hello world"; printf("%s\n", arr2); printf("%s\n", my_strcpy(arr2, arr1)); return 0; }
strcat
此函數(shù)接收兩個(gè)char*參數(shù),在前一個(gè)字符串\0的位置開始拷貝后一個(gè)字符串,直到后一個(gè)字符串的\0,返回前一個(gè)字符串首地址。注意要保證前一個(gè)指針指向的空間足夠大
strcat的模擬實(shí)現(xiàn)
char* my_strcat(char* dest, const char* src) { char* ret = dest; assert(dest && src); while (*dest)//讓dest到達(dá)str1的\0位置 { dest++; } while (*dest++ = *src++)//這一部分和strcpy同 { ; } return ret; } int main() { char arr1[20] = "hello "; char arr2[20] = "underworld"; printf("%s\n", my_strcat(arr1, arr2)); return 0; }
strcmp
接收兩個(gè)char*參數(shù),依次比較每個(gè)字符,在第一個(gè)不相等的字符處比較他們的編碼值,前者大則返回一個(gè)大于0的數(shù),前者小則返回一個(gè)小于0的數(shù),字符串完全相等則返回0
strcmp模擬實(shí)現(xiàn)
int my_strcmp(const char* str1, const char* str2) { while (*str1 == *str2) { if (*str1 == '\0')//說明兩個(gè)字符串同時(shí)到達(dá)結(jié)束標(biāo)記 return 0; str1++; str2++; } return *str1 - *str2;//如果不是在循環(huán)內(nèi)部返回,就一定不相等,而字符相減可以反映大小 } int main() { char *str1 = "hello world"; char *str2 = "hello underworld"; printf("%d\n", my_strcmp(str1, str2));//w比u大 return 0; }
strstr
接收兩個(gè)char*參數(shù),返回第二個(gè)字符串在第一個(gè)字符串第一次出現(xiàn)的首位置指針
strstr模擬實(shí)現(xiàn)
char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); char* s1;//s1維護(hù)str1 char* s2;//s2維護(hù)str2 char* cp = str1;//cp用來記錄比較開始的位置 if (*str2 == '\0')//特殊情況 return str1; while (*cp) { s1 = cp; s2 = str2; while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)//其實(shí)*s1='\0'且*s2!='\0'時(shí)已經(jīng)沒有比較下去的必要了 //*s1==*s2就讓兩個(gè)維護(hù)指針分別+1;不等就讓cp+1,s1和s2分別重置 { s1++; s2++; } if (*s2 == '\0')//*s2=='\0'說明找到了 { return cp; } cp++; } return NULL; }
應(yīng)用KMP算法的strstr
void get_next(char* str, int* next) { int i, k; i = 0; k = -1; next[0] = -1;//這個(gè)值沒用;或者說是為了使i增加而j不增加 int len = strlen(str); while (i < len - 1)//next數(shù)組最大下標(biāo)是字符串長度減1,數(shù)組長度和字符串長度相同 { if (k == -1 || *(str + i) == *(str + k)) { ++i; ++k; next[i] = k; } else k = next[k]; } //測試打印next int z; printf("next:"); for (z = 0; z < len; z++) { printf("%d ", next[z]); } printf("\n"); } int Index_KMP(char* str1, char* str2, int pos) { int i = pos; int j = 0; int next[255]; get_next(str2, next); int len1 = strlen(str1); int len2 = strlen(str2); int count = 0; while (i < len1 && j < len2)//i從0到10(len1=11),共11次,但是考慮到走else的回溯,單字符查找一共循環(huán)22次 { count++; if (j == -1 || *(str1 + i) == *(str2 + j))//先判斷,再把下標(biāo)加1 { ++i; ++j; } else { j = next[j]; } } printf("i=%d\n", i); printf("j=%d\n", j); printf("count=%d\n", count);//是存在回溯的,那么這個(gè)函數(shù)的時(shí)間復(fù)雜度還是O(m)嗎? //if ((len2!=1) && (j >=(len2-1)))//有缺陷,對于len2=1的情況無法處理 if (j >= (len2 - 1))//單字符查找情形,while結(jié)束時(shí)j=0,而len2-1也=0,故不能作為找到了的標(biāo)志 //對于單字符查找以外的情形,len2-1一定大于0,len2-1代表的就是目標(biāo)串最后一個(gè)字符的下標(biāo),既然j //到達(dá)了這個(gè)位置,就說明完全匹配了 return i - len2;//由于字符串長度與數(shù)組下標(biāo)的差異造成len2=1時(shí) else return 0; } int main() { char* str1 = "hello underworld!"; char* str2 = "under"; printf("%s\n", my_strstr(str1, str2)); printf("%s\n", *(str1+Index_KMP(str1, str2, 0))); return 0; }
strncpy
比strcpy多一個(gè)參數(shù),描述拷貝的字節(jié)數(shù),如果多于str2的長度,則會補(bǔ)0
int main() { char arr1[20] = "abcdefghi"; char arr2[] = "xxxx"; //strncpy(arr1, arr2, 6);//從arr2拷貝6個(gè)字符給arr1?如果arr2長度不夠,則補(bǔ)0 //strncpy(arr1, arr2, 3);//長度不夠不拷貝\0 //strncpy(arr1, arr2, 4); strncpy(arr1, arr2, 5); printf("%s\n", arr1); return 0; }
strncat
比strcat多一個(gè)參數(shù),最多只拷貝完整的str2(包括\0)
int main() { char arr1[20] = "abc\0xxxxxxx"; char arr2[] = "def"; //strncat(arr1, arr2, 6);//在arr1后面接上arr2的六個(gè)字符?最多只接arr2這么長的字符串,包括\0 //strncat(arr1, arr2, 3);//自己會加上\0 strncat(arr1, arr2, 2); printf("%s\n", arr1); return 0; }
strncmp
比strcmp多一個(gè)參數(shù),描述比較的字節(jié)數(shù)
int main() { char arr1[] = "abcdew"; char arr2[] = "abcdeqj"; printf("%d\n",strncmp(arr1, arr2, 5)); printf("%d\n",strncmp(arr1, arr2, 6)); return 0; }
strtok
字符串分割函數(shù),接收兩個(gè)char*參數(shù),第一個(gè)是要被分割的字符串,第二個(gè)是分割符,分割符順序不重要;第一個(gè)參數(shù)不為NULL時(shí),返回分割的第一段;第一個(gè)參數(shù)為NULL,將從上個(gè)位置開始查找下一段
int main() { char arr1[] = "cjh@scu.edu"; char arr2[100] = { 0 };//保存臨時(shí)數(shù)據(jù) char sep[] = "@."; char* ret = NULL;//接收strtok的返回值 strcpy(arr2, arr1); for (ret = strtok(arr2, sep); ret != NULL; ret = strtok(NULL, sep)) { printf("%s\n", ret); } return 0; }
int main() { char str[] = "- This, a sample string."; char* pch; printf("Splitting string \"%s\" into tokens:\n", str); pch = strtok(str, ", .-");//分隔標(biāo)記的位置不重要 while (pch != NULL) { printf("%s\n", pch); pch = strtok(NULL, " ,.-");//注意這里有空格 } return 0; }
memcpy
接收三個(gè)參數(shù),第一個(gè)是char的目標(biāo)位置,第二個(gè)是被拷貝的char的數(shù)據(jù)源,最后一個(gè)是size_t的拷貝字節(jié)數(shù)。注意標(biāo)準(zhǔn)未定義把自己的內(nèi)容拷貝被自己的結(jié)果。
memcpy模擬實(shí)現(xiàn)
void* my_memcpy(void* dest, void* src, size_t count) { void* ret = dest; assert(dest && src); while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } //printf("%d\n", count);//count=-1 return ret; } int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[20] = { 0 }; my_memcpy(arr2, arr1, 10 * sizeof(int)); int i; for (i = 0; i < 20; i++) { printf("%d ", arr2[i]); } return 0;
memmove
此函數(shù)原型和memcpy一樣,包含額memcpy的功能,且可以處理把自己的內(nèi)容拷貝給自己的情景
#include <stdio.h> #include <string.h> int main() { char str[] = "memmove can be very useful......"; printf("%c\n", *(str + 15)); printf("%c\n", *(str + 20)); memmove(str + 20, str + 15, 11);//注意memmove和memcpy不會遇到\0停下來,什么時(shí)候停取決于第三個(gè)參數(shù) puts(str); return 0; }
memmove模擬實(shí)現(xiàn)
void* my_memmove(void* dest, void* src, size_t count)//關(guān)鍵在于拷貝之前先判斷是否會出現(xiàn)overlap { void* ret = dest; if (dest <= src || (char*)dest >= ((char*)src + count)) { while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { dest = (char*)dest + count - 1; src = (char*)src + count - 1; while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest - 1; src = (char*)src - 1; } } return ret; } int main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr + 2, arr, 4 * sizeof(int));//1 2 1 2 3 4 7 8 9 10 //my_memcpy(arr + 2, arr, 4 * sizeof(int));//1 2 1 2 1 2 7 8 9 10 int i; for (i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
memcmp
接收三個(gè)參數(shù),前兩個(gè)是void*型,指向被比較的兩塊內(nèi)容,最后一個(gè)size_t的參數(shù)表示要比較多少字節(jié)
#include <stdio.h> #include <string.h> int main() { char buffer1[] = "DWgaOtP12df0"; char buffer2[] = "DWGAOTP12DF0"; int n; n = memcmp(buffer1, buffer2, sizeof(buffer1)); if (n > 0) printf("'%s' is greater than '%s'.\n", buffer1, buffer2); else if (n < 0) printf("'%s' is less than '%s'.\n", buffer1, buffer2); else printf("'%s' is the same as '%s'.\n", buffer1, buffer2); return 0; }
字符分類函數(shù)
函數(shù) 如果他的參數(shù)符合下列條件就返回真
- iscntrl 任何控制字符
- isspace 空白字符:空格‘ ',換頁‘\f',換行'\n',回車‘\r',制表符'\t'或者垂直制表符'\v'
- isdigit 十進(jìn)制數(shù)字 0~9
- isxdigit 十六進(jìn)制數(shù)字,包括所有十進(jìn)制數(shù)字,小寫字母af,大寫字母AF
- islower 小寫字母a~z
- isupper 大寫字母A~Z
- isalpha 字母az或AZ
- isalnum 字母或者數(shù)字,az,AZ,0~9
- ispunct 標(biāo)點(diǎn)符號,任何不屬于數(shù)字或者字母的圖形字符(可打?。?/li>
- isgraph 任何圖形字符
- isprint 任何可打印字符,包括圖形字符和空白字符
字符串換函數(shù)
- tolower()
- toupper()
#include <stdio.h> int main () { int i=0; char str[]="Test String.\n"; char c; while (str[i]) { c=str[i]; if (isupper(c)) c=tolower(c); putchar (c); i++; } return 0; }
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
- c語言重要的字符串與內(nèi)存函數(shù)
- C語言內(nèi)存函數(shù)的使用及其模擬實(shí)現(xiàn)
- C語言詳解如何應(yīng)用模擬字符串和內(nèi)存函數(shù)
- C語言超詳細(xì)講解字符串函數(shù)和內(nèi)存函數(shù)
- C語言深入詳解四大內(nèi)存函數(shù)的使用
- C語言字符串函數(shù),字符函數(shù),內(nèi)存函數(shù)使用及模擬實(shí)現(xiàn)
- 深入了解C語言中的字符串和內(nèi)存函數(shù)
- 深度解析三個(gè)常見的C語言內(nèi)存函數(shù)
- C語言內(nèi)存函數(shù)的實(shí)現(xiàn)示例
- C語言內(nèi)存函數(shù)的具體使用
- C語言實(shí)現(xiàn)內(nèi)存函數(shù)的示例代碼
相關(guān)文章
Visual?Studio?2022?安裝低版本?.Net?Framework的圖文教程
這篇文章主要介紹了Visual?Studio?2022?如何安裝低版本的?.Net?Framework,首先打開?Visual?Studio?Installer?可以看到vs2022?只支持安裝4.6及以上的版本,那么該如何安裝4.6以下的版本,下面將詳細(xì)介紹,需要的朋友可以參考下2022-09-09C++11 std::function和std::bind 的使用示例詳解
C++11中的std::function和std::bind是函數(shù)對象的重要組成部分,它們可以用于將函數(shù)和參數(shù)綁定在一起,形成一個(gè)可調(diào)用的對象,這篇文章主要介紹了C++11 std::function和std::bind 的使用示例詳解,需要的朋友可以參考下2023-03-03C++標(biāo)準(zhǔn)C函數(shù)在各平臺編譯結(jié)果都相同
今天小編就為大家分享一篇關(guān)于C++標(biāo)準(zhǔn)C函數(shù)在各平臺編譯結(jié)果都相同,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12C語言實(shí)現(xiàn)食堂就餐管理系統(tǒng)(帶鏈表)
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)食堂就餐管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-11-11C++實(shí)現(xiàn)LeetCode(71.簡化路徑)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(71.簡化路徑),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07用C語言winform編寫滲透測試工具實(shí)現(xiàn)SQL注入功能
本篇文章主要介紹使用C#winform編寫滲透測試工具,實(shí)現(xiàn)SQL注入的功能。使用python編寫SQL注入腳本,基于get顯錯(cuò)注入的方式進(jìn)行數(shù)據(jù)庫的識別、獲取表名、獲取字段名,最終獲取用戶名和密碼;使用C#winform編寫windows客戶端軟件調(diào)用.py腳本,實(shí)現(xiàn)用戶名和密碼的獲取2021-08-08用C語言來實(shí)現(xiàn)一個(gè)簡單的虛擬機(jī)
這篇文章主要介紹了用C語言來實(shí)現(xiàn)一個(gè)簡單的虛擬機(jī),其中棧數(shù)組的部分非常值得學(xué)習(xí),需要的朋友可以參考下2015-07-07