C标准库源码解剖(4):字符串处理函数string.h和wchar.h

    string.h中包含了所有的字符串处理函数,也包含了内存处理函数,因为这些内存处理函数(如比如、复制、搜索)的功能与字符串处理函数功能类似。我们是用通用指针来指向内存块的,通用指针可以用char*类型(传统C语言),也可以用void*类型(标准C语言)。每个函数都有对应的宽字符版本,在wchar.h中。
    string.h中包含的标准库函数:strcat,strncat,strcmp,strncmp,strcpy,strncpy,strlen,strchr,strrchr,strspn,strcspn,strpbrk, strstr,strok,strcoll,strxfrm,strerror; memcpy,memmove,memcmp,memchr,memeset。GNU还提供了很多非标准的扩展,如memccpy,rawmemchr,memrchr, strdup,strndup等。

/* ISO C99 Standard: 7.21 字符串处理 <string.h> */ #ifndef _STRING_H #define _STRING_H 1 #include <features.h> /* 非标准头文件,定义了一些编译选项 */ __BEGIN_DECLS /* 从<stddef.h>中获得size_t和NULL */ #define __need_size_t #define __need_NULL #include <stddef.h> __BEGIN_NAMESPACE_STD /* 从SRC中复制N个字节的内容到DEST中 */ extern void *memcpy (void *__restrict __dest, __const void *__restrict __src, size_t __n) __THROW __nonnull ((1, 2)); /* 从SRC中复制N个字节的内容到DEST中,保证对重叠字符串(即SRC与DEST共用存储空间)有正确的行为 */ extern void *memmove (void *__dest, __const void *__src, size_t __n) __THROW __nonnull ((1, 2)); __END_NAMESPACE_STD /* 从SRC中复制不超过N个字节的内容到DEST中,当遇到字符C时便停止,返回DEST中C的拷贝后面的字符指针。 如果在SRC的前面N个字节中没有发现字符C,则返回NULL */ #if defined __USE_SVID || defined __USE_BSD || defined __USE_XOPEN extern void *memccpy (void *__restrict __dest, __const void *__restrict __src, int __c, size_t __n) __THROW __nonnull ((1, 2)); #endif /* SVID. */ __BEGIN_NAMESPACE_STD /* 把S的前N个字节的内容设置为C */ extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1)); /* 比较S1和S2的前n字节的内容 */ extern int memcmp (__const void *__s1, __const void *__s2, size_t __n) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 在S的前N个字节中搜索C的第一次出现 */ extern void *memchr (__const void *__s, int __c, size_t __n) __THROW __attribute_pure__ __nonnull ((1)); __END_NAMESPACE_STD #ifdef __USE_GNU /* 在S中搜索C,这与“memchr”类似,但这里没有长度的限制 */ extern void *rawmemchr (__const void *__s, int __c) __THROW __attribute_pure__ __nonnull ((1)); /* 在S的前N个字节中搜索C的最后一次出现 */ extern void *memrchr (__const void *__s, int __c, size_t __n) __THROW __attribute_pure__ __nonnull ((1)); #endif __BEGIN_NAMESPACE_STD /* 将SRC复制到DEST中 */ extern char *strcpy (char *__restrict __dest, __const char *__restrict __src) __THROW __nonnull ((1, 2)); /* 将SRC的前N个字符复制到DEST */ extern char *strncpy (char *__restrict __dest, __const char *__restrict __src, size_t __n) __THROW __nonnull ((1, 2)); /* 将SRC追加到DEST */ extern char *strcat (char *__restrict __dest, __const char *__restrict __src) __THROW __nonnull ((1, 2)); /* 将SRC的前N个字符追加到DEST */ extern char *strncat (char *__restrict __dest, __const char *__restrict __src, size_t __n) __THROW __nonnull ((1, 2)); /* 比较S1和S2 */ extern int strcmp (__const char *__s1, __const char *__s2) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 比较S1和S2的前N个字符 */ extern int strncmp (__const char *__s1, __const char *__s2, size_t __n) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 比较S1和S2对照后的形式(即按特定区域设置来进行字符排序) */ extern int strcoll (__const char *__s1, __const char *__s2) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 对SRC作转换并放到DEST的前N个字节中 */ extern size_t strxfrm (char *__restrict __dest, __const char *__restrict __src, size_t __n) __THROW __nonnull ((2)); __END_NAMESPACE_STD #ifdef __USE_GNU /* 下面的函数与上面的两个等价,但它们带一个额外的区域设置参数,用于设置字符对照规则。 这是非标准的函数,但是在不久之后有可能会被标准化 */ # include <xlocale.h> /* 使用L中的规则来比较S1和S2对照后的形式 */ extern int strcoll_l (__const char *__s1, __const char *__s2, __locale_t __l) __THROW __attribute_pure__ __nonnull ((1, 2, 3)); /* 对SRC作转换并放到DEST的前N个字节中 */ extern size_t strxfrm_l (char *__dest, __const char *__src, size_t __n, __locale_t __l) __THROW __nonnull ((2, 4)); #endif #if defined __USE_SVID || defined __USE_BSD || defined __USE_XOPEN_EXTENDED /* 复制S,返回一个相同的副本 */ extern char *strdup (__const char *__s) __THROW __attribute_malloc__ __nonnull ((1)); #endif /* 返回STRING的前N个字节的副本。即使STRING[N]前面没有出现终止符, 结果字符串也会被终止 */ #if defined __USE_GNU extern char *strndup (__const char *__string, size_t __n) __THROW __attribute_malloc__ __nonnull ((1)); #endif #if defined __USE_GNU && defined __GNUC__ /* 复制S,返回一个相同的副本 */ # define strdupa(s) / (__extension__ / ({ / __const char *__old = (s); / size_t __len = strlen (__old) + 1; / char *__new = (char *) __builtin_alloca (__len); / (char *) memcpy (__new, __old, __len); / })) /* 返回字符串前N个字节的副本 */ # define strndupa(s, n) / (__extension__ / ({ / __const char *__old = (s); / size_t __len = strnlen (__old, (n)); / char *__new = (char *) __builtin_alloca (__len + 1); / __new[__len] = '/0'; / (char *) memcpy (__new, __old, __len); / })) #endif __BEGIN_NAMESPACE_STD /* 在S中搜索C的第一次出现 */ extern char *strchr (__const char *__s, int __c) __THROW __attribute_pure__ __nonnull ((1)); /* 在S中搜索C的最后一次出现 */ extern char *strrchr (__const char *__s, int __c) __THROW __attribute_pure__ __nonnull ((1)); __END_NAMESPACE_STD #ifdef __USE_GNU /* 这个函数与“strchr”类似,但如果在S中没有找到C,则它返回一个指向NUL终止符的指针 */ extern char *strchrnul (__const char *__s, int __c) __THROW __attribute_pure__ __nonnull ((1)); #endif __BEGIN_NAMESPACE_STD /* 返回S中的第一个子串长度,这个子串的所有字符都不在REJECT中 */ extern size_t strcspn (__const char *__s, __const char *__reject) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 返回S中的第一个子串长度,这个子串的所有字符都在ACCEPT中 */ extern size_t strspn (__const char *__s, __const char *__accept) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 返回S中的第一个在ACCEPT中出现的字符指针 */ extern char *strpbrk (__const char *__s, __const char *__accept) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 查找字符串NEEDLE在HAYSTACK中第一次出现 */ extern char *strstr (__const char *__haystack, __const char *__needle) __THROW __attribute_pure__ __nonnull ((1, 2)); /* 用DELIM中的字符作为分隔符把S解析成多个记号 */ extern char *strtok (char *__restrict __s, __const char *__restrict __delim) __THROW __nonnull ((2)); __END_NAMESPACE_STD /* 用DELIM中的字符作为分隔符把S分解为多个记号,上一次调用的信息存储在SAVE_PTR中 */ extern char *__strtok_r (char *__restrict __s, __const char *__restrict __delim, char **__restrict __save_ptr) __THROW __nonnull ((2, 3)); #if defined __USE_POSIX || defined __USE_MISC extern char *strtok_r (char *__restrict __s, __const char *__restrict __delim, char **__restrict __save_ptr) __THROW __nonnull ((2, 3)); #endif #ifdef __USE_GNU /* 与“strstr”类似,但这个函数忽略字符串的大小写 */ extern char *strcasestr (__const char *__haystack, __const char *__needle) __THROW __attribute_pure__ __nonnull ((1, 2)); #endif #ifdef __USE_GNU /* 在HAYSTACK中查找NEEDLE的第一次出现。NEEDLE为NEEDLEN个字节长, HAYSTACK为HAYSTACKLEN个字节长 */ extern void *memmem (__const void *__haystack, size_t __haystacklen, __const void *__needle, size_t __needlelen) __THROW __attribute_pure__ __nonnull ((1, 3)); /* 将SRC的N个字节复制到DEST,返回最后一个写入字节后面位置的指针 */ extern void *__mempcpy (void *__restrict __dest, __const void *__restrict __src, size_t __n) __THROW __nonnull ((1, 2)); extern void *mempcpy (void *__restrict __dest, __const void *__restrict __src, size_t __n) __THROW __nonnull ((1, 2)); #endif __BEGIN_NAMESPACE_STD /* 返回S的长度 */ extern size_t strlen (__const char *__s) __THROW __attribute_pure__ __nonnull ((1)); __END_NAMESPACE_STD #ifdef __USE_GNU /* 查找STRING的长度,但只扫描前MAXLEN个字符,如果其中没有发现'/0'终止符,则返回MAXLEN */ extern size_t strnlen (__const char *__string, size_t __maxlen) __THROW __attribute_pure__ __nonnull ((1)); #endif __BEGIN_NAMESPACE_STD /* 返回对错误码ERRNUM的字符串描述 */ extern char *strerror (int __errnum) __THROW; __END_NAMESPACE_STD /* 下面都是非标准扩展或内部使用的函数 */ __END_DECLS #endif /* string.h */
    1、字符串连接strcat,strncat,wcscat,wcsncat:将字符串src(或其前n个字符)连接到dest,后面两个是宽字符版本。

