• 走进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 = '';
    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++ = '';
    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
    ", 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 == '') */
    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++ = '';
    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来完成实际的工作。

  • 相关阅读:
    汇编写启动代码之关看门狗、设置栈、调用C、开关icache
    ARM汇编伪指令
    多寄存器访问、后缀、栈、!、^
    协处理器CP15操作指令
    常用的ARM指令
    汇编指令及其特点
    ARM的37个寄存器以及异常处理方法
    一步步点亮LED之汇编点亮LED
    机器学习_第一节_numpy
    函数进阶_生成器
  • 原文地址:https://www.cnblogs.com/shijiezhenmei/p/3702410.html
Copyright © 2020-2023  润新知