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

来源:互联网 发布:tomcat 启动端口 编辑:程序博客网 时间:2024/06/01 16:44

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

2.strcpymemcpy主要有以下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);}


0 0