構造体のメンバに可変長配列(第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 のサイズは1です。
そして、この構造体を動的に確保します。この時、構造体のサイズよりも大きくメモリを確保すれば、その余分に確保した部分は 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;
のようにはしないのです。ただ、最後のメンバを使用しないときは、このようにしてもとりあえず実害はありません。
今回はこれで終わりです。今回のプログラムはちょっと長いですが、落ち着いて解析してみて下さいね。
では、今回の要点です。
それでは、また。
Last update was done on 2000.8.10
この講座の著作権はロベールが保有しています