/* strcat.c:strcat函数的实现 */ #include <string.h> #include <memcopy.h> /* 非标准头文件,定义了reg_char类型 */ #undef strcat /* 将字符串SRC连接到DEST */ char * strcat (dest, src) char *dest; const char *src; { char *s1 = dest; const char *s2 = src; reg_char c; /* reg_char在memcopy.h中定义,就是char类型,它表示c存储在register中 */ do /* 让s1指向dest的终止位置,即首个'/0'的下一位置 */ c = *s1++; while (c != '/0'); s1 -= 2; /* 让s1指向dest中的终止符'/0'的前一个位置,这样就可以进行连接操作了 */ do /* 做连接操作 */ { c = *s2++; *++s1 = c; } while (c != '/0'); return dest; } libc_hidden_builtin_def (strcat)

/* strncat.c:strncat函数的实现 */ #include <string.h> #ifdef _LIBC # include <memcopy.h> /* 非标准头文件,定义了reg_char类型,就是char类型 */ #else typedef char reg_char; #endif #undef strncat /* 将s2的前n个字符连接到s1,若s2不足n个字符,则连接完s2(包括终止符)后 就返回 */ char * strncat (s1, s2, n) char *s1; const char *s2; size_t n; { reg_char c; char *s = s1; do /* 让s1指向dest的终止位置,即首个'/0'的下一位置 */ c = *s1++; while (c != '/0'); s1 -= 2; /* 让s1指向dest中的终止符'/0'的前一个位置,这样就可以进行连接操作了 */ if (n >= 4) /* 做连接操作,每4个字符作为一组来进行连接 */ { size_t n4 = n >> 2; /* 让n除以4,计算出循环次数 */ do { c = *s2++; /* 每次循环都要连接4个字符,总共连接了4*n4个字符 */ *++s1 = c; if (c == '/0') /* 在连接时,每当遇到'/0',连接操作就停止,并返回目标串 */ return s; c = *s2++; *++s1 = c; if (c == '/0') return s; c = *s2++; *++s1 = c; if (c == '/0') return s; c = *s2++; *++s1 = c; if (c == '/0') return s; } while (--n4 > 0); n &= 3; /* 求出n除以4的余数 */ } while (n > 0) /* 对剩下的几个字符(最多3个)进行连接 */ { c = *s2++; *++s1 = c; if (c == '/0') return s; n--; } if (c != '/0') /* 如果末尾不是终止符,则要补上一个终止符 */ *++s1 = '/0'; return s; }

    解释:
    (1)strcat基本思想:把指针移到dest的终止符'/0'的前一个位置,然后扫描src的每个字符并连接到dest的后面。
    (2)strncat基本思想:为减少扫描的循环次数以提高效率,对src的每4个字符作为一组来进行连接,让n除以4,计算出循环次数n4。每次循环都要连接4个字符,总共连接了4*n4个字符,最后对剩下的几个字符(最多3个)进行连接。若src的第n个字符不是终止符'/0',则连接的末尾还要补上一个终止符。
    2、字符串比较strcmp,strncmp,wcscmp,wcsncmp:按照字典顺序比较两个字符串(或其前n个字符)的大小。

