字串函式詳解及模擬實作
- 前言
- 求字串長度
- strlen
- 介紹
- 作用
- 注意事項
- 模擬實作
- 長度不受限制的字串函式
- strcpy
- 介紹
- 作用
- 注意事項
- 模擬實作
- strcat
- 介紹
- 作用
- 注意事項
- 模擬實作
- strcmp
- 介紹
- 作用
- 模擬實作
- 長度受限制的字串函式
- 說明
- strncpy
- 介紹
- 注意事項
- 模擬實作
- strncat
- 介紹
- 注意事項
- 模擬實作
- strncmp
- 介紹
- 模擬實作
- 字串查找函式
- strstr
- 介紹
- 模擬實作
- strtok
- 介紹
- 錯誤資訊報告
- strerror
- 介紹
- 記憶體操作函式
- memcpy
- 介紹
- 模擬實作
- memmove
- 介紹
- 模擬實作
- memset
- 介紹
- 模擬實作
- memcmp
- 介紹
- 模擬實作
前言
C語言中有許多庫函式,有我們常見的IO函式(printf,scanf),時間函式(time),數學函式(fabs),字串函式等等,
C語言中對字符和字串的處理很是頻繁,但是C語言本身沒有字串型別,字串通常放在常量字串中或者字符陣列中,
字串常量適用于那些對它不做修改的字串函式
這篇文章會為你介紹字串函式,來幫助你更好的理解和學習字串函式,
求字串長度
strlen
介紹
strlen的引數:size_t strlen( const char *string );
strlen函式位于<string.h>頭檔案下,其回傳值是求得字串的長度,
PS:長度一定是正數,size_t是通過宏定義實作的,其型別是unsigned int無符號整型,本次說明之后本文將不會再介紹

作用
計算字串長度

strlen對于接受到的地址以1個位元組為單位向前計算,當遇到終止符’\0’時停止計算,
注意事項
如果輸入的字串沒有確切的大小,比如未初始化等,那么回傳的會是隨機值,因為只有遇到’\0’,函式才會停止,如圖:

并且,由于strlen回傳值是無符號整數,其相減結果也一定會是正整數,所以不能隨意的相減,乘負數也不可以,如圖:

當然,可以通過強制型別轉換來得到想要的結果

模擬實作
知道了原理,很容易實作strlen
有三種實作方法,分別是
- 計數
- 指標減去指標(指標減去指標為兩個地址之間的元素個數)
- 遞回
代碼如下
size_t my_strlen1(const char* str) {//計數法,遇到不是\0的數便+1
char *s = (char*)str;
size_t count = 0;
while (*s++ != '\0')
count++;
return count;
}
size_t my_strlen2(const char* str) {//地址減去地址是兩個地址之間的元素個數
char *s = (char*)str;
while (*s++ != '\0');
return (size_t)(s - str)-1;//這里-1是因為此時s在\0處
}
size_t my_strlen3(const char* str) {//遞回方法
if (*str == '\0')
return 0;
else
return 1 + my_strlen3(str + 1);
}
執行結果如下:

長度不受限制的字串函式
strcpy
介紹
strcpy的引數:char *strcpy( char *str1, const char *str2 );
strcpy函式位于<string.h>頭檔案下,其回傳值是char*型別指標,
作用
將字串str2的內容復制到字串str1里,
注意事項
-
strcpy會將\0也放進去

-
strcpy不會在意空間大小,將8位元組的字串放進4位元組大小中,代碼仍然能夠執行且會強制放進去,結果會報錯,因為“放不下”;所以目標空間必須足夠大

-
(字串必須以\0結束)字串str2應該有具體大小,類似strcpy,否則會將隨機值放入str1中并報錯:因為和strcpy一樣,只會str2遇到了\0才會停止復制

模擬實作
代碼:
char* my_strcpy(char* s1, const char*s2) {
assert(s1&&s2);//斷言函式,判斷空指標
char* ret = s1;
while (*s1++ = *s2++)
;
return ret;
}
結果:

strcat
介紹
strcat的引數:char *strcat( char *str1, const char *str2 );
strcat函式位于<string.h>頭檔案下,其回傳值是char*型別指標,
作用
將字串str2追加到str1后面,并回傳str1的指標,

注意事項
- str1要預留一定空間給str2的位置,否則會越界,

