3、字符串復制strcpy,strncpy,wcscpy,wcsncpy:將字符串src(或其前n個字符)復制到dest中,覆蓋dest的內容。實現中先檢查指針是否越界,計算指針dest到src的偏移,然后開始做復制操作,復制到dest的開始位置處,以覆蓋dest的內容。對strncpy,也采用了每4個字符作為一組來進行復制的方法,以加快復制速度。
-
- #include <stddef.h> /* 用到了ptrdiff_t */
- #include <string.h>
- #include <memcopy.h>
- #include <bp-checks.h> /* 定義了CHECK_BOUNDS_LOW和CHECK_BOUNDS_HIGH */
- #undef strcpy
-
- char *
- strcpy (dest, src)
- char *dest;
- const char *src;
- {
- reg_char c;
-
- char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
-
- const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
- size_t n;
- do
- {
- c = *s++;
- s[off] = c;
- }
- while (c != '/0');
- n = s - src;
- (void) CHECK_BOUNDS_HIGH (src + n);
- (void) CHECK_BOUNDS_HIGH (dest + n);
- return dest;
- }
- libc_hidden_builtin_def (strcpy)
-
- #include <string.h>
- #include <memcopy.h>
- #undef strncpy
-
-
- char *
- strncpy (s1, s2, n)
- char *s1;
- const char *s2;
- size_t n;
- {
- reg_char c;
- char *s = s1;
- --s1;
- if (n >= 4)
- {
- size_t n4 = n >> 2;
- for (;;)
- {
- c = *s2++;
- *++s1 = c;
- if (c == '/0')
- break;
- c = *s2++;
- *++s1 = c;
- if (c == '/0')
- break;
- c = *s2++;
- *++s1 = c;
- if (c == '/0')
- break;
- c = *s2++;
- *++s1 = c;
- if (c == '/0')
- break;
- if (--n4 == 0)
- goto last_chars;
- }
- n = n - (s1 - s) - 1;
- if (n == 0)
- return s;
- goto zero_fill;
- }
- last_chars:
- n &= 3;
- if (n == 0)
- return s;
- do
- {
- c = *s2++;
- *++s1 = c;
- if (--n == 0)
- return s;
- }
- while (c != '/0');
- zero_fill:
- do
- *++s1 = '/0';
- while (--n > 0);
- return s;
- }
- libc_hidden_builtin_def (strncpy)
4、字符串求長strlen,wcslen:返回str中終止符'/0'之前的字符個數。這里通過把指針移到終止符處,然后計算該指針與開始處指針的差值來獲取字符串的長度。為了加快移動速度,實現中把const char*型指針char_ptr轉換成了unsigned long*型指針longword_ptr,這樣一次就可以移動4個字節。算法關鍵是要辨別出longword_ptr指向的值(有4個字節)中有一個字節為0(它表示字符'/0'),這說明指針到達了終止符'/0'處,要停止移動,并轉換回const char*型指針,計算指針之間的差值。
-
- #include <string.h>
- #include <stdlib.h> /* 用到abort()函數 */
- #undef strlen
-
- size_t
- strlen (str)
- const char *str;
- {
- const char *char_ptr;
- const unsigned long int *longword_ptr;
- unsigned long int longword, magic_bits, himagic, lomagic;
-
-
- for (char_ptr = str; ((unsigned long int) char_ptr
- & (sizeof (longword) - 1)) != 0;
- ++char_ptr)
- if (*char_ptr == '/0')
- return char_ptr - str;
-
-
- longword_ptr = (unsigned long int *) char_ptr;
-
-
-
-
- magic_bits = 0x7efefeffL;
- himagic = 0x80808080L;
- lomagic = 0x01010101L;
- if (sizeof (longword) > 4)
- {
-
-
-
- magic_bits = ((0x7efefefeL << 16) << 16) | 0xfefefeffL;
- himagic = ((himagic << 16) << 16) | himagic;
- lomagic = ((lomagic << 16) << 16) | lomagic;
- }
- if (sizeof (longword) > 8)
- abort ();
-
-
- for (;;)
- {
- longword = *longword_ptr++;
- if (
- #if 0
-
- (((longword + magic_bits)
-
- ^ ~longword)
-
-
- & ~magic_bits)
- #else
- ((longword - lomagic) & himagic)
- #endif
- != 0)
- {
-
- const char *cp = (const char *) (longword_ptr - 1);
- if (cp[0] == 0)
- return cp - str;
- if (cp[1] == 0)
- return cp - str + 1;
- if (cp[2] == 0)
- return cp - str + 2;
- if (cp[3] == 0)
- return cp - str + 3;
- if (sizeof (longword) > 4)
- {
- if (cp[4] == 0)
- return cp - str + 4;
- if (cp[5] == 0)
- return cp - str + 5;
- if (cp[6] == 0)
- return cp - str + 6;
- if (cp[7] == 0)
- return cp - str + 7;
- }
- }
- }
- }
- libc_hidden_builtin_def (strlen)
解釋:
(1)先移動char_ptr,使其值對齊到長整型字的邊界,即移動到使char_ptr中的值是4的倍數為止。在對齊過程中若到達了終止符處,則直接返回與開始處的指針str的差值。
(2)對longword_ptr指針進行移動時,指針指向的值為longword。為了判斷指針是否到達終止符,算法實現使用了兩個魔數lomagic和himagic,lomagic各個字節的最低位為1,其余位均為0;himagic各個字節的最高位為1,其余位均為0??幢磉_式(longword - lomagic) & himagic,若longword中有一個字節為00000000,則減00000001時要向高字節借一位,得到11111111或11111110(當借了一位給更低的字節時),與10000000做“與”運算后變成10000000,這時表達式的結果必定不為0??梢?,只有在表達式結果不等于0時,longword中才有可能有終止符。因此,在移動過程中,一旦表達式結果不等于0,只要逐一檢查一下每個字節,看哪個為0,這時就到達終止符,計算指針差值并返回。若都不為0,則繼續移動。
(3)也可以只用一個魔數magic_bits來實現,即代碼中用#if 0注釋掉的那部分,它可以達到同樣的效果??幢磉_式((longword + magic_bits) ^ ~longword) & ~magic_bits,最后的“與”運算會使結果的其他位清零,只留下那4個洞位,因此我們只要看longword的洞位的變化即可。若longword中有一個字節為0,則做加法后它不可能向高字節(即左側字節)進位,左側字節的洞位沒有改變(因為magic_bits的對應洞位為0,加上0而又沒有低字節的進位,因此不會改變)。做異或運算后,必定使這個洞位變成1,因此表達式的結果必定不為0??梢?,這跟(2)用兩個魔數實現的效果是一樣的。
5、字符搜索strchr,strrchr,wcschr,wcsrchr:在字符串s中查找字符c的第一次(或最后一次)出現,若沒找到則返回NULL指針。算法實現與strlen類似,只不過在strlen是搜索到終止符'/0'為止,這里是搜索到字符c為止。
-
- #include <string.h>
- #undef strrchr
-
- char *
- strrchr (const char *s, int c)
- {
- register const char *found, *p;
- c = (unsigned char) c;
-
- if (c == '/0')
- return strchr (s, '/0');
- found = NULL;
- while ((p = strchr (s, c)) != NULL)
- {
- found = p;
- s = p + 1;
- }
- return (char *) found;
- }
- #ifdef weak_alias
- #undef rindex
- weak_alias (strrchr, rindex)
- #endif
- libc_hidden_builtin_def (strrchr)
解釋:
(1)算法中,有可能搜索到字符c,也有可能搜索到終止符(當字符串中沒有c時)。對于搜索到終止符,與strlen中一樣,對于搜索到字符c,要判斷longword中是否有一個字節為c,看表達式(((longword ^ charmask) + magic_bits) ^ ~(longword ^ charmask)) & ~magic_bits,長整型字charmask的每個字節都是字符c。strlen的對應表達式中的longword換成了這里的longword ^ charmask,而這里的longword中有一個字節為c,恰好等價于longword ^ charmask中有一個字節為0,因此具體的分析過程是一樣的。
(2)strrchr的實現直接使用strchr。用strchr不停地向前搜索,直到搜索到最后一個c為止。
6、子串的無順序匹配strspn,strcspn,strpbrk,wcsspn,wcscspn,wcspbrk:strspn和strcspn在s的開頭查找一個最長子串,使其所有字符都在accept中(或都不在reject中),返回這個子串的長度。strspn的實現中直接對s中開頭的每個字符搜索accept,看其是否在accept中。strcspn的實現則使用了strchr來查找字符。strpbrk在s中搜索第一個出現在accept中的字符,返回其指針。
-
- #include <string.h>
- #undef strspn
-
- size_t
- strspn (s, accept)
- const char *s;
- const char *accept;
- {
- const char *p;
- const char *a;
- size_t count = 0;
- for (p = s; *p != '/0'; ++p)
- {
- for (a = accept; *a != '/0'; ++a)
- if (*p == *a)
- break;
- if (*a == '/0')
- return count;
- else
- ++count;
- }
- return count;
- }
- libc_hidden_builtin_def (strspn)
-
- #if HAVE_CONFIG_H
- # include <config.h>
- #endif
- #if defined _LIBC || HAVE_STRING_H
- # include <string.h>
- #else
- # include <strings.h>
- # ifndef strchr
- # define strchr index
- # endif
- #endif
- #undef strcspn
-
- size_t
- strcspn (s, reject)
- const char *s;
- const char *reject;
- {
- size_t count = 0;
- while (*s != '/0')
- if (strchr (reject, *s++) == NULL)
- ++count;
- else
- return count;
- return count;
- }
- libc_hidden_builtin_def (strcspn)
-
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #if defined _LIBC || defined HAVE_CONFIG_H
- # include <string.h>
- #endif
- #undef strpbrk
-
- char *
- strpbrk (s, accept)
- const char *s;
- const char *accept;
- {
- while (*s != '/0')
- {
- const char *a = accept;
- while (*a != '/0')
- if (*a++ == *s)
- return (char *) s;
- ++s;
- }
- return NULL;
- }
- libc_hidden_builtin_def (strpbrk)
7、模式匹配及字符串解析strstr,strtok,wcsstr,wcstok:strstr(src,sub)在src中搜索子串sub,返回其第一次出現的位置。strtok(str,set)用set中的字符作為分隔符把str分解為多個標號。
strstr的實現用了最新的二路模式匹配算法,可以達到最好的效率。由于算法比較復雜,涉及到很多內部函數,這里就不解剖了,我們平時一般使用KMP算法來進行模式匹配,這個效率也已經非常不錯了。strtok實現如下:
-
- #include <string.h>
- static char *olds;
- #undef strtok
-
-
-
-
-
-
-
-
- char *
- strtok (s, delim)
- char *s;
- const char *delim;
- {
- char *token;
- if (s == NULL)
- s = olds;
-
- s += strspn (s, delim);
- if (*s == '/0')
- {
- olds = s;
- return NULL;
- }
-
- token = s;
- s = strpbrk (token, delim);
- if (s == NULL)
-
- olds = __rawmemchr (token, '/0');
- else
- {
-
- *s = '/0';
- olds = s + 1;
- }
- return token;
- }
算法先用strspn函數使s路過分隔符,移到當前記號的開始處;接著讓token指向當前記號開始處,s移到下一個分隔符處(通過strpbrk函數);然后把這個分隔符替換成終止符'/0',以解析出當前記號,olds指向下一記號的開始處。下一次解析時,若指定s為NULL,則從olds處開始新的解析。