纯C 字符串操作函数 实现 (strcpy, strncpy, memcpy, memset, strcat, strlen ... )

实现C/C++中的字符串操作函数是很练内功的,别看着这些函数很简单,自己实现起来,还是有许多地方需要考虑的,所以没独立写过的朋友,可以自己敲敲看 . --By Crazybaby

strcpy:

  1. char* cb_strcpy(char* dst, const char* src) {  
  2.        
  3.      assert((dst!=NULL) && (src!=NULL));  
  4.        
  5.      char* retAddr = dst;       /**< retAddr is in static , char retAddr[] will in Stack, So... */  
  6.      while ((*(dst++) = *(src++))!='\0') {   
  7.           ;  
  8.      }  
  9.        
  10.      return retAddr;  
  11. }  

strncpy:

  1. char* cb_strncpy(char* dst, const char* src, size_t size) {  
  2.        
  3.      assert((dst!=NULL) && (src!=NULL));  
  4.        
  5.      char* retAddr = dst;       /**< retAddr is in static , char retAddr[] will in Stack, So... */  
  6.      int i = 0;   
  7.      while (((*(dst++) = *(src++))!='\0') && ((i++) < size)) {   
  8.           ;  
  9.      }  
  10.        
  11.      *(retAddr+size)='\0';      /**< cut off String  */  
  12.        
  13.      return retAddr;  
  14. }  

这个strncpy实现版本 和 stdlib.h 下的 strncpy 还是有区别的, 比如 

  1. char a[30];  
  2. strncpy(a, "Hello", 28);  //a除了有Hello,之后会有23个repeat '\0' . 这样会有效率的问题.   
  3.   
  4. char b[30];  
  5. cb_strncpy(b, "Hello", 28);   // 而只有 Hello'\0'   

CB:  strncpy相对于strcpy来说 ,安全性提高了一个等级 . 另外一个要非常注意的地方那个是 strcpy 和 strncpy 都会遇到 '\0' 结束.  

          另外:当请求的目标地址空间比源字符串空间要小的时候,strncpy 将不再用”\0”来结束字符串。这是一个巨大的隐患,所以还是不安全


memcpy Version 1:

  1. char* cb_memcpyVer1(char* dst, const char* src, size_t size) {  
  2.        
  3.      assert((dst!=NULL) && (src!=NULL));  
  4.   
  5.      char* retAddr = dst;  
  6.      while (size-- > 0) {           
  7.           *(dst++) = *(src++);  
  8.      }  
  9.      return retAddr;  
  10. }  


CB: memcpy 和 strncpy 最大的区别是 memcpy不会遇到 '\0' 结束拷贝 .


memcpy Version 2 :

  1. char* cb_memcpyVer2(char* dst, const char* src, size_t size) {  
  2.        
  3.      assert((dst!=NULL) && (src!=NULL));  
  4.        
  5.      char* retAddr = dst;  
  6.      size_t i = 0;  
  7.      /* --- 解决数据区重叠问题 --- */  
  8.      if ((retAddr>src) && (retAddr<(src+size))) {  
  9.           for (i=size-1; i>=0; i--) {  
  10.                *(dst++) = *(src++);  
  11.           }  
  12.      }  
  13.      else  
  14.      {  
  15.           for (i=0; i<size; i++) {  
  16.                *(dst++) = *(src++);  
  17.           }  
  18.      }  
  19.      *(retAddr+size)='\0';  
  20.        
  21.      return retAddr;  
  22. }  

CB: memcpyVersion1不能防止overlapping区域的问题, Ver2很好的解决了.

       

memcpy还有种蛋疼的写法:

  1. void* cb_memcpy(void* dest, const void* src, size_t count)  
  2. {  
  3.     char* d = (char*)dest;  
  4.     const char* s = (const char*)src;  
  5.     /* --- 每次复制8bit --- */  
  6.     int n = (count + 7) / 8;  
  7.     switch(count & 7)  
  8.     {  
  9.     case 0: do {   *d++ = *s++;  
  10.     case 7:        *d++ = *s++;  
  11.     case 6:        *d++ = *s++;  
  12.     case 5:        *d++ = *s++;  
  13.     case 4:        *d++ = *s++;  
  14.     case 3:        *d++ = *s++;  
  15.     case 2:        *d++ = *s++;  
  16.     case 1:        *d++ = *s++;  
  17.                } while (--n > 0);  
  18.     }  
  19.   
  20.     return dest;  
  21. }  


