1. 程式人生 > >高效面試之實現strcpy等簡單庫函式

高效面試之實現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.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);
}