ヒープ領域に割り当てたアドレスでメモリープールを作るのが、筆者が定義するカスタムヒープアロケーターです。
メモリープールとは 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