memset:

  1. void* cb_memset(void* buffer, int b, size_t size) {  
  2.        
  3.      assert(buffer!=NULL);  
  4.      char* retAddr = (char*)buffer;  
  5.      while (size-- > 0) {  
  6.           *(retAddr++) = (char)b;    
  7.      }  
  8.      return retAddr;  
  9. }  


Memset使用时误区: 

  1. char a[10];  
  2. memset(a, 0, sizeof(char)*10); //这个操作完全没必要 因为下面这块内存马上要被使用了。  
  3. memcpy(a, "Hello", 5);  


strlen:

  1. int cb_strlen(const char* str) {  
  2.        
  3.      assert(str!=NULL);  
  4.        
  5.      int len = 0;  
  6.      while (*str!='\0') {       /**< '\0', stop */  
  7.           str++;  
  8.           len++ ;  
  9.      }  
  10.        
  11.      return len;  
  12. }  


Recursive strlen:

  1. int cb_strlen(const char *str)  
  2. {  
  3.      if ((str == NULL) || (*str == '\0')) {  
  4.           return 0;  
  5.      }  
  6.      else  
  7.      {  
  8.           return cb_strlen(str+1)+1; /**< Rescursive */  
  9.      }  
  10. }  


strcat:

  1. char* cb_strcat(char* dst, char* src) {  
  2.        
  3.      assert(src!=NULL);  
  4.        
  5.      char* retAddr = dst;  
  6.      /* --- Find last position --- */  
  7.      while (*dst++ != '\0') {  
  8.           ;  
  9.      }  
  10.      dst--;  
  11.      while (*dst++ = *src++) {  
  12.           ;  
  13.      }  
  14.      return retAddr;  
  15. }  


strcmp:

  1. int cb_strcmp(char* str, char* str2) {  
  2.        
  3.      assert((str!=NULL) && (str2!=NULL));  
  4.        
  5.      char* pStr = str;  
  6.      char* pStr2 = str2;  
  7.      while (*pStr && *pStr2 && (*pStr==*pStr2)) {  
  8.           pStr++;  
  9.           pStr2++;  
  10.      }  
  11.      return (*pStr - *pStr2);   /**< 相等则为0 , 前者大于后者大于0, 反之小于0 */  
  12. }  


strncmp:

  1. int cb_strncmp(char* str, char* str2, size_t size) {  
  2.        
  3.      assert((str!=NULL) && (str2!=NULL));  
  4.        
  5.      char* pStr = str;  
  6.      char* pStr2 = str2;  
  7.      while (size-- && *pStr && *pStr2 && (*pStr == *pStr2)) {  
  8.           pStr++;  
  9.           pStr2++;  
  10.      }  
  11.      return (*pStr - *pStr2);     
  12. }  


strchr:

  1. char* cb_strchr(char* str, char c){  
  2.        
  3.      assert(str!=NULL);  
  4.      char* retAddr = str;  
  5.        
  6.         while (*retAddr!=c) {  
  7.           retAddr++;  
  8.      }  
  9.      if (*retAddr == c) {  
  10.           return retAddr;  
  11.      }  
  12.      else  
  13.      {  
  14.           return NULL;  
  15.      }  
  16. }  


这些字符串操作函数实现时要注意这些问题 :
1. 需要检查指针的有效性,一般通过直接和NULL进行比较来判断。
2. 函数需要能够进行链式操作,也就是说 char* a = strcpy(b, "Hello"); 
3. src的值需要加 const
4. strcnpy 和 memcpy  有 '\0' 判断的区别 
5. 内存区重叠问题 :

 比如这个程序:

  1. int main(void) {  
  2.      char buffer[]="abcdefg";  
  3.      memcpy(buffer, buffer+2 ,3); //buffer+2(从c开始 长度3个 cde)   
  4.      printf("%s", buffer);  
  5. }  

这个结果为: cdedefg .

