您的位置:首页 > 编程语言 > C语言/C++

C字符串函数的典型实现

2015-08-30 16:57 363 查看
学习字符串一段时间,整理下字符串函数的实现,并加入一些注释。

strlen -- 计算字符串的长度

/*返回值:目标串的长度

*参数:str为欲计算长度的字符串

*/

size_t strlen(const char* str)

{

     size_t length = 0;

     while(*str++)          //注:*p++相当于(*p)++

          ++length;

     return length;

}

稍加改进如下:

size_t strlen(const char* str)

{

     const char* cp = str;

     while(*cp++) ;

     return (cp - str -1);

}

不借助中间变量:

(标准写法)

int strlen(char* str)

{

     if(str[0] == '\0')

          return 0;

     else

          return ( strlen( (char*) (&str[0] + 1) ) + 1 );     //运用递归

}

(简略写法)

int strlen(char* str)

{

     if(*str == '\0')

          return 0;

     else

          return ( (strlen(str + 1) ) + 1);

}

但是如果给此函数传入一个NULL的话,程序会崩溃。所以,用以下的做法加强函数的容错性和健壮性:

int strlen(const char* str)

{

     int len = 0;

     assert(str != NULL);     //使用断言要include头文件assert.h,assert表示理论可能但实际不可能发生的事

     while(*str++)

          len++;

     return len;

}

strcpy -- 字符串拷贝

/*返回值:目标串的地址

*参数:dst为目标字符串,src为原字符串

*/

char* strcpy(char* dst, const char
10520
* src)

{

     char* r = dst;

     assert( (dst != NULL) && (src != NULL) );

     while( (*dst++ = *src++) != '\0' );          //赋值表达式返回左操作数,所以在赋值NULL后,循环停止

     return r;

}

1. 为什么要返回char*?

--为了实现链式表达式,例如 int length = strlen( strcpy(dst, "hello world" ) );

2. 假如考虑dst和src内存重叠的情况,strcpy该怎么实现?

     解决内存重叠,用memmove,原型是void* memmove(void* dst, const void* src, size_t count)。

3. 这个函数不会自己判断原字符串是否比目标空间大,必须要程序员自己检查,否则很容易造成拷贝越界,如:

     char* a = "hello world";

     char c[5];

     strcpy(c, a);     //输出为c = "hello world",数组c只有5个字节的空间,但是经过strcpy后a的剩余字符也拷贝过去了,如果c后面是系统程

                              序空间,那就要出问题了。

strncpy -- strcpy 的改进版本,拷贝规定长度的字符串 

/*返回值:目标串的地址

*参数:dst为目标字符串,src为原字符串, len为复制字符串的长度,即使未遇到原串的'\0',如果已经复制了len个字符,复制同样会终止。

*/

char* strcpy(char* dst, const char* src, int len)

{

     assert(dst != NULL && src != NULL);

     char* r = dst;

     int i = 0;

     while(i++ < len && *dst++ = *src++);

     if(*dst != '\0')

          *dst = '\0';

     return r;

}

多了一个拷贝长度的参数。需要注意的是长度参数应该为目的空间的大小,并且这个函数不会自己附加字符串结束符'\0',要自己加。看下面的例子:

     char* a = "hello world";

     char* b = "123456789";

     char c[5];

    strncpy(c, b, strlen(b) ) = 123456789     //拷贝长度不对,还是越界

    strncpy(c, a, sizeof(c) ) = hello6789      //拷贝长度正确,但是因为拷贝长度内不包括'\0',所以输出的时候还是会把原本的空间内容输

                                                                 出,直到遇到一个结束符'\0'。

所以正确的做法应该是: strncpy(c, a, sizeof(c)-1); c[4] = '\0';     //输出为c=hell

memcpy -- 类似strncpy 

区别:

(1)strncpy只能复制字符串,但memcpy对类型没有要求。

