第15章 伸縮自在

 構造体のメンバに可変長配列(第1部第73章参照)を入れることがあると思います。しかし、それをファイルに保存したいとき、処理が少し面倒です。もし、そのメンバ自体が可変長なら簡単なのですが...。


 では、今回の要点です。


 では、いってみましょう。


 さて、ファイルの情報を構造体に入れ、それをメモリ上に保存しておくとします。簡単のため、情報はパス名(ファイルの位置名)、属性、サイズの3つとします。

struct SFileData
{
    char          szPath[_MAX_PATH];  // パス名
    unsigned int  fAttribute;         // 属性
    unsigned int  nFileSize;          // サイズ
};

 しかし、パス名というのは最高文字数 _MAX_PATH に達するようなことは滅多にないことです。それどころか、半分にすら満たないことがほとんどです。

 となると、szPath メンバはこんなにも確保するのはメモリがもったいないです。とはいえ、_MAX_PATH だけ必要な場合もあるので、サイズを小さくするということもできません。

 では、どうすればいいのでしょうか? 可変長配列第1部第73章参照)を使うと、ある程度問題は解決しそうです。

struct SFileData
{
    char*         pszPath;     // パス名
    unsigned int  nPathLen;    // パス名の長さ
    unsigned int  fAttribute;  // 属性
    unsigned int  nFileSize;   // サイズ
};

 これでいい感じです。

 さて、これをファイルに保存もできるようにしたいとします。この構造体を直接保存しようとすると、pszPath はポインタなので、そこには確保したメモリへのアドレスが保存されてしまいます。そして、肝腎の確保したメモリの内容は保存されません。

 となると、先ず nPathLen 以降のデータを保存し、次に確保したメモリの内容を保存するという、2回の処理が必要になります。前者のではそのまま保存できるというのに、これはちょっと面倒です。

 可変長でありながらメンバ変数にポインタを使わないということができれば、この問題も解決するのですが...。


 そこで、次のような荒技が考えられます。

 先ず、

struct SFileData
{
    unsigned int  nSize;      // 構造体のサイズ
    unsigned int  fAttribute; // 属性
    unsigned int  nFileSize;  // サイズ
    char          szPath[1];  // パス名
};

という構造体を作ります。szPath のサイズはです。

 そして、この構造体を動的に確保します。この時、構造体のサイズよりも大きくメモリを確保すれば、その余分に確保した部分は szPath を使うことで利用できます

 こうすれば、ファイルに保存するときも nSize だけファイルに書き込めばいいようになりますね。

 何とも荒っぽい方法ですが、この方法はC/C++ではたまに使われます。この長さの変えられるメンバ szPath のことを、可変長配列メンバと呼びます。

 よく使う所では、ビットマップの情報を保持する構造体 BITMAPINFO がそうなっています。

 256色ビットマップは色を0〜255までの番号で扱います。そして、その番号に対応する色の情報をこの BITMAPINFO 構造体に保持しておきます。この色の情報のことを「パレット」と呼びます。

 これは16色ビットマップでも同じです。しかし、16色と256色とではパレットの数は違います。ということは、このメンバは可変長だと便利ですね。

 これがフルカラーになれば、パレットを使用しません。なので、このメンバは使わないということになります。このことから分かるように、要素数は1でなくてはならないわけではありません。


 では、実際に SFileData 構造体を使ってみましょう。

プログラム
// Sizing1.cpp
#include <iostream.h>
#include <string.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>

#define numof(array)   (sizeof (array) / sizeof *(array))

struct SFileData
{
    unsigned int  nSize;      // 構造体のサイズ
    unsigned int  fAttribute; // 属性
    unsigned int  nFileSize;  // サイズ
    char          szPath[1];  // パス名
};

void Init();   // 初期化
bool Input();  // 入力
void Output(); // 出力
void Delete(); // メモリの解放

// ファイルのデータ
SFileData* g_apfdata[100];
// データ数
int        g_nData = 0;

int main()
{
    Init();

    while(Input());
    Output();

    Delete();

    return 0;
}

// 初期化
// 一応ヌルポインタの値が0でないときのために...
void Init()
{
    int i;
    for(i = 0; i < numof(g_apfdata); i++)
        g_apfdata[i] = NULL;
}