/* strcmp.c:strcmp函数的实现 */ #include <string.h> #include <memcopy.h> /* 非标准头文件,定义了reg_char类型 */ #undef strcmp /* 比较S1和S2,小于返回小于0的数,等于时返回0,大于时返回大于0的数 */ int strcmp (p1, p2) const char *p1; const char *p2; { register const unsigned char *s1 = (const unsigned char *) p1; register const unsigned char *s2 = (const unsigned char *) p2; unsigned reg_char c1, c2; do { c1 = (unsigned char) *s1++; c2 = (unsigned char) *s2++; if (c1 == '/0') /* 若s1终止,则比较结束,返回相应差值 */ return c1 - c2; } while (c1 == c2); /* 做相等比较,直到不相等时退出循环 */ return c1 - c2; /* 返回相应的差值 */ } libc_hidden_builtin_def (strcmp)

/* strncmp.c:strncmp函数的实现 */ #include <string.h> #include <memcopy.h> #undef strncmp /* 比较s1和s2的前n个字符,不足n个字符时使用整个字符串, 小于返回小于0的数,等于时返回0,大于时返回大于0的数 */ int strncmp (s1, s2, n) const char *s1; const char *s2; size_t n; { unsigned reg_char c1 = '/0'; unsigned reg_char c2 = '/0'; if (n >= 4) /* 做比较操作,每4个字符作为一组来进行比较 */ { size_t n4 = n >> 2; /* 做比较操作,每4个字符作为比较来进行连接 */ do { c1 = (unsigned char) *s1++; /* 每次循环都要比较4个字符,总共比较了4*n4个字符 */ c2 = (unsigned char) *s2++; if (c1 == '/0' || c1 != c2)/* 每当遇到s1终止或c1!=c2,比较操作就结束,并返回相应差值 */ return c1 - c2; c1 = (unsigned char) *s1++; c2 = (unsigned char) *s2++; if (c1 == '/0' || c1 != c2) return c1 - c2; c1 = (unsigned char) *s1++; c2 = (unsigned char) *s2++; if (c1 == '/0' || c1 != c2) return c1 - c2; c1 = (unsigned char) *s1++; c2 = (unsigned char) *s2++; if (c1 == '/0' || c1 != c2) return c1 - c2; } while (--n4 > 0); n &= 3; /* 求出n除以4的余数 */ } while (n > 0) /* 对剩下的几个字符(最多3个)进行比较 */ { c1 = (unsigned char) *s1++; c2 = (unsigned char) *s2++; if (c1 == '/0' || c1 != c2) /* 若s1终止或c1!=c2,则比较结束,返回相应差值 */ return c1 - c2; n--; } return c1 - c2; /* 返回相应差值 */ } libc_hidden_builtin_def (strncmp)

    解释:
    (1)strcmp基本思想:扫描两个串,并对字符作相等比较,直到不相等时退出循环,返回这两个字符的差值。
    (2)strncmp基本思想:做比较操作,每4个字符作为一组来进行比较,以提高效率。扫描两个串,每次循环时连续比较4个字符,直到比较完前n个字符。

你可能感兴趣的:(c,String,存储,扩展,include,extension)