(2)strncpy有两个终止条件,memcpy只有一个终止条件,那就是复制n个字节。(n是memcpy的第三个参数)

(3)要特别注意目的地址和源地址重合的问题,拷贝前要加以判断。

(4)实现这个函数时一般要把原来的指针类型转换成char*,这样每次移动都是一个字节。

      strncpy是把Num个字符从src复制到dest,但是如果遇到src字符结尾,那么复制提前结束,后面没有复制完的字符,不予以处理,当然dest,src地址不能重叠。

    memcpy也是把Num个字符从src复制到dest,但是它是内存复制,完整的复制Num个字节,不会因为遇到'\0'而结束。

函数实现:

void* memcpy(void* dst, void* src, unsigned int count)

{

    assert( dst != NULL && src != NULL);

     if(dst == src)

          return src;

     char* d = (char*)dst;     //在函数里面生成临时指针,这样不会改变原始指针

     char* s = (char*)s;

     while(count-- > 0)

          *d++ = *s++;

     return dst;

}

strcat -- 字符串拼接

/*功能:把str2所指字符串添加到str1结尾处(覆盖str1结尾处的'\0')并添加'\0'

*返回值:拼接后的字符串首地址

*参数:str1为原字符串,str2为要拼接的字符串

*/

char* strcat(char* str1, char* str2)

{

   assert(str1 != NULL || str2 != NULL);

   char* pt = str1;

   //while(*str1++ != '\0');      //不能这样遍历str1!str1移到了的下一位,导致后面拼接后再打印的字符串也只能读出str1,读到'\0'处就结

                                             //束了。*和++的优先级是一样的,当优先级一样时,程序按自左至右的顺序执行,所以当*str1='\0'时,str仍

                                             //然要往下移一位,即str1指向满足条件后的下一个字节。 

     while(*str1 != '\0') str1++;      //正确做法!str1移到'\0'处就停止了。

     while((*str1++ = *str2++) != '\0');      //本句的计算顺序:1. *str1=*str2;   2. 判断整个表达式的值是否为真,即*str1!='\0';  3. 满足,

                                                               //则继续循环,否则终止,不论循环继续与否,接下来要执行str1++;str2++; str1和str2谁先自增

                                                               //在这里是无关紧要的。 

     //while(*str2 != '\0') *str1++ = *str2++;

     //*str1 = '\0';                                         //这两句与上一句等效

     return pt;

}

注:这里返回char*,同样是为了实现链式表达式。

strcmp -- 比较两个字符串

/*返回值:0:s1 == s2; 1:s1 > s2;-1:s1 < s2.

*参数:s1, s2为要比较的字符串

*/

int strcmp(const char* s1, const char* s2)

{

     int ret = 0;

     while( !( ret = *(unsigned char*)s1 - *(unsigned char*)s2 ) && *s2 )     //直到s1和s2当前数值不相等且s2不为\0时退出while

     {

          ++s1;

          ++s2;

     }

          if( ret < 0 )

               ret = -1;

          else if( ret > 0 )

               ret = 1;

     

     return ret;

}

strchr -- 查找字符串中首次出现字符ch的位置

/*返回值:如果字符串中存在字符ch,返回首次出现ch的位置指针,否则返回NULL。

*参数:s1, s2为要比较的字符串

*/

char* strchr(const char* str, int ch)

{

     while(*str && *str != (char)ch)

          str++;

     if(*str == (char)ch)

          return (char*)str;

     return NULL;

}

strstr -- 查找字符串中第一次包含另一个字符串的位置

char* strstr(const char* s1, const char* s2)

{

     if(*s1 == 0)

     {

          if(*s2)

               return NULL;

          return (char*)s1;

     }

     while(*s1)

     {

          int i = 0;

          while(1)

          {

               if(s2[i] == 0)

                    return (char*)s1;

               if(s1[i] != s2[i])

                    break;

               i++;

          }

          s1++;

     }

     return NULL;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 字符串