您的位置:首页 > 其它

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

2016-07-29 00:00 447 查看
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
前面没有出现终止符,
结果字符串也会被终止 */
#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个字符。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: