生活随笔
收集整理的這篇文章主要介紹了
几个重要库函数的实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
面試官很喜歡讓求職者寫一些常用庫函數的實現,有很多是和字符串相關的,有一些是關于內存拷貝的。一般,常會讓寫的函數有以下幾個:
strcpy , strncpy, memcpy。
memset一般不會讓去寫,但這個函數也很有特點,有很多容易用錯的地方。一并總結吧。
1. strcpy
strcpy函數的原型是:
char * strcpy(char* dest, const char* src)
strcpy的實現經常要注意的細節是:
(1)判斷地址是否為空,個人感覺可以使用斷言
(2)參數只有兩個地址,沒有拷貝的長度。拷貝到'\0‘時就會終止,要保證最終dest末尾是'\0'。
(3)要保證目標字串的長度足夠,能夠容納原串的長度。
(4)因為拷貝是dest會移動,而最終要返回的是拷貝后字符串的起始地址,因此要先保存dest的地址,便于最終返回。
在實現這一點時,有兩種方法。 char* temp=dest; 拷貝時移動dest返回temp,或者拷貝時移動temp返回dest,不知道哪個是對的。感覺兩個都是沒有問題的
其中一種實現方式:
[cpp]?view plaincopy
char*?mystrcpy(char*?dest,const?char*?src)?? ?? {?? ?? ????????????assert(dest!=NULL?&&?src!=NULL);?? ?? ????????????char*?temp=dest;?? ?? ????????????while((*temp++?=?*src++?)!='\0')?? ?? ????????????{}?? ?? ????????????return?dest;?? ?? }??
2. strncpy
strncpy的功能和strcpy相似,只是它復制時多了一個終止條件。即是未遇到原串的'\0’,如果已經復制了n個字符(n為提供的參數長度),復制同樣會終止。
strcpy的實現要注意的細節也基本適用于strncpy的實現。
實現方式:
[cpp]?view plaincopy
char*?mystrncpy(char*?dest,?const?char*?src,?int?len)?? ?? {?? ?? ????????????assert(dest!=NULL?&&?src!=NULL);?? ?? ????????????char*?temp=dest;?? ?? ????????????int?i=0;?? ?? ????????????while(i++?<?len??&&?(*temp++?=?*src++)!='\0')?? ?? ????????????{}?? ?? ????????????if(*(temp)!='\0')?? ?? ???????????????????????*temp='\0';?? ?? ????????????return?dest;?? ?? }??
3. memcpy
memcpy和strncpy有些類似,但也有本質的不同。
(1)strncpy只能復制字符串,但memcpy對類型沒有要求。
(2)strncpy有兩個終止條件,memcpy只有一個終止條件,那就是復制n個字節。(n是memcpy的第三個參數)
(3)要特別注意目的地址和源地址重合的問題,拷貝前要加以判斷。
(4)實現這個函數時一般要把原來的指針類型轉換成char*,這樣每次移動都是一個字節。
實現方式:(考慮了兩個地址空間是否會有重疊)
[cpp]?view plaincopy
void*?mymemcpy(void*?dest,?void*?src,?int?len)?? ?? {?? ?? ????????????int?i=0;?? ?? ????????????char*?tempdest=(char*)dest;?? ?? ????????????char*?tempsrc=(char*)src;?? ?? ????????????if(tempdest<tempsrc?||?tempdest>(tempsrc+len-1))?? ?? ????????????{?? ?? ???????????????????????while(i<len)?? ?? ???????????????????????{?? ?? ???????????????????????????????????*tempdest++?=?*tempsrc++;?? ?? ???????????????????????????????????i++;?? ?? ???????????????????????}?? ?? ????????????}?? ?? ????????????else?? ?? ????????????{?? ?? ???????????????????????tempdest+=len;?? ?? ???????????????????????tempsrc+=len;?? ?? ???????????????????????i=len;?? ?? ???????????????????????while(i>0)?? ?? ???????????????????????{?? ?? ???????????????????????????????????*tempdest--?=?*tempsrc--;?? ?? ???????????????????????????????????i--;?? ?? ???????????????????????}?? ?? ????????????}?? ?? ????????????return?dest;?? ?? }??
?
注意,memcpy是對內存的拷貝,對其他安全性不做考慮。用戶在使用這個函數時要小心,比如用它來拷貝字符串(當然如果是字符串拷貝肯定是用strncpy)就要注意末尾的\0字符之類的。
4. memset
memset函數的原型是:
void *memset(void *s, int ch,size_t?n)
作用是把s所指向的地址開始的n個字節的內容全部置位ch所指定的ASCII值。
一般經常用memset對某段內存空間置零。
經常會出現的一個問題:在C++中,為什么不提倡在構造函數中使用:memset(this,0,sizeof(*this))
原因:?在C++中,如果類中都是基本類型的數據成員并且沒有虛函數和虛繼承的話,使用memset這樣用到沒有太多影響。
如果有虛函數,memset會把虛表指針等全部置零,對類會產生破壞。
三個函數的原型如下:
[cpp]?view plaincopy
void*?memset(void?*des,?int?val,?size_t?size)??? void?*?memcpy(void?*des,?const?void*?src,?size_t?size)?? void?*?memmove(void?*des,?const?void?*src,?size_t?size)?? 實現如下:
[cpp]?view plaincopy
void*?memset(void?*des,?int?val,?size_t?size)?{?? ????void?*start?=?des;?? ????while?(size--)?{?? ????????*(char*)?des?=?(char)?val;?? ????????des?=?(char?*)?des?+?1;?? ?? ?????????? ????}?? ????return?start;?? }?? void?*?memcpy(void?*des,?const?void*?src,?size_t?size)?{?? ????void?*ret?=?des;?? ????while?(size--)?{?? ????????*(char?*)?des?=?*(char?*)?src;?? ????????des?=?(char?*)des?+?1;?? ????????src?=?(char?*)src?+?1;?? ?? ?? ????}?? ????return?ret;?? }?? void?*?memmove(void?*des,?const?void?*src,?size_t?size)?{?? ????void?*ret?=?des;?? ????if?(des?<?src?||?(char?*)?des?>?(char?*)?src?+?size?-?1)?{?? ????????while?(size--)?{?? ????????????????*(char?*)?des?=?*(char?*)?src;?? ????????????????des?=?(char?*)?des?+?1;?? ????????????????src?=?(char?*)src?+?1;?? ?? ?? ????}?? ????}else{?? ????????des?=?(char?*)des?+?size?-?1;?? ????????src?=?(char?*)src?+?size?-?1;?? ????????while?(size?--?>?0){?? ????????????*(char?*)?des?=?*(char?*)?src;?? ?? ?? ????????????des?=?(char?*)des?-?1;?? ????????????src?=?(char?*)src?-?1;?? ????????}?? ????}?? ????return?ret;?? }?? 不采用//中的寫法是因為包報出警告:warning: value computed is not used
看起來不爽。
注意事項:
(1)使用memset的時候,要把最后一位或者最后一位的下一位置為‘\0’;
[cpp]?view plaincopy
char?buffer[20]?=?"hello";?? memset(buffer,?'1',?sizeof(char)*20);?? printf("%s\n",buffer);?? 運行結果:111111111111111111110@?? ?? char?buffer[20]?=?"hello";?? memset(buffer,?'1',?sizeof(char)*20);?? buffer[20]?=?'\0';?? printf("%s\n",buffer);?? 運行結果:11111111111111111111?? 因為在prinf一個字符串的時候,printf函數是遇見‘\0就停止。想第一個例子中的,buffer[20]都是‘1’,結束沒有‘\0’,所以打印出來的結果就不確定。當然,也有可能是對的,那只是運氣好而已。
(2)memcpy和strcpy的區別:
實際上區別只有一個,strcpy的操作對象只能是char *,而memcpy操作的對象是void *。(什么類型的都可以)。實際上,在memcpy的實現上,都是將(void *)裝換成為了(char *)來做的,其實跟strcpy一樣。
(3)memmove和memcpy的區別:
區別就是memmove要考慮內存區間重疊的情況,而memcpy不會。
關于這個問題,可以用下面的圖片來解釋:
內存區間重疊的情況如下和不會出現內存區間重疊的情況:
假設des為src + 2,如果按照memcpy處理,從頭開始拷貝,就要出現下面的悲劇:
src的內存都被污染了,而且如果這時候打印*des開頭的內存,仍然會出現未定義的情況:因為'\0'被覆蓋了。
[cpp]?view plaincopy
char?buffer5[10]?=?"1234";?? ????memcpy(buffer5?+?2,?buffer5,?sizeof(buffer5));?? ????printf("%s\n",?buffer5);?? ????char?buffer6[10]?=?"1234";?? ????memmove(buffer6?+?2,?buffer6,?sizeof(buffer6));?? ????printf("%s\n",buffer6?+?2);?? 運行結果:
121212121212????"
1234
總結
以上是生活随笔為你收集整理的几个重要库函数的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。