// 入力
bool Input()
{
    if(g_nData == numof(g_apfdata))
        return false;

    // 領域は動的確保しなくてはならないわけではありません
    char       bufFData[sizeof (SFileData) + _MAX_PATH];
    SFileData* pfdTemp = (SFileData*)bufFData;
    char*      pbufTemp;

    cout << "パス名、属性、サイズを入力して下さい" << endl;
    cin >> pfdTemp->szPath >> pfdTemp->fAttribute
        >> pfdTemp->nFileSize;
    if(pfdTemp->nFileSize == 0)
        return false;

    // サイズは szPath までのバイト数+文字列の長さ+1
    // 最後の1はヌルキャラクタの分です
    pfdTemp->nSize = offsetof(SFileData, szPath) +
                     strlen(pfdTemp->szPath) + 1;

    // メモリの確保
    pbufTemp = new char[pfdTemp->nSize];
    if(pbufTemp == NULL)
        return false;
    g_apfdata[g_nData] = (SFileData*)pbufTemp;

    // データのセット
    memcpy(g_apfdata[g_nData], pfdTemp, pfdTemp->nSize);

    g_nData++;
    return true;
}

// 出力
void Output()
{
    FILE* pfile;
    int  i;

    pfile = fopen("Sizing1.dat", "wb");

    for(i = 0; i < g_nData; i++)
        fwrite(g_apfdata[i], 1, g_apfdata[i]->nSize, pfile);

    fclose(pfile);
}

// メモリの解放
void Delete()
{
    int i;
    for(i = 0; i < g_nData; i++)
        delete [] (char*)g_apfdata[i];
}
実行結果例
パス名、属性、サイズを入力して下さい
C:\Program\Algorithms.html 32 412315
パス名、属性、サイズを入力して下さい
C:\Program\Registry\デスクトップアイコンの非表示法.txt 32 155
パス名、属性、サイズを入力して下さい
C:\Program\Program.ico 32 3638
パス名、属性、サイズを入力して下さい
C:\Program\カセットずらし\slide.html 32 6380
パス名、属性、サイズを入力して下さい
0 0 0
Sizing1.dat
16進値文字
27 00 00 00 20 00 00 00  9B 4A 06 00 43 3A 3A 5C
72 6F 67 72 61 6D 5C 41  6C 67 6F 72 69 74 68 6D
73 2E 68 74 6D 6C 00 43  00 00 00 20 00 00 00 9b
00 00 00 43 3A 5C 50 72  6F 67 72 61 6D 5C 52 65
67 69 73 74 72 79 5C 83  66 83 58 83 4E 83 67 83
62 83 76 83 41 83 43 83  52 83 93 82 CC 94 F1 95
5C 8E A6 96 40 2E 74 78  74 00 23 00 00 00 20 00
00 00 36 0E 00 00 43 3A  5C 50 72 6F 67 72 61 6D
5C 50 72 6F 67 72 61 6D  2E 69 63 6F 00 31 00 00
00 20 00 00 00 EC 18 00  00 43 3A 5C 50 72 6F 67
72 61 6D 5C 83 4A 83 5A  83 62 83 67 82 B8 82 E7
82 B5 5C 73 6C 69 64 65  2E 68 74 6D 6C 00
'... ....J..C:\P
rogram\Algorithm
s.html.C... ....
...C:\Program\Re
gistry\デスクトッ
 プアイコンの非表
 示法.txt.#... .
..6...C:\Program
\Program.ico.1..
. .......C:\Prog
ram\カセットずら
し\slide.html.

 このように、可変長配列メンバを持つ構造体は直接宣言しません。つまり、

SFileData fdata;

のようにはしないのです。ただ、最後のメンバを使用しないときは、このようにしてもとりあえず実害はありません。


 今回はこれで終わりです。今回のプログラムはちょっと長いですが、落ち着いて解析してみて下さいね。

 では、今回の要点です。


 それでは、また。


第14章 整理整頓 | 第16章 仰山の引数

Last update was done on 2000.8.10

この講座の著作権はロベールが保有しています