C語言模擬實現(xiàn)字符串庫函數(shù)的示例講解
字符串檢驗
strlen
函數(shù)原型
/// @brief 返回給定空終止字符串的長度,即首元素為 str 所指,且不包含首個空字符的字符數(shù)組中的字符數(shù) /// @param str 指向要檢測的字符串的指針 /// @return 字符串 str 的長度 size_t strlen( const char *str );
空終止字符串即 C 語言中以 ‘\0’ 作為終止符的字符串,strlen 計算字符串的長度并返回,返回的長度不包含 ‘\0’。
char arr[] = "qgw"; // num 的值是 3 int num = strlen(arr);
模擬實現(xiàn)
下面的實現(xiàn)方式比較簡單,采用遍歷字符串的方式,遇到 ‘\0’ 就退出循環(huán),返回結(jié)果。
size_t my_strlen(const char* str) { size_t cnt = 0; while (*str != '\0') { ++str; ++cnt; } return cnt; }
strcmp
函數(shù)原型
/// @brief 以字典序比較二個字符串 /// @param str rhs 要比較的兩字符串 /// @return 相等返回 0 int strcmp(const char* lhs, const char* rhs);
返回值:
- 兩字符串字典序相同,返回 0
- lhs 字典序大于 rhs,返回一個正數(shù)
- lhs 字典序小于 rhs,返回一個負數(shù)
模擬實現(xiàn)
實現(xiàn)的思路是:遍歷兩個字符串,直到出現(xiàn)不同或有字符串結(jié)束。
res 存儲兩字符串比較的結(jié)果,如果等于 0 循環(huán)繼續(xù),不等于 0 直接退出,lhs 指向的字符大于 rhs 指向的字符就為正數(shù),否則反之。
若 lhs 先結(jié)束,rhs 還沒結(jié)束,此時 res 為負數(shù),\0 的 ASCII 碼為 0,是最小的。若 rhs 先結(jié)束,lhs 還沒結(jié)束,此時 res 為正數(shù)。若同時結(jié)束,res 剛好為 0 返回。
int my_strcmp(const char* lhs, const char* rhs) { int res = 0; while ((res = *lhs - *rhs) == 0 && *lhs != '\0' && *rhs != '\0') { ++lhs; ++rhs; } return res; }
strstr
函數(shù)原型
/// @brief 在 str 中查找 substr 子串 /// @param str 指向要檢驗的空終止字符串的指針 /// @param substr 指向要查找的空終止字節(jié)字符串的指針 /// @return 指向于 str 中找到的子串首字符的指針,或若找不到該子串則為空指針 char *strstr( const char* str, const char* substr );
若 substr 指向空,則會返回 str。
模擬實現(xiàn)
下面實現(xiàn)的方法為暴力匹配子串,實際中可以使用 KMP 或 BM 等字符串搜索函數(shù)優(yōu)化這一過程。
char* my_strstr(const char* str, const char* substr) { if (substr == NULL) { return str; } // 遍歷 str 所有字符,看以其起始字符是否匹配 while (*str != '\0') { const char* backStr = str; const char* backSub = substr; while (*backStr != '\0' && *backSub != '\0' && *backStr == *backSub) { ++backStr; ++backSub; } // 如果 substr 走完了,說明匹配成功了,返回此時的 str if (*backSub == '\0') { return str; } ++str; } }
字符串操作
strcpy
函數(shù)原型
/// @brief 復制 src 所指向的空終止字符串,包含空終止符,到首元素為 dest 所指的字符數(shù)組 /// @param dest 指向要寫入的字符數(shù)組的指針 /// @param src 指向要復制的空終止字符串的指針 /// @return 返回 dest 的副本 char *strcpy( char *dest, const char *src );
需要注意的是:
1.若 dest 數(shù)組長度不足則行為未定義
即 dest 數(shù)組不足以包含 src 中所有元素,此時大概率會因越屆訪問崩潰
2.若字符串覆蓋則行為未定義
現(xiàn)在主流編譯器都可以處理有覆蓋的情況,比如:MSVC、GCC
3.若 dest 不是指向字符數(shù)組的指針或 src 不是指向空終止字符串的指針則行為未定義
模擬實現(xiàn)
下面的方法先記錄要返回的地址,最后遍歷 src 遇到 \0,此時退出循環(huán)。
注意:該實現(xiàn)方法并不能解決字符串有覆蓋的情況。
char* my_strcpy(char* dest, const char* src) { char* res = dest; while (*dest = *src) { ++dest; ++src; } return res; }
strcat
函數(shù)原型
/// @brief 后附 src 所指向的空終止字符串的副本到 dest 所指向的空終止字符串的結(jié)尾 /// @param dest 指向要后附到的空終止字符串的指針 /// @param src 指向作為復制來源的空終止字符串的指針 /// @return 返回 dest 的副本 char *strcat( char *dest, const char *src );
需要注意的是:
會用字符 src[0] 替換 dest 末尾的 \0
若目標數(shù)組對于 src 和 dest 的內(nèi)容以及空終止符不夠大,則行為未定義
若字符串重疊,則行為未定義
若 dest 或 src 不是指向空終止字符串的指針,則行為未定義
模擬實現(xiàn)
因為會先用 src 第一個字符替換 dest 結(jié)尾的 \0,所以要先找到 dest 結(jié)尾 \0。然后再遍歷 src,將其添加到 dest 結(jié)尾。
char* my_strcat(char* dest, const char* src) { char res = dest; // 先找 \0 的位置 while (*dest != '\0') { ++dest; } // 追加到 dest 后面 while (*dest = *src) { ++dest; ++src; } return res; }
內(nèi)存操作
memcpy
函數(shù)原型
/// @brief 從 src 所指向的對象復制 count 個字符到 dest 所指向的對象 /// @param dest 指向要復制的對象的指針 /// @param src 指向復制來源對象的指針 /// @param count 復制的字節(jié)數(shù) /// @return 返回 dest 的副本 void* memcpy(void* dest, const void* src, size_t count);
要注意的是:
若訪問發(fā)生在 dest 數(shù)組結(jié)尾后則行為未定義
若對象重疊,則行為未定義
也就是說在標準中 memcpy 也不能處理對象重疊的情況
若 dest 或 src 為非法或空指針則行為未定義
模擬實現(xiàn)
下面給出的實現(xiàn)方式與 strcpy 相似,不過是改用 count 變量來控制循環(huán)次數(shù)。
memcpy 是最快的內(nèi)存到內(nèi)存復制子程序。它通常比必須掃描其所復制數(shù)據(jù)的 strcpy,或必須預防以處理重疊輸入的 memmove更高效。
void* my_memcpy(void* dest, const void* src, size_t count) { void* res = dest; while (count--) { *(char*)dest = *(char*)src; ++(char*)dest; ++(char*)src; } return res; }
memmove
函數(shù)原型
/// @brief 從 src 所指向的對象復制 count 個字節(jié)到 dest 所指向的對象 /// @param dest 指向要復制的對象的指針 /// @param src 指向復制來源對象的指針 /// @param count 復制的字節(jié)數(shù) /// @return 返回 dest 的副本 void* memmove(void* dest, const void* src, size_t count);
memmove 是不是看起來和 memcpy 一模一樣,但 memmove 可以處理內(nèi)存重疊的情況,這也就說明它要做一些檢查,來保證能夠處理內(nèi)存重疊的情況。
1.無重疊
直接調(diào)用更高校的 memcpy
2.有重疊,dest 在 src 之前,正常正向復制
3.有重疊,dest 在 src 之后,需要反向復制
模擬實現(xiàn)
通過對上圖的觀察,我們可以發(fā)現(xiàn):若 dest 在 src 的后面并且存在內(nèi)存重疊,就需要反向復制。
我們可以簡化這一函數(shù),對無重疊的情況不去調(diào)用 memcpy 而是包含在下面兩種情況中。
dest 在 src 之前,采用正向復制
dest 在 src 之后,采用反向復制
void* my_memmove(void* dest, const void* src, size_t count) { void* res = dest; if (dest < src) { while (count--) { *(char*)dest = *(char*)src; ++(char*)dest; ++(char*)src; } } else { while (count--) { *((char*)dest + count) = *((char*)src + count); } } }
到此這篇關(guān)于C語言模擬實現(xiàn)字符串庫函數(shù)的示例講解的文章就介紹到這了,更多相關(guān)C語言字符串庫函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++實現(xiàn)職工工資管理系統(tǒng)課程設(shè)計
這篇文章主要為大家詳細介紹了C++實現(xiàn)職工工資管理系統(tǒng)課程設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03C++11中l(wèi)onglong超長整型和nullptr初始化空指針
本文介紹?C++11?標準中新添加的?long?long?超長整型和?nullptr?初始化空指針,在?C++11?標準下,相比?NULL?和?0,使用?nullptr?初始化空指針可以令我們編寫的程序更加健壯,本文結(jié)合示例代碼給大家詳細講解,需要的朋友跟隨小編一起看看吧2022-12-12C語言中帶頭雙向循環(huán)鏈表基本操作的實現(xiàn)詳解
無頭單向非循環(huán)鏈表結(jié)構(gòu)簡單,一般不會單獨用來存數(shù)據(jù)。而帶頭雙向循環(huán)鏈表的結(jié)構(gòu)較為復雜,一般用在單獨存儲數(shù)據(jù)。本文將介紹帶頭雙向循環(huán)鏈表的基本操作,需要的可以參考一下2022-11-11QT線程池的使用(QThreadPool類和QRunnable類)
本文主要介紹了QT線程池的使用(QThreadPool類和QRunnable類),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04