資料結構逆向分析-Map
map是一個典型的二叉樹結構,準確的來說是一個平衡二叉樹或者紅黑樹,特點是資料儲存是有序的儲存。
參考侯傑老師的stl原始碼剖析,map裡面採用的是RB-TREE也就是紅黑樹
map儲存的資料是以鍵值對的形式來儲存的,Key:Value
優勢:查詢資料效率高,因為是平衡二叉樹
缺點:插入資料效率低,因為要插入後變成平衡二叉樹。
開始分析:
採用比較簡單的程式碼:
#include<map>
using std::map;
using std::pair;
int main()
{
map<int, int> MyMap;
int SizeMap = sizeof(MyMap);
MyMap.insert(pair<int, int>(1, 100));
MyMap.insert(pair<int, int>(2, 300));
MyMap.insert(pair<int, int>(3, 150));
MyMap.insert(pair<int, int>(4, 200));
MyMap.insert(pair<int, int>(5, 170));
MyMap.insert(pair<int, int>(6, 180));
MyMap.insert(pair<int, int>(7, 90));
return 0;
}
然後畫圖表示一下:這就是初始化一個map的記憶體圖,從分析stl的經驗上來看MyMap中的第一個指標指向的這個0081e5f8又指回去了好像stl容器都有個這個東西,猜測來講沒啥用。然後這個後面的0081F5F8很有可能是一個指向整個平衡二叉樹的頭,然後第三個000很有可能就是一個size來記錄節點的個數,當然這是我們的猜測,還要看記憶體來說話。
執行第一條插入指令:
MyMap.insert(pair<int, int>(1, 100));
成了這樣:
再來猜測一下,首先這個MyMap的第一個欄位肯定是沒啥用了,然後最後一個欄位確實是裡面的大小,然後第二個欄位指向的內容,很有可能是指向來了根節點,然後節點的內容裡面,最後兩個欄位是來存放 key->value鍵值對的。有一些欄位暫時不知道有啥用,繼續往下弄幾個看看。
執行第二條和第三條指令:
MyMap.insert(pair<int, int>(2, 300));
MyMap.insert(pair<int, int>(3, 150));
首先這裡的平衡二叉樹應該是這樣的:
如果所有箭頭都標出來會比較亂,所以這裡只用了一些關鍵的:
由此可以推出來,這個map第二個欄位指向的應該是一個總領作用的結構體,其中第二個欄位指向的是平衡二叉樹的根節點,然後第一個和第三個欄位是什麼意思暫時不知道,最後一個結束欄位的0101也不知道,然後資料節點的話,通過對根節點猜測,第一個欄位應該指向的是左節點,第三個欄位指向的是右節點。
目前的結果是這樣:
再參考侯傑老師的stl原始碼剖析書後:
可以確定很多東西了:
根據侯傑老師的定義重新寫程式碼分析:
由於目前只有cdcd後面哪兩個位元組的內容不知道了,這裡我選擇根據官方書重新寫程式碼來觀察這個欄位的內容:
#include<map>
using std::map;
using std::pair;
int main()
{
map<int, int> MyMap;
int SizeMap = sizeof(MyMap);
MyMap.insert(pair<int, int>(5, 100));
MyMap.insert(pair<int, int>(6, 300));
MyMap.insert(pair<int, int>(8, 150));
MyMap.insert(pair<int, int>(7, 200));
MyMap.insert(pair<int, int>(11, 170));
MyMap.insert(pair<int, int>(10, 180));
MyMap.insert(pair<int, int>(12, 90));
MyMap.insert(pair<int, int>(13, 400));
MyMap.insert(pair<int, int>(15, 400));
return 0;
}
這裡畫圖太麻煩,我只畫比較關鍵的內容了:
可以看到內容只有0和1,我測試是平衡二叉樹的平衡因子,但是不對,如果是平衡因子的畫13為啥是0001,由於這個底層是紅黑樹,所以這裡我猜是用來標記紅黑的。
紅黑樹的性質:
規則1:節點是紅色或黑色的;
規則2:根節點是黑色的;
規則3:每個葉子節點都是黑色的空節點(NIL節點);
規則4:每個紅色節點的兩個子節點都是黑色的(從每個葉子到根的所有路徑上不可能有兩個連續的紅色節點);
規則5:從任一節點到其每個葉子節點的所有路徑都包含相同數目的黑色節點;
將紅黑樹的節點預設顏色設定為紅色,是為儘可能減少在插入新節點對紅黑樹造成的影響。
也就是說8肯定是黑色,那麼預設是紅色的情況下6,11是紅色,而紅色不能連續,紅色的子節點必須是黑色,則5,7,10,13肯定是黑色,而預設是紅色,且不會影響別的那麼12,15也就是紅色。
破案了:情況非常吻合,紅色為0000,黑色為0001。