C++サンプル集

【第1部】C言語(K&R検証)編

0030.メモリプールを作成する

 今項ではメモリプールの作成を行います。
 C/C++では、新しいデータ領域(ヒープ領域)を必要とするときはmalloc()関数new演算子を使うのが一般的です。
 しかし、これらの処理はOSに対して新たな領域を要求するという、結構、重い処理になります。
 そのためスピード重視のソフトでは、あらかじめ、大きな領域を確保しておき、各処理などで必要になったときに、そのプールから一部を取得し、いらなくなったらかえす、という処理をする場合があります。
 このサンプルはその中でも後入れ先出しスタックのような処理が必要ですが、メモリプールの概要を説明するには十分です。
 きちんとしたメモリプールの実装は後ほど出てきます。

ソース紹介

 このサンプルはproject/Sample0030_1VS2019のソリューションがあります。もしほかのバージョンの場合はC++コンソールアプリを作成の上、以下のファイルを作成してください。
CPPファイルに記述
function.h
#pragma once

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cctype>
using namespace std;

char* alloc(int n);
void afree(char* p);
alloc.cpp
#include "function.h"

#define ALLOCSIZE 10000
static char allocbuff[ALLOCSIZE];
static char* allocp = allocbuff;

char* alloc(int n) {
    if (allocbuff + ALLOCSIZE - allocp >= n) {
        allocp += n;
        return allocp - n;
    }
    else {
        return nullptr;
    }
}
void afree(char* p) {
    if (p >= allocbuff && p < allocbuff + ALLOCSIZE) {
        allocp = p;
    }
}
main.cpp
#include "function.h"

/*
文字列をtからsにコピーする(ポインタ版3)
*/
void strcpy2(char* s, const char* t) {
    while (*s++ = *t++)
        ;
}

void printarr(char** parr) {
    while (*parr != nullptr) {
        cout << *parr << endl;
        parr++;
    }
    cout << endl;
}

int main()
{
    char* parray[6]{};

    char buff[10];

    for (int i = 0; i < 5; i++) {
        parray[i] = alloc(10);
        sprintf_s(buff, "hello%d", i);
        strcpy2(parray[i], buff);
        printarr(parray);
    }
    cout << "-------------"<< endl;

    for (int i = 4; i >= 0; i--) {
        afree(parray[i]);
        parray[i] = nullptr;
        printarr(parray);
    }

    return 0;
}
出力
hello0

hello0
hello1

hello0
hello1
hello2

hello0
hello1
hello2
hello3

hello0
hello1
hello2
hello3
hello4

-------------
hello0
hello1
hello2
hello3

hello0
hello1
hello2

hello0
hello1

hello0

サンプル説明

 このサンプルはalloc()関数およびafree()関数の動きを検証するものです。main()関数を見ていただくとわかりますが
    char* parray[6]{};
 というポインタの配列をあらかじめ用意しalloc()関数で取得した領域のポインタを保存します。その際その内容に
        sprintf_s(buff, "hello%d", i);
 で作成される文字列をコピーしておきます。
 領域は、1つ取得するたびにprintarr()関数で表示します。そこの途により、1つ1つ領域が増えていくのがわかります。
 領域の解放の場合は、逆の順番で開放していきます。1つ1つ領域が解放されるたびに、printarr()関数で表示します。そのため領域が減っていくのがわかります。

K&Rでの記述

 元になったのは第5章:ポインタと配列に記述されています。アドレス計算の説明のところです。