這邊文章對c語言的字串處理的常用庫函數總結一下,並進行實現。
1.字串比較
int strcmp(const char *s1, const char *s2);
比較兩個字串的大小(不忽略大小寫),傳回值很有學問:如果s1小於s2返回一個小於0的數,如果s1大於s2返回一個大於0的數,如果相等則返回0。傳回值是兩個字串中第一個不相等的字元ascii碼的差值。實現如下:
int my_strcmp(const char *s1, const char *s2){//important! validate arguments first!assert(NULL !=s1 && NULL != s2);while(*s1 != '\0' && *s2 != '\0' && *s1==*s2){s1++;s2++;}return *s1 - *s2;}
注意再函數開始進行參數檢查,防止輸入參數有NULL時發生執行階段錯誤。
strcmp是最常用的字串比較函數,一般用法是if(!strcmp(s1, s2)){ ...}。如果不是對整個字串進行比較而只是比較指定數目的字串,可以使用函數:
int strncmp(const char *s1, const char *s2, size_t n);
用法和傳回值都和strcmp類似,之比較給定字串的前n個字元,或者到遇到任一字串結尾。實現如下:
int my_strncmp(const char *s1, const char *s2, size_t n){//important! validate arguments first!assert(NULL!=s1 && NULL!=s2);if(n == 0)return 0;size_t cnt = 1;while(*s1 != '\0' && *s2 != '\0' && *s1==*s2 && cnt < n){s1++;s2++;cnt++;}return *s1 - *s2;}
需要注意的除了參數檢查外,還要注意n=0的特殊情況,這裡我們n=0永遠返回0。
還有其他一些帶特殊要求字串比較函數,如:
stricmp,memcmp,memicmp等等,加i表示比較時忽視大小寫,帶mem的是比較一塊記憶體區間。
2.字串尋找
最簡單的是尋找字串尋找字元:
char *strchr(const char *s, int c);
至於參數為什麼是int,曆史遺留問題,這裡不多討論。函數返回在s中找到的第一個c的位置的指標,注意的是,字串末尾的‘\0’也是可以被尋找的。實現如下:
char *my_strchr(const char *s, int n){assert(s != NULL);char c = (char)n;do{if(*s == c)return (char *)s;}while(*s++);return NULL;}
還有尋找字串的函數strstr:
char *strstr(const char *s1, const char *s2);
函數返回s2在s1中出現的首字元的位置,實現如下:
char *my_strstr(const char *s1, const char *s2){assert(NULL!=s1 && NULL!=s2);size_t len = strlen(s2);while(*s1){if(!strncmp(s1,s2,len))return (char *)s1;s1++;}return NULL;}
c標準庫中並沒有定義類似strnchr和strnstr的限定尋找範圍的函數,當然需要的話我們可以自己定義,如:
char *strnstr(const char* s1, const char* s2, size_t n){ const char* p; size_t len = strlen(s2); if (len == 0) { return (char *)s1; } for (p = s1; *p && (p + len<= buffer + n); p++) { if ((*p == *token) && (strncmp(p, token, tokenlen) == 0)) { return (char *)p; } } return NULL;}
3.字串複製
最常見的字串複製函數是strcpy:
char *strcpy(char *dst, const char *src);
把src所指的由NULL結尾的字串複製到由dst所指的字串中,src和dst不可以相同(可以由c99的restrict關鍵字聲明),dst必有足夠的空間存放複製的字串。
還有一點要注意的是函數傳回值,傳回值是指向dst的指標,這樣做的目的是方便程式中語句內聯,比如strlen(strcpy(s,t))。
函數的實現如下:
char *my_strcpy(char *dst, const char *src){assert(NULL!=dst && NULL!=src);char *p = dst;while((*dst++ = *src++) != '\0');return p;}
使用strcpy是危險的,因為函數本身是不檢查dst指向的空間是否足夠儲存需要複製的字串,導致的一個潛在隱患就是字串溢出。這也是上個世紀常被駭客利用的一個經典漏洞。所以,在大多數情況下都是用strncpy無疑更加保險:
char *my_strncpy(char *dst, const char *src, size_t n){assert(NULL!=dst && NULL!=src);char *p = dst;while(n){if((*dst++ = *src++) == '\0')break;n--;}return p;}
需要注意另外一個函數strdup:
char *strdup(const char *);
該函數和strcpy的不同是,函數會自己申請記憶體空間存放拷貝的字串,然後返回指向該字串的指標。所以在使用strdup函數時需要注意的是,在使用完複製的字串後使用free函數釋放其佔用的空間。
另memcpy函數和strncpy類似,只是不會再遇到NULL時終止拷貝,該函數一定會拷貝n個字元。
4.字串串連
字串串連是把一個字串的頭串連到另一個字串的結尾。
char *strcat(char *s1, const char *s2);
函數的實現如下:
char *my_strcat(char *s1, const char *s2){assert(NULL!=s1 && NULL!=s2);char *p =s1;while(*s1)s1++;strcpy(s1,s2);return p;}
同樣,strcat也是不安全的,因為也對緩衝區足夠存放串連的字串進行了假設。所以,多數情況下我們應該使用更安全的:
char *strncat(char *s1, const char *s2, size_t n);