高效面試之實現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; /* 其他情況 */
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); }