ヒープ領域に割り当てたアドレスでメモリープールを作るのが、筆者が定義するカスタムヒープアロケーターです。
メモリープールとは malloc() で割り当てられたヒープ領域のことで、特に何か変なことはする必要はありません。
しかし都度 malloc() / free() をするスタイルではなく、malloc() も free() も一回だけしか行わないのがポイントです。
考え方としてはカスタムスタックアロケーターと同様で、割り当てたメモリー領域のアドレスを加算したり減算して、どの変数やオブジェクトに使うかの管理は自分でやります。
この場合に重要なのが、ヒープ領域のアドレスが上方向か、下方向に割り当てられていくかという点です。
例えばアドレス領域が 0x10 から 0x20 だと仮定して考えてみてください。
8 バイト間隔のアドレスを使いたい場合は [0x10, 0x18, 0x20] というように上方向か、 [0x20, 0x18, 0x10] の下方向のいずれかの可能性があります。
幸いにもこの検証は容易にできるのでやってみましょう。
main.cpp.
1 #include <cstdio> 2 #include <cstdlib> 3 4 int main() 5 { 6 int *x = new int(5); 7 int *y = new int(10); 8 std::printf("x: %p\n",x); 9 std::printf("y: %p\n",y); 10 11 int* arr = new int[10]; 12 13 for(int i = 0; i < 10; ++i){ 14 std::printf("arr[%d]: %p\n",i,arr+i); 15 } 16 17 delete x; 18 delete y; 19 delete[] arr; 20 return 0; 21 }
ビルドと実行結果.
$ g++ main.cpp $ ./a.out x: 0x55dc7ad0ee70 y: 0x55dc7ad0ee90 arr[0]: 0x55dc7ad0f2c0 arr[1]: 0x55dc7ad0f2c4 arr[2]: 0x55dc7ad0f2c8 arr[3]: 0x55dc7ad0f2cc arr[4]: 0x55dc7ad0f2d0 arr[5]: 0x55dc7ad0f2d4 arr[6]: 0x55dc7ad0f2d8 arr[7]: 0x55dc7ad0f2dc arr[8]: 0x55dc7ad0f2e0 arr[9]: 0x55dc7ad0f2e4
このソースコードでは new / delete を使ってますが、malloc() / free() でも同じとなるはずです。
まあやりたい人は自分でやってみてくださいね。
それで最初の検証は、割り当ての順序とアドレス割り当ての関係です。
6 int *x = new int(5); 7 int *y = new int(10); 8 std::printf("x: %p\n",x); 9 std::printf("y: %p\n",y);
ポインターは以下のような値となります。
x: 0x55dc7ad0ee70 y: 0x55dc7ad0ee90
y は x の後に割り当てられ、 x のアドレスより上位アドレスにあるので、上方向にアドレスが伸びていってますね。
では配列はどうでしょうかね?
11 int* arr = new int[10]; 12 13 for(int i = 0; i < 10; ++i){ 14 std::printf("arr[%d]: %p\n",i,arr+i); 15 }
この場合は arr[] 配列には整数型 10 個分のメモリー割り当てがされてます。
これを要素ごとに出力すると以下のようになります。
arr[0]: 0x55dc7ad0f2c0 arr[1]: 0x55dc7ad0f2c4 arr[2]: 0x55dc7ad0f2c8 arr[3]: 0x55dc7ad0f2cc arr[4]: 0x55dc7ad0f2d0 arr[5]: 0x55dc7ad0f2d4 arr[6]: 0x55dc7ad0f2d8 arr[7]: 0x55dc7ad0f2dc arr[8]: 0x55dc7ad0f2e0 arr[9]: 0x55dc7ad0f2e4
配列のインデックスが上がるほど、アドレスも上位アドレスになっています。
てなことで、ヒープ領域のメモリーは上向きに伸びてくことが確認できました。
Copyright 2018-2019, by Masaki Komatsu