C語言函數(shù)之memcpy函數(shù)用法實例_第1頁
C語言函數(shù)之memcpy函數(shù)用法實例_第2頁
C語言函數(shù)之memcpy函數(shù)用法實例_第3頁
C語言函數(shù)之memcpy函數(shù)用法實例_第4頁
C語言函數(shù)之memcpy函數(shù)用法實例_第5頁
已閱讀5頁,還剩5頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第C語言函數(shù)之memcpy函數(shù)用法實例目錄前言下面是參考網(wǎng)友的一些總結(jié):補充:背景再來看看dest和src地址有重疊的情況總結(jié)

前言

昨天自己動手實現(xiàn)memcpy這個函數(shù),用一個例程試了一下,結(jié)果正確,滿心歡心,可是有些地方想不明白,于是百度了一下,結(jié)果自己寫的函數(shù)簡直無法直視。

覺得還是寫個總結(jié),以示教訓(xùn)。

先貼上我自己的函數(shù):

char*mymemcpy(char*dest,constchar*src,intn)

char*pdest;

char*psrc;

pdest=dest;

psrc=src;

for(n;nn--)

*pdest=*psrc;

pdest++;

psrc++;

}

我這個程序只能是在非常理想的條件下才能完成復(fù)制任務(wù),一旦參數(shù)有誤,那么就會運行出錯;另外沒有返回值,而是直接將dest指針作為了返回型參數(shù)。

另一點需要注意:在函數(shù)體中我另外聲明了兩個指針,分別指向dest和src,我是這樣想的:由于循環(huán)中要移動指針,為了不影響主程序中實參指針的位置,所以我認為需要重新定義兩個指針。后來我發(fā)現(xiàn),我這樣想是錯誤的,指針變量實質(zhì)上也是變量,指針作為形參,那么這個指針也是實實在在存在的,那么在函數(shù)中改變這個形參指針的位置并不會影響主程序中實參指針的位置。所以在這個函數(shù)中,沒必要重新聲明兩個指針,最好是在返回值中返回一個指向dest的指針就夠了。

我會犯上述這個錯誤,應(yīng)該是受了地址傳參和值傳參的影響。由于地址傳參太過于強調(diào)函數(shù)可以改變主程序中的數(shù)據(jù)內(nèi)容,以至于讓我把形參指針和實參指針等同起來了。實質(zhì)上形參指針變量位置的改變并不會影響實參指針的位置。

重要的內(nèi)容寫前面,

自己總結(jié)該函數(shù)的幾個要點:

1、參數(shù)判斷:對參數(shù)的合法性進行判斷

2、聲明中間變量:由于要返回目的指針,所以需要保留目的首地址;最好是不要破壞形參,設(shè)置臨時變量替換

3、void*類型:要注意dest和src的類型可能不同,進而造成dest++src++不匹配的問題,先強制類型轉(zhuǎn)換

4、void類型做右值:void類型變量或是返回值為void類型的函數(shù),一旦做右值編譯出錯

5、指針形參:即上面提到的,指針變量本質(zhì)仍是指針,形參指針位置的改變不會影響實參指針的位置

下面是參考網(wǎng)友的一些總結(jié):

memcpy實現(xiàn)內(nèi)存拷貝,根據(jù)這個問題,我們可以提取出下面幾點:

1.可以拷貝任何數(shù)據(jù),數(shù)據(jù)類型不能受限

2.源數(shù)據(jù)不能被改變

通過上面兩點可以確定函數(shù)原型為void*memcpy(void*dest,constvoid*src),現(xiàn)在分析一下這些足夠了嗎這個函數(shù)拷貝什么時候結(jié)束,當時我就用了這個函數(shù)原型,由于是拷貝的任意數(shù)據(jù),所以不能指定一個明確的結(jié)束標志,既然這樣那么只有明確的指定拷貝的大小才可以.所以函數(shù)原型變成這樣void*memcpy(void*dest,void*src,size_tcount);好吧,函數(shù)原型既然已經(jīng)確認了,剩下的應(yīng)該就是寫函數(shù)了,先等等,先別急著寫函數(shù),實際上對于C語言的開發(fā)者來說,重要的不是函數(shù)功能的實現(xiàn),重要的是函數(shù)出錯時的處理,如果你用的是Java或者C#大不了拋個異常出來,軟件崩潰一下,不會對其他造成任何影響;C這東西弄不好會把整個系統(tǒng)弄癱瘓,所謂兵馬未動,糧草先行,我么還是先考慮考慮出錯的問題吧!我們根據(jù)函數(shù)原型來分析,

void*memcpy(void*dest,constvoid*src,size_tcount);

1.空指針的問題,如果dest、src兩者或者兩者之一為NULL,那么自然能沒得完了;

2.拷貝大小count為小于等于0的值,自然也是不正確的;

3.目標有沒有足夠的大小容納源數(shù)據(jù),這個我們在函數(shù)內(nèi)部似乎也無法進行保證,但是我們自己也要想到

4.內(nèi)存地址有沒有重疊,這個我們暫時不考慮了。

有了上面的提示寫起來自然比較簡單了

#includestdio.h

void*memcpy(void*dest,constvoid*src,size_tcount)

span/spanif(NULL==dest||NULL==src||count=0)

span/spanreturnNULL;

span/spanwhile(count--)

span/span*dest++=*src++;

span/spanreturndest;

}

上面這段代碼在Linux中使用gcc編譯是沒錯的,但是會有警告,所以改成這樣:

(注意,上述代碼我在測試時,不僅有警告還有一個錯誤:error:invaliduseofvoidexpression,這是因為void型的變量或者是函數(shù)返回值被使用了。使用下面這段代碼是可以通過編譯的:)

#includestdio.h

void*memcpy(void*dest,constvoid*src,size_tcount)

if(NULL==dest||NULL==src||count=0)

returnNULL;

while(count--)

*(char*)dest++=*(char*)src++;

returndest;

}

OK,也就這樣了,要是面試官再問起內(nèi)存重疊的問題,你再和他侃侃.我的面試算是泡湯了.

總結(jié):不要著急慢慢來,根據(jù)需求推出原型,根據(jù)原型推斷問題,這算是個教訓(xùn)吧!!!

補充:

在這里非常感謝博客園的求道于盲這位好心的網(wǎng)友指出了我程序中的兩個錯誤,再次感謝.

1.返回了一個++過的指針

2.size_t是無符號類型的,size_t的定義為:typedefunsignedintsize_t;

所以count=0,只會判斷==0的情況,如果傳入-1,會產(chǎn)生一個很大的無符號整型.

希望別人注意,改過的程序如下:

void*memcpy(void*dest,constvoid*src,intcount)

void*ptr=dest;

if(NULL==dest||NULL==src||count=0)

returnNULL;

while(count--)

*(char*)dest++=*(char*)src++;

returnptr;

}

本文通過匯總一些網(wǎng)上搜集到的資料,總結(jié)c語言中的memcpy實現(xiàn)

背景

想必大多數(shù)人在面試時被要求寫memcpy的實現(xiàn),很不幸,我也吃過這個虧(這種題要是寫的一塌糊涂后面完全沒戲),所以還是得提前準備一下,不然就只能呵呵了。先來看看一段錯誤的示范:找茬:)

void*memcpy(void*dest,constvoid*src,unsignedintcount);

if((src==NULL)||(dest==NULL))

return;

while(count--)

*dest++=*src++;

returndest;

}

dest都指到哪里去了怎么著也得備份一下dest的值,好讓函數(shù)返回的指針是從頭開始的

考慮一下指針類型,如果dest和src的指針類型不一樣,不能直接++賦值.例如:int*p和char*q,p++指針的值是4個4個加(0,4,8),q++是1個1個加(0,1,2,3,4)

第二版-定義兩個臨時變量,不要直接++dest和src,并且指明指針類型char*

void*memcpy(void*dest,constvoid*src,size_tcount)

char*tmp=dest;

constchar*s=src;

while(count--)

*tmp++=*s++;

returndest;

}

能否改進src和dest都強制轉(zhuǎn)換成char*類型的指針,那么copy一定是一個字節(jié)一個字節(jié)的完成?那么第三版來了

void*memcpy(void*dst,constvoid*src,size_tnum)

intnchunks=num/sizeof(dst);/*按CPU位寬拷貝*/

intslice=num%sizeof(dst);/*剩余的按字節(jié)拷貝*/

unsignedlong*s=(unsignedlong*)src;

unsignedlong*d=(unsignedlong*)dst;

while(nchunks--)

*d++=*s++;

while(slice--)

*((char*)d++)=*((char*)s++);

returndst;

}

看著沒什么問題了,可是如果dst和src地址不對齊,copy效率豈不降低是否需要先處理一下地址不對齊的情況

再來看看dest和src地址有重疊的情況

內(nèi)存重疊問題是指目的地址的內(nèi)存空間的首地址,包含在源內(nèi)存空間中,這兩段內(nèi)存空間有了交集,因而在使用memcpy進行內(nèi)存復(fù)制操作時,這段重疊的內(nèi)存空間會被破壞.這種情況在應(yīng)用程序級代碼中一般不會出現(xiàn)的,而在驅(qū)動或內(nèi)核級代碼中要十分小心,盡量使用memmove函數(shù).

memcpy對內(nèi)存空間有要求的,dest和src所指向的內(nèi)存空間不能重疊,否則復(fù)制的數(shù)據(jù)是錯誤的.下面具體講解一下這個錯誤是如何產(chǎn)生的.

如果內(nèi)存空間布局入下圖所示:

src所指向的內(nèi)存空間后面部分數(shù)據(jù)被新拷貝的數(shù)據(jù)給覆蓋了(也就是dest=src+size).所以拷貝到最后,原來的數(shù)據(jù)肯定不是原來的數(shù)據(jù),拷貝的數(shù)據(jù)也不是想要的數(shù)據(jù),使用memcpy函數(shù)可以得到錯誤的結(jié)果.

再者,如果內(nèi)存空間布局入下圖所示:

雖然原來的數(shù)據(jù)不再是原來的數(shù)據(jù)(dest+size=src),但拷貝的數(shù)據(jù)是原來的數(shù)據(jù),使用memcpy函數(shù)可以得到正確的結(jié)果.因此,在使用memcpy這個函數(shù)之前,還需要做一個判斷,如果dest=src你才能使用這個函數(shù)不過完全沒有必要,解決辦法,從高地址向地地址copy

實例

void*memcpy(void*dest,constvoid*src,size_tcount)

char*d;

constchar*s;

if(dest(src+size))||(destsrc))

d=dest;

s=src;

while(count--)

*d++=*s++;

else/*overlap*/

d=(char*)(dest+count-1);/*offsetofpointerisfrom0*/

s=(char*)(src+count-1);

while(count--)

*d--=*s--;

returndest;

}

memcpy是把src指向的對象中的size個字符拷貝到dest所指向的對象中,返回指向結(jié)果對象的指針.

memmove也是把src指向的對象中的size個字符拷貝到dest所指向的對象中,返回指向結(jié)果對象的指針,但這兩個函數(shù)在處理內(nèi)存區(qū)域重疊的方式不同.

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論