calloc や malloc といった API の仕様は、言語である C/C++ の範疇ではなく、ライブラリ (VC ランタイムや GLIBC) に依存する部分だと思います。"malloc しくみ" などのキーワードで検索すれば、どのようにメモリ領域が割り当てられるかを説明しているページが数多くヒットするので、幾つか探してみてはいかがでしょうか。また、Youtube には伝説の malloc 動画と呼ばれる有名な動画もあります。
わん さんが書きました:このような結果となってしまった理由について、お願いしたいと思います。
aとbは不連続な領域をとり、aとbの間の領域に消えた12文字が存在する。
そのような結果となってしまった理由として、12文字はメモリの管理に使う情報またはアラインメントに合わせるためという理解でよろしいでしょうか?
わんさんの見た 12 文字 (すなわち "b - a == 0x20" ) は、梅衣堂ひよさんが仰るように、プロセス開始直後でヒープ領域ががら空きであり、a と b に隣り合った領域 (= チャンク) が順当に割り当てられた結果だと思います。
VC++ の場合は分かりませんが、GLIBC であれば calloc を呼び出すと以下のコードで実装されている __libc_calloc という内部関数が呼ばれます。
sourceware.org Git - glibc.git/blob - malloc/malloc.c
https://sourceware.org/git/?p=glibc.git ... 3db04#l496
上記ソースコード中のコメントに、各チャンクの構造が略図で示されています。
コード:
/*
malloc_chunk details:
(..snip..)
An allocated chunk looks like this:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk, but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(..snip..)
*/
もし隣り合ったチャンクがともに解放されていないのであれば、みけCAT さんの仰る「管理データ」は[Size of next chunk, in bytes] で示された 8 バイトということになります。ただし、各チャンクが管理データを持っているため、管理データの長さがチャンク間の長さに寄与することはなさそうです。
そこで出てくるのが「アラインメント」の方ですが、これは単純に 64bit 境界に合わせるのではなく、GLIBC の中でアラインメント境界 (= MALLOC_ALIGNMENT) がワード サイズの 2 倍とされているようなので、64bit だと 16 バイトでしょうか。さらに、チャンクの最小サイズ (= MIN_CHUNK_SIZE = &((struct malloc_chunk*)0)->fd_nextsize == 32?) も決まっています。
20 バイトを確保すると、まず最小サイズの 32 バイトに切り上げられ、これは 16 バイト境界に合うためチャンクは 32 バイトとなります。これが 12 バイトの真相ではないでしょうか。
上記はコードをざっと眺めただけの判断なので厳密には間違っているかもしれませんが、回答になっていれば幸いです。