再看下面这个程序:

  1. int main(void) {  
  2.      char buffer[]="abcdefg";  
  3.      memcpy(buffer+2, buffer ,3); //如果你幸运的话 会出现 abcab[d]fg 中括号是随机值  
  4.      printf("%s", buffer);  
  5. }  

这里的memcpy就需要用 memmove的实现方式来代替 。




最近查看了微软的c标准库针对字符串操作的部分源代码,整理了一下放上来
char * strcat(char * dst, const char * src)
{
 char * cp = dst;
 
 while(*cp)
 {
  cp++;  /* find end of dst */
 }
 
 while (*cp++ = *src++); /* Copy src to end of dst */
 
 return dst; /* return dst */
}

char * strcpy(char * dst, const char * src)
{
 char * cp = dst;
 
 while (*cp++ = *src++);  /* Copy src over dst */
 
 return dst;
}

char * strchr(const char * string, int ch)
{
 while (*string && *string != (char)ch)
 {
  string++;
 }
 
 if (*string  == (char)ch;
  return (char *)string;
 
 return NULL;
}

int strcmp(const char * src, const char * dst)
{
 int ret = 0;
 
 while (!(ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
  ++src, ++dst;
  
 if ( ret < 0 )
   ret = -1 ;
  else if ( ret > 0 )
   ret = 1 ;

  return( ret );
}

size_t strlen(const char * str)
{
 const char * eos = str;
 
 while (*eos++);
 
 return (int)(eos - str - 1);
}

char * strstr(const char * str1, const char * str2)
{
 char * cp = (char *)str1;
 char *s1, *s2;
 
 if (!*str2)
  return (char *)str1;
 
 while (*cp)
 {
  s1 = cp;
  s2 = str2;
  
  while (*s1 && *s2 && !(*s1-*s2))
   s1++, s2++;
  
  while (!*s2)
   return cp;
  
  cp++;
 }
 
 return NULL;
}



如果你的简历上,写了你精通C/C++,如果让你写个字符串函数,结果写不出来,是不是很囧。
字符串是C中很重要的一部分,我们在编程的操作上,很多都是处理字符串的。
对C中的字符串你应该这样认识,它是一个结尾带'\0'的数组。不关写成
char *s = "nihao";
char s[] ="nihao";
char s[10] = "nihao";
他们在内存中都是可以认为是数组形式的存在,连续的存放,最后以'\0'结尾。
数组在C中是二等公民,所以它不是像内置类型int,double,...等这样可以直接写=,>,==...这样进行操作的。
他需要用到string.h中的字符串处理函数。
首先结束3个最常用的。
1,字符串拷贝函数strcpy(); //string copy;
2,字符串比较函数strcmp(); //string compare;
3,字符串长度函数strlen(); //string length;
下面是上面3个函数的实现
#include <stdio.h>
//字符串拷贝函数,把dest指向的字符串拷贝到source中
char *mystrcpy(char *source,const char *dest)
{
char *s = source;
while (*dest!='\0')
   *s++ = *dest++;
*s = '\0';
return source;
}
//字符串比较函数,相等返回0,s1大返回正整数,s2大返回负整数
int mystrcmp(const char *s1,const char *s2)
{
    while(*s1!='\0'&&*s2!='\0'&&(*s1==*s2))s1++,s2++;
    return (*s1)-(*s2);
}
//字符串长度函数,长度不包括结尾的'\0'
int mystrlen(const char *s)
{
int res = 0;
while(*s++!='\0')
   res++;
return res;
}
void main()
{
char data[100];
char *s = "ni";
char *s1 = "nihao";
mystrcpy(data,s);
printf("%s,%d\n",data,mystrlen(s));
printf("%d\n",mystrcmp(s,s1));
}
注:这3个函数常用,实现的代码其实也简单,可以看到,每个函数的体的代码不超过5行。最少的2行。
其次是会用到的,没有前3个那么高。
1,字符串连接函数strcat();//string catenate
2,字符串转换为小写函数strlwr();//string lowercase
3,字符串转换为大写函数strupr();//string uppercase
字符串连接函数,类似C++中的+号操作。大小写字母的转换,本质上就是ASCII码值加减32.
下面是这3个函数的实现。
#include <stdio.h>
//字符串连接函数
char *mystrcat(char *source,const char *dest)
{
char *s = source;
while(*s!='\0')s++;
while(*dest!='\0')*s++ = *dest++;
*s='\0';
return source;
}
//字符串转换小写字母函数
char *mystrlwr(char *source)
{
   char *s = source;
   do 
   {
   if(*s>='A'&&*s<='Z')
      *s+=32;
   } while (*s++!='\0');
   return source;
}
//字符串转换大写字母函数
char *mystrupr(char *source)
{
char *s = source;
do 
{
   if(*s>='a'&&*s<='z')
    *s-=32;
} while (*s++!='\0');
    return source;
}
void main()
{
char data[100]="ni";
char *s = " hao!";
printf("%s\n",mystrcat(data,s));
printf("%s\n",mystrupr(data));
printf("%s\n",mystrlwr(data));
}
注:看这3个函数的代码实现也不难,函数体内的代码也不长。大小写转换函数很相似。
然后说几个不常用,但往往确会容易面试遇到的字符串处理函数。
1,字符串倒转函数。strrev();//string reverse
2,字符串查找字符串第一次出现函数。strstr();
这2个函数实现如下:
#include <stdio.h>
//字符串倒转函数
char *mystrrev(char *source)
{
char *s1 = source,*s2 = source,temp;
while(*s2!='\0')s2++;
if(s1!=s2)s2--;
while(s1<s2)temp = *s1,*s1 = *s2,*s2 = temp,s1++,s2--;
return source;
}
//字符串查找指定字符串的第一次出现,没有出现返回NULL,出现则返回出现的位置
char *mystrstr(const char *source,const char *dest)
{
   char *s = source,*d = dest;
   while(*source!='\0')
   {
    while(*s!='\0'&&*d!='\0'&&*s==*d)s++,d++;
    if(*d=='\0')
    {
     s = source;break;
    }
    if(*s=='\0')
    {
     s = NULL;break;
    }
       source++;
    s=source,d=dest;
   }
   if (*source=='\0')s=NULL;
   return s;
}
void main()
{
char data[100]="huifeng00 : ni hao";
char *s = " in";
printf("%s\n",mystrrev(data));
printf("%s\n",mystrstr(data,s));
}
最后说一个调用比较另类的字符串处理函数
1,查找由第2个字符串指定分界符对第一个串分解后的单词函数
strtok();//string token;
函数原型:char *strtok(char *s, const char *delim);它的调用比较特别。
首次调用s指向要分解的串,以后在调用s参数则设为NULL。
例如:char data[] = "a,b,c,d";
分界符","就是逗点,字符串形式表示。
第一次调用strtok(data,",");然后在调用就是strtok(NULL,",");
这个函数的处理思想:用一个静态变量记录每次调用时候分解字符串的处理指针位置,且每次调用的时候,
把字符串中出现分界符的字符用'\0'替换,直到把字符串遍历完为止。
这个函数的实现
#include <stdio.h>
#include <string.h>
char * mystrtok(char * source, char *delim)
{
static char *begin;
if (source!=NULL)
{
   begin = strstr(source,delim);
   while(begin==source)
   {
    source += strlen(delim);
    begin = strstr(source,delim);
   }
}
else
{
        source = begin;
   if (source==NULL)
   {
    return NULL;
   }
   begin = strstr(source,delim);
   while(begin==source)
   {
    source += strlen(delim);
    begin = strstr(source,delim);
   }
}
if (begin!=NULL)
{
   memset(begin,0,strlen(delim));
   begin += strlen(delim);
}
else
   if (*source==0)
   {
    return NULL;
   }
   return source;
}
void main()
{
char data[100]=" huifeng00 ni hao";
char *p = mystrtok(data," ");
printf("%s\n",p);
while (p!=NULL)
{
   p = mystrtok(NULL," ");
   if(p!=NULL)
        printf("%s\n",p);
}
}
以前专门写过这个函数的实现
http://hi.baidu.com/huifeng00/blog/item/f8dfa80b420a408ed0581b0f.html
能看到这里,算你有耐心。
算是一个总结吧。用了一定的时间写代码和文章。
希望对能看到这篇文章的C爱好者有帮助。
coder:huifeng00


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值