- str2 需要有固定的大小,否則會將隨機值錄入,且會有越界的風險,
程式崩潰

解決方案:放入\0即可

模擬實作
代碼:
char* my_strcat(char* str1, const char* str2) {
assert(str1 && str2);//斷言
char* ret = str1;//保留str1地址
while (*str1)
str1++;
//找到了str1的\0的位置
while (*str1++ = *str2++)
;
//追加
return ret;
}
結果:

strcmp
介紹
strcmp的引數:int strcmp( const char *str1, const char *str2 );
strcmp函式位于<string.h>頭檔案下,
作用
比較字串大小內容(不是長度)(參考字典序)(ASCII值),若str1>str2,回傳一個大于0的數,若str1=str2,回傳0,若str1《str2,回傳小于0的數,
通常會回傳1,0,-1,

模擬實作
代碼:
int my_strcmp (const char* str1,const char* str2) {
assert(str1 && str2);
while (*str1 && *str2){//遍歷
if (*str1 > *str2)//大于回傳1
return 1;
else if (*str1 < *str2)//小于回傳-1
return -1;
*str1++;//等于則繼續
*str2++;
}
return 0;//遍歷結束,相等,回傳0
}
結果:

長度受限制的字串函式
說明
因為strcpy,strcat,ctrcmp長度不受限制,在上面的三個函式的注意事項里面也已經提到了,所以這些函式并不安全,會出現一些問題,比如越界訪問,
所以,為了更好的使用函式,C語言又規定了一些長度受限制的相對安全的函式使用,接下來將會介紹這些函式,
注意,并不是絕對安全
strncpy
介紹
strncpy的引數:
char *strncpy( char *str1, const char *str2, size_t count );
和strcpy一樣,位于<string.h>頭檔案下,
相對于strcpy,多了一個count引數,其作用是規定復制count個位元組,用于解決strcpy不看大小直接復制容易出錯的問題

注意事項
- 若count大于str2的長度,即要復制進去的內容不夠,那么會補’\0’
當然,如果str2并未規定大小,那么這種情況下不會錄入\0,而是會存入隨機值,


模擬實作
代碼:
char * my_strncpy(char * str1, const char *str2, size_t n)
{
assert(str1 && str2);
char *ret = str1;
for (size_t i = 0; i < n; i++) {
if (n>strlen(str1)){
*(str1 + i) = '\0';//對超出部分進行處理
}
else{
*(str1 + i) = *str2++;
}
}
return ret;
}
結果:

strncat
介紹
strncat的引數:
char *strncat( char *str1, const char *str2, size_t count );
位于<string.h>頭檔案下,
和strncpy相對于strcpy一樣,strncat多了一個count引數,其作用是規定追加count個位元組,
注意事項
- strncat并不是真的追加count個位元組,而是會幫你多放了個’\0’進去,用來結束字串,

- strncat最多只會追加count+1個字符,如果count大于要追加的字串長度,則無事發生

模擬實作
代碼:
char* my_strncat(char* str1, const char* str2,size_t count) {
assert(str1 && str2);//斷言
char* ret = str1;//保留str1地址
while (*str1)
str1++;
//找到了str1的\0的位置
while(count--){
if (*str2 == '\0'){//處理count過大的情況
*str1 = '\0';
break;
}
else{
*str1++ = *str2++;
}
}
//追加
return ret;
}
結果:

strncmp
介紹
strncmp的引數:
char *strncat( const char *str1, const char *str2, size_t count );
位于<string.h>頭檔案下,
多了一個count引數,其作用是比較count個位元組,

模擬實作
代碼:
int my_strncmp(const char* str1, const char* str2,size_t count) {
assert(str1 && str2);
while (count--){//遍歷
if (*str1 > *str2)//大于回傳1
return 1;
else if (*str1 < *str2)//小于回傳-1
return -1;
*str1++;//等于則繼續
*str2++;
}
return 0;//遍歷結束,相等,回傳0
}
結果:

字串查找函式
顧名思義,用于查找字串的函式
strstr
介紹
引數:char *strstr( const char *str1, const char *str2 );
在str1中查找str2是否存在,回傳str1中str2第一次出現的位置的起始地址,否則回傳空指標,

模擬實作
代碼:
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char* start = (char*)str1;//在這里需要強制型別轉換成char*
char* find = (char*)str2;//尋找的字串
char* p = (char*)str1;//cp就是用來保存首地址的
while (*p)
{
start = p;//吧p賦給start,用start來尋找,防止找不到已經找到str2的起始點
while (*start != '\0' && *find != '\0' && *start == *find)
{
//回圈,尋找字串
start++;
find++;
}
if (*find == '\0')//find遇到了\0,意味著找到了
{
return p;//回傳此時的p
}
find = (char*)str2;//沒找到,但是find可能已經改變過,所以重新賦值
p++;//p++可以得到原起始位置的下一個位置
}
return NULL;
}
結果:

strtok
介紹
strtok的引數:char *strtok( char *str1, const char *str2 );
在str1中尋找和str2其中相等的字符然后置為\0
- 第一個引數str1指定一個字串,它包含了0個或者多個由str2字串中一個或者多個分隔符分割的標記,
- strtok函式找到str1中的下一個標記,井將其用\0結尾,回傳一個指向這個標記的指標,(注:strtok函式會改變被操作的字串,所以在使用strtok函式切分的字串一般都是臨時拷貝的內容并且可修改,)
- strtok函式的第一個引數不為NULL,函式將找到str1中第一個標記,strtok函式將保存它在字串中的位置,
- strtok函式的第一個引數不為NULL,函式將找到str中第一個標記,strtok函式將保存它在字串中的位置,
- 如果字串中不存在更多的標記,則回傳NULL指標,
- 這是用static靜態變數實作的,

錯誤資訊報告
strerror
介紹
引數:char *strerror( int errnum );
strerror函式從內部陣列中搜索錯誤號 errnum,并回傳一個指向錯誤訊息字串的指標,strerror 生成的錯誤字串取決于開發平臺和編譯器,

記憶體操作函式
memcpy
介紹
引數:void *memcpy( void *str1, const void *str2, size_t count );
- 函式memcpy從str2的位置開始向后復制count個位元組的資料到str1的記憶體位置,
- 這個函式在遇到’\0’的時候并不會停下來,
- 如果str2和str1有任何的重疊,復制的結果都是未定義的,
- memcpy能夠拷貝的資料不會受限制,可以拷貝多種型別的資料

模擬實作
代碼:
void* my_memcpy(void* arr1, const void* arr2, size_t count) {
assert(arr1 && arr2);
void* ret = arr1;
while(count--) {
*(char*)arr1 = *(char*)arr2;
arr1 = (char*)arr1 + 1;
arr2 = (char*)arr2 + 1;
}
return ret;
}
結果:

memmove
介紹
引數:void *memmove( void *str1, const void *str2, size_t count );
其作用和memcpy類似,都是復制拷貝,
但是memmove比memcpy要更強,因為在部分情況下,memcpy無法完成覆寫拷貝,如圖,
在vs下,memcpy可以完成覆寫拷貝,但是在某些編譯器下無法完成,所以在這種情況下盡量使用memmove,

模擬實作
對于資料重復區間的情況進行特殊處理
代碼:
void* my_memmove(void* dest, const void* src, size_t count) {
void *ret = dest;
if (dest < src){//從前向后拷貝
while (count--) {
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else{//從后向前拷貝
while (count--) {
*((char*)dest + count) = *((char*)src+count);
}
}
return ret;
}
結果:

memset
介紹
引數:void *memset( void *dest, int c, size_t count );
該函式可以將dest開始的count個位元組全部置為c

模擬實作
代碼:
void* my_memset(void *arr, const int c, size_t count){
void*ret = arr;
for (size_t i = 0; i < count; i++){
*((char*)arr + i) = c;
}
return ret;
}
結果:

memcmp
介紹
引數:int memcmp(const void *str1, const void *str2, size_t n);
C 庫函式 memcmp 把存盤區 str1 和存盤區 str2 的前 n 個位元組進行比較,
模擬實作
代碼:
void* my_memcpy(void* dest, const void* src, size_t n)
{
assert(dest);
assert(src);
char* pdest = (char*)dest;
const char* psrc = (const char*)src;
while (n--)
{
*pdest++ = *psrc++;
}
return dest;`
🐂🐸完結撒花
彩蛋就是沒有彩蛋

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/301569.html
標籤:其他
上一篇:常見排序之平方排序(時間復雜度)
