2019-07-19

純C語言的realloc,使用起來真是有夠危險

在純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)。

就一個很簡單的函式,隱藏的大量的陷阱。太容易寫錯。

沒有留言: