其實這個問題大致的意思就是讓你寫一個函數,這個函數有三個參數:需要移動的指針地址void *src,目的地地址void*dest,以及內存的字節長度len。讓你將src中的內容移動到dest中。
之所以總結這個,是因為在面試騰訊的實習時,兩次被問到這個問題,在一面的時候面試官挺耐心的,因為我沒聽過這個(雖然很基礎),他就很細心地講解,然后讓我寫出自己的思路;在二面的時候另一個面試官讓我把代碼寫出來,結果我寫得很復雜,就跪了。。。
其實在一面之后,我有回去百度看了下這個函數,但是因為自己知道了在內存移動的時候會出現什么情況,就沒看代碼實現(自大啊。。。),后來就吃虧了。
分析:
對於內存移動,首先要考慮:dest和src是不是指向同一內存地址。在這種情況下,就不需要進行任何實際的移動了,如圖所示:
其次,我們要考慮兩個指針指向的len長度的內存會不會有重疊,如果有重疊的話,直接進行移動有可能會破壞原始數據(在還沒移動之前):
在情況2中,dest在src的右邊(dest的地址值比src大),且dest與src之間的長度比len小,在這種情況下,如果直接從低地址往高地址復制,那么從dest開始的位置到src+len位置的數據將會被破壞。
在情況3中,因為dest與src的距離大於len,所以不會出現重疊的情況。
在情況4中,dest的地址值比src小(即dest在src的左邊),如果src與dest的距離比len小,雖然會出現重疊的情況,但是卻不會造成數據的損壞。因為重疊那部分數據在被替換前已經被移走。因此在這種情況下,不管dest和src之間的距離是多大,從低地址向高地址移動是沒有問題的。
代碼實現:
在面試的時候,我傻傻地把所有情況都用一個if來做判斷,然后用幾個指針來避免數據破壞,其實,並不需要這么麻煩。
當dest在src的左邊(即dest的地址值比src的小)時,只需要從左往右賦值(即從低地址開始復制);當dest在src的右邊(即dest的地址值比src的大),只需要從右往左開始賦值就行了(即從高地址開始復制)。使用這種方法,即使發生重疊,也能避免數據的破化。
1 void* MyMemMove(void *dest, const void *src, size_t nBytes){ 2 void *ret = dest; //用於返回 3 4 const char *from = static_cast<const char*>(src); 5 char *to = static_cast<char*>(dest); 6 7 if( dest < src ){ //dest在src的左邊 8 while( nBytes-- ) //從左往右復制 9 *to++ = *from++; 10 }else if( dest > src ){ //dest在src的右邊 11 from += nBytes - 1; //從右往左復制 12 to += nBytes - 1; 13 while( nBytes-- ) 14 *to-- = *from--; 15 } 16 return ret; 17 }
寫完之后,在博客園看到這篇寫得挺簡潔的,推薦一下:http://www.cnblogs.com/kekec/archive/2011/07/22/2114107.html