高效面试之实现strcpy等简单库函数

atoi,itoa

strcpy,strstr,strcmp

memcpy,memove,memset

1.itoa 

注意:字符串倒置

char* itoa(int a,char* string)
{
    int i=0,j=0;
    char temp[10],string[10];
    while(a)
    {    
        i=a%10+'0';
        temp[j++]=i;
        a=a/10;
     }
     i=0;
     j--;
     while(j>=0)
     string[i++]=temp[j--];
     string[i]='\0';
     return string;
}
 

/* 实现itoa函数的源代码 */

char *myitoa(int num,char *str,int radix)

{

/* 索引表 */

char index[]="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

unsigned unum; /* 中间变量 */

int i=0,j,k;

/* 确定unum的值 */

if(radix==10&&num<0) /* 十进制负数 */

{

unum=(unsigned)-num;

str[i++]='-';

}

else unum=(unsigned)num; /* 其他情况 */

/* 逆序 */

do

{

str[i++]=index[unum%(unsigned)radix];

unum/=radix;

}while(unum);

str[i]='\0';

/* 转换 */

if(str[0]=='-') k=1; /* 十进制负数 */

else k=0;

/* 将原来的“/2”改为“/2.0”,保证当num在16~255之间,radix等于16时,也能得到正确结果 */

char temp;

for(j=k;j<=(i-k-1)/2.0;j++)

{

temp=str[j];

str[j]=str[i-j-1];

str[i-j-1]=temp;

}

return str;

}

 

2.atoi

注意:判断正负

int atoi(char* s)

{

   assert(s!=NULL);

   int i,num,flag=0;//正负数标记

   if(s[0]=='-') flag=-1;

   i=(flag==0)?0:1;

   for(;i<strlen(s);i++)

      num=num*10+s[i]-'0';

   return (flag==0)?num:(-num);

}

 

3.strcpy

原型

char* strcpy(char *dest,const char *src);

实现

 

char* strcpy(char *dest,const char *src)

{

    assert( (dest != NULL) && (src != NULL) );  //写上

    char *temp=dest;

    while( (*dest=*src)!='\0')//赋值加判断写在一起,这样写简洁

    { 

        dest++;

        src++;

    }

    /*

     while( (*dest++=*src++)!='\0'); //更加简洁

    */

    return temp;

}

注意:

错误的做法:

1.不检查指针的有效性,说明答题者不注重代码的健壮性。

2.return new string("Invalid argument(s)");,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。

3.循环写成while(*strSrc!='\0') *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上'\0'。

 

4.strstr

原型:

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

{

   int len=strlen(s2);

   if(len == 0) return (char*)s1;

   for(;*s1;++s1)//这里其实就是for(s1=s1;*s1!='\0';++s1)

   {

       if(*s1==*s2  && strncmp(s1,s2,len))

           return (char*)s1;

   }

   return NULL;

}

 

注意:

1.注意判断s2为空的情况

2.使用strncmp来做判断

 

3.for ( ; *s1; ++s1 )和for ( ; *s1; s1++)有区别吗?在这里没有区别,++s1 和 s1++ 在赋值表达式中才有区别,单独使用的时候没有区别

 

5.strcmp

原型:int strcmp(constchar *s1,const char * s2);

 

当s1<s2时,返回值=-1

当s1==s2时,返回值=0

当s1>s2时,返回值 =1

注:c++ 中

当s1<s2时,返回值小于0

当s1==s2时,返回值等于0

当s1>s2时,返回值 大于0

即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。

 

 

 

6.memcpy

说明

1.source和destin所指的内存区域可以重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。

2.strcpy和memcpy主要有以下3方面的区别。

2.1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。

2.2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。

2.3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy

3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。

 

Linux中实现:

 

void* memcpy(void *dest, const void *src, size_t count)

{

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

    char *tmp = dest;//不能少,转换为char型,保证tmp++按照一个字节寻址

    const char *s = src;//

    while(count--)  //strcpy while( (*dest=*src)!='\0') 遇到\0结束复制

        *tmp++ = *s++ ;

    

    return dest;

}

 

注意:

1.判断结束条件为while(count--)即复制count个字节

2.参数都为void *类型,

3.在进行复制之前,为了保证按一个字节寻址,把void进行类型转换为char型

 

7.memmove

 

当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。

memmove的处理措施:

(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝

(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝

(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝

 

 

void * memmove(void* dest,void* src,size_t count)

{

    char *d =dst;

    const char *s =(const char*)src;

    if(d<s) //和memcoy一样,正向拷贝

    {

          while(count--)

            *d++=*s++;

    }   

    if(d>s)//反向拷贝

     { 

           d=d+count-1;

           s=s+count-1;

           while(count--)

               *d--=*s--;

     }      

    return dest;

}

 

8.memset

 

原型: void *memset(void *buffer, int c, int count);

用法:#include <string.h>

功能:把buffer所指内存区域的前count个字节设置成字符c。

说明:返回指向buffer的指针。

源码实现:

void *memset(void *src, int c, size_t count)
{
     assert(src!=NULL);
     char *tmpsrc=(char*)src;
      while(count--)
           *tmpsrc++ =(char)c;
      return src;

}

 

注意:

1.和前面一样,assert判断src是否为空

2.类型转换void转char

 

 

 

编写一个函数,把一个char组成的字符串循环右移n位

思路1:

void loopmove(char temp[],int n)

{

      int len=strlen(temp);

      char  c;

      for(int i=0;i<len;i++)

      {

            c=temp[len-1];//取出最后一位

            for(int j=len-2;j>=0;j--)

            {

                   temp[j+1]=temp[j];//剩余len-1位全部右移

             }

             temp[0]=c;

      }

}

思路2:

 void loopmove(char*pstr,int steps)
 {
     char temp[max];
     int n=strlen(pstr)-steps;
     strcpy(temp,pstr+n);
     strcpy(temp+steps,pstr);
    *(temp+strlen(pstr))='\0';//别忘了加\0
     strcpy(pstr,temp);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值