首页 > 代码库 > 走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数
走进C标准库(8)——"string.h"中函数的实现相关字符串操作函数
我的strcat:
1 char *strcat(char *dest,char *src) 2 { 3 char * reval = dest; 4 while(*dest) 5 dest++; 6 while(*src) 7 *dest++ = *src++ ; 8 *dest = *src; 9 return reval; 10 }
MSVC:
1 char * __cdecl strcat ( 2 char * dst, 3 const char * src 4 ) 5 { 6 char * cp = dst; 7 8 while( *cp ) 9 cp++; /* find end of dst */ 10 11 while( *cp++ = *src++ ) ; /* Copy src to end of dst */ 12 13 return( dst ); /* return dst */ 14 15 }
在while( *cp++ = *src++ )中,条件的值为赋值语句的返回值即*cp被赋的值,也就是此时*src的值。则,当*src为0时,将其赋给*cp后完成赋值。非常简洁。
该函数的前提条件:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。
我的strncat:
1 char *strncat(char *dest,char *src,int n) 2 { 3 char * reval = dest; 4 while(*dest) 5 dest++; 6 while(n-- && (*dest++ = *src++)); 7 if(n < 0) 8 *dest = 0; 9 return reval; 10 }
MSVC:
1 char * __cdecl strncat ( 2 char * front, 3 const char * back, 4 size_t count 5 ) 6 { 7 char *start = front; 8 9 while (*front++) 10 ; 11 front--; 12 13 while (count--) 14 if (!(*front++ = *back++)) 15 return(start); 16 17 *front = ‘\0‘; 18 return(start); 19 }
我的strchr:
1 char *strchr(const char *s,char c){ 2 while(*s) 3 { 4 if(*s == c) 5 return s; 6 s++; 7 } 8 return NULL; 9 }
MSVC:
1 char * __cdecl strchr ( 2 const char * string, 3 int ch 4 ) 5 { 6 while (*string && *string != (char)ch) 7 string++; 8 9 if (*string == (char)ch) 10 return((char *)string); 11 return(NULL); 12 }
我的strcmp:
1 int strcmp(const char *s1,const char * s2){ 2 while(*s1 == *s2 && *s1) 3 { 4 ++s1; 5 ++s2; 6 } 7 return *(unsigned char *)s1 - *(unsigned char *)s2; 8 }
MSVC:
1 int __cdecl strcmp ( 2 const char * src, 3 const char * dst 4 ) 5 { 6 int ret = 0 ; 7 8 while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) 9 ++src, ++dst; 10 11 if ( ret < 0 ) 12 ret = -1 ; 13 else if ( ret > 0 ) 14 ret = 1 ; 15 16 return( ret ); 17 }
strcmp返回值不必为1和-1的。使用unsigned char 因为有符号数可能会导致比较大小错误。
我的strcpy:
1 char *strcpy(char *dest,const char *src){ 2 char * reval = dest; 3 while(*dest++ = *src++); 4 return reval; 5 }
MSVC:
1 char * __cdecl strcpy(char * dst, const char * src) 2 { 3 char * cp = dst; 4 5 while( *cp++ = *src++ ) 6 ; /* Copy src over dst */ 7 8 return( dst ); 9 }
我的strncpy:
1 char *strncpy(char *dest, const char *src, int n){ 2 char * reval = dest; 3 while(n--){ 4 if(*src) 5 *dest++ = *src++; 6 else 7 *dest++ = 0; 8 } 9 return reval; 10 }
MSVC:
1 char * __cdecl strncpy ( 2 char * dest, 3 const char * source, 4 size_t count 5 ) 6 { 7 char *start = dest; 8 9 while (count && (*dest++ = *source++)) /* copy string */ 10 count--; 11 12 if (count) /* pad out with zeroes */ 13 while (--count) 14 *dest++ = ‘\0‘; 15 16 return(start); 17 }
我的strcspn:
1 int strcspn(const char *s1,const char *s2){ 2 const char *cp; 3 int reval = 0; 4 for(; *s1; s1++){ 5 for(cp = s2; *cp; cp++){ 6 if(*s1 == *cp) 7 return reval; 8 } 9 ++reval; 10 } 11 return reval; 12 }
MSVC:
1 size_t __cdecl strcspn ( 2 const char * string, 3 const char * control 4 ) 5 { 6 const unsigned char *str = string; 7 const unsigned char *ctrl = control; 8 9 unsigned char map[32]; 10 int count; 11 12 /* Clear out bit map */ 13 for (count=0; count<32; count++) 14 map[count] = 0; 15 16 /* Set bits in control map */ 17 while (*ctrl) 18 { 19 map[*ctrl >> 3] |= (1 << (*ctrl & 7)); 20 ctrl++; 21 } 22 23 /* 1st char in control map stops search */ 24 count=0; 25 map[0] |= 1; /* null chars not considered */ 26 while (!(map[*str >> 3] & (1 << (*str & 7)))) 27 { 28 count++; 29 str++; 30 } 31 return(count); 32 }
函数说明:strcspn()从参数s 字符串的开头计算连续的字符, 而这些字符都完全不在参数reject 所指的字符串中. 简单地说, 若strcspn()返回的数值为n, 则代表字符串s 开头连续有n 个字符都不含字符串reject 内的字符。
返回值:返回字符串s 开头连续不含字符串reject 内的字符数目。
我的实现和《C标准库》书中基本相同,不需要任何的额外存储空间,但是使用两层的循环,花费较多的时间。
该函数的实质其实是判断s1中的每一个字符,是否在s2字符串中,对于这种判断是否在其中的问题,经常会采用映射表的方式来缩减查找时间,典型实现就是布隆过滤器。
此处,MSVC的实现就是采用了映射表的方式。
因为ASCII码有256个,所以需要256bit来作为映射标记。一个字节是8bit,所以需要32个字节。所以在代码中有 unsigned char map[32]的定义。
那么,我们就需要将8bit,分别映射出一个map的下标和bit中的位位置。
map的下表需要使用5bit(32),而bit中的位位置使用剩余的3bit(8)来映射。
通过*ctrl >> 3取到高5位到0-31的映射,通过1 << (*ctrl & 7)取得到1字节中某一位的标记。
完成控制字符的映射表建立,就能用o(1)的时间完成某个字符的查找了。
strerror
功 能: 返回指向错误信息字符串的指针
例如:
1 #include <stdio.h> 2 #include <errno.h> 3 4 int main(void) 5 { 6 char *buffer; 7 buffer = strerror(errno); 8 printf("Error: %s\n", buffer); 9 return 0; 10 }
此段代码strerror指向的内存中的字符串为No Error
我的strlen:
1 int strlen(const char *s){ 2 const char *cp = s; 3 while(*cp){ 4 cp++; 5 } 6 return (cp - s); 7 }
MSVC:
1 size_t __cdecl strlen ( 2 const char * str 3 ) 4 { 5 const char *eos = str; 6 7 while( *eos++ ) ; 8 9 return( (int)(eos - str - 1) ); 10 }
返回值应该不需要强制类型转换,因为指针相减返回值是int。当然,加上显式转换则更加明确。
ptrdiff_t
This is the type returned by the subtraction operation between two pointers. This is a signed integral type, and as such can be casted to compatible fundamental data types.
strpbrk和strspn的实现和strcspn相同
我的strrchr:
1 char *strrchr(const char *str, const char c){ 2 const char *cp = str; 3 if(*str == 0) 4 return NULL; 5 while(*str) 6 str++; 7 while(*cp++){ 8 if(*--str == c) 9 return str; 10 } 11 return NULL; 12 }
MSVC:
1 char * __cdecl strrchr ( 2 const char * string, 3 int ch 4 ) 5 { 6 char *start = (char *)string; 7 8 while (*string++) /* find end of string */ 9 ; 10 /* search towards front */ 11 while (--string != start && *string != (char)ch) 12 ; 13 14 if (*string == (char)ch) /* char found ? */ 15 return( (char *)string ); 16 17 return(NULL); 18 }
确实只需要初始位置的拷贝,不需要用拷贝来计数。
strtok,没想好如何实现比较合适。
MSVC:
1 char * __cdecl strtok ( 2 char * string, 3 const char * control 4 ) 5 { 6 unsigned char *str; 7 const unsigned char *ctrl = control; 8 9 unsigned char map[32]; 10 int count; 11 12 static char *nextoken; 13 14 /* Clear control map */ 15 for (count = 0; count < 32; count++) 16 map[count] = 0; 17 18 /* Set bits in delimiter table */ 19 do { 20 map[*ctrl >> 3] |= (1 << (*ctrl & 7)); 21 } while (*ctrl++); 22 23 /* Initialize str. If string is NULL, set str to the saved 24 * pointer (i.e., continue breaking tokens out of the string 25 * from the last strtok call) */ 26 if (string) 27 str = string; 28 else 29 str = nextoken; 30 31 /* Find beginning of token (skip over leading delimiters). Note that 32 * there is no token iff this loop sets str to point to the terminal 33 * null (*str == ‘\0‘) */ 34 while ( (map[*str >> 3] & (1 << (*str & 7))) && *str ) 35 str++; 36 37 string = str; 38 39 /* Find the end of the token. If it is not the end of the string, 40 * put a null there. */ 41 for ( ; *str ; str++ ) 42 if ( map[*str >> 3] & (1 << (*str & 7)) ) { 43 *str++ = ‘\0‘; 44 break; 45 } 46 47 /* Update nextoken (or the corresponding field in the per-thread data 48 * structure */ 49 nextoken = str; 50 51 /* Determine if a token has been found. */ 52 if ( string == str ) 53 return NULL; 54 else 55 return string; 56 }
用一个static变量来记录当前分割到的位置,它是线程不安全的,多次调用也会使它失效。
strcoll使用当前的区域设置来比较字符串,strxfrm使用当前的区域设置来转换字符串。当前区域设置由LL_COLLATE宏指定。它们均调用带有区域设置参数的内部版本strcoll_l和strxfrm_l来完成实际的工作。
完