在純C底下,動態配置記憶體。估計大家都很熟練malloc()、free()。利用malloc配置記憶體,等用完後再用free刪除。非常單純。加入realloc來攪局後,事情就變得非常複雜了。
realloc原始目的是為了提昇效率。
我先用malloc挖了100的記憶體,然後我需要加大,變成101。那我先free釋放掉100,然後再重新配置101。太慢!原先的100記憶體,它的隔壁可能恰巧是空閒記憶體,只要後面多挖1就好了。這時候就可用realloc。
大概類似這樣
void* p;
p = malloc(100);
p = realloc(p,101);
free(p);
先要了100,覺得不夠,決定改要101。事後一樣用free刪除。似乎毫無問題?
問題出在realloc的行為非常複雜,沒有想像中的那麼美好。實際上完整的realloc行為如下:
1.先從舊位置找找,看看能不能原地加大。如果可以原地加大,就直接加大,然後傳回指標即可。當然多出來的1,函式庫不會幫你填值,所以內容不知。
2.舊位置不能加大。其他記憶體挖出101,舊記憶體100資料複製過去。然後舊記憶體會用free釋放掉。新的指標傳回去。當然多出來的1,函式庫不會幫你填值,所以內容不知。
3.舊位置不能加大。其他記憶體挖不出101。舊記憶體完全不動。直接回傳NULL。
所以下面這行會有什麼問題?
void* p = realloc(p,101);
等號左右兩邊的p可能記憶體位置根本不一樣。如果遇到情況3,新的p變成NULL。舊的p沒有人去呼叫free。
所以最正確方法是這樣:
void* p;
p = malloc(100);
if(p)
{
void *p2 = realloc(p,101);
if(p2==NULL)
{
// handle your error
}
else
p = p2;
}
free(p);
還有一個更麻煩的小細節。如果呼叫這行。
void *p2 = realloc(p,0);
會變成怎樣?
答案是p2會變成NULL,然後函式realloc會自動幫你free(p)。
所以你不能自己再手動free(p)一次。所以每次呼叫realloc之前,最好自己手動檢查長度。
這跟malloc行為不一樣。因為
p = malloc(0);
是合法的。而且事後要自己手動呼叫free(p)。
就一個很簡單的函式,隱藏的大量的陷阱。太容易寫錯。
沒有留言:
張貼留言