忘れないうちにメモをしろ 2000-5〜9 

(『サブルーチンやテクニックの覚書』改題)

 知っていたはずなのに忘れてたりする 基本的なルーチン(アルゴリズム)や どこかで見聞きしたテクニック、 また、よくコピー&ペースト して使ったりするサブルーチンなどの覚書メモです。
 ごく初歩的な件もあれば、自分ローカルな用途のもの も含めて(って高レベルなのは無しか:-)。
 そのまま使うものもあれば、ちょこっと書き換えたり して使いまわしているものです (たぶん権利関係の問題はないと思うけれど)。

シャッフル
乱数(rand,srand)ルーチン
自前の printf 系ルーチン(vsprintfの使用例)
自前の printf 系ルーチン 補足(winでprintfデバッグ)
複数バイトデータの検索(BM法)
整列済みのデータのテーブルから指定のデータを探す(ニ分岐検索)
CRC を使う(チェックサムのかわりに……)
#if,#define,#include,#macroなどの展開機能付き1行入力ルーチン


(2001-1) サブルーチン紹介みたいなのを目指すも、 ネタがそんなあるわけでもなく、息切れしたページ^^;
 自分がシャッフルの方法を思いだせず、自分の旧ソースから 引っ張りだすのに苦労したのが発端だったような。



シャッフル


 (トランプ)カードや(麻雀)牌などの (プログラム内部で)一連の通し番号を順不当に並び替えた 配列をつくるのに使う。

/*---------------------------------------------------*/
static void inttbl_shuffle(int tbl[], int n)
{
    int i, j, t;

    /* まずは, 順番に値をテーブルに設定 */
    for (i = 0; i < n; i++) {
        tbl[i] = i;
    }
    /* n個の各要素ごとに 乱数で交換相手を選んで交換 */
    for(i = 0; i < n; i++) {
        j = rand() % n;
        t = tbl[i];
        tbl[i] = tbl[j];
        tbl[j] = t;
    }
}
/*---------------------------------------------------*/
2000-05-21



乱数(rand,srand)ルーチン


 (ゲームでの)乱数の用途は2パターンある。

  1. 全くのランダムに値がほしい場合 (やるたびに絶対に違うゲーム展開にしたい場合)
  2. 一定の間(対戦中など)はランダムだけど、 プレーヤの再挑戦などでやり直しのときに 前回と全く同じ状況(乱数)になってほしい場合

 まず、乱数ルーチンの例を見てみる。

/*---------------------------------------------------*/
static unsigned long rand_seed = 1;

int rand(void)		/* 乱数を発生 */
{
    rand_seed = rand_seed * 1103515245L + 12345;
    return (rand_seed >> 16) & 0x7fff;
}

void  srand(int seed)	/* 乱数の種を設定 */
{
    rand_seed = (unsigned long)seed;
}
/*---------------------------------------------------*/
 これは「C言語によるアルゴリズム辞典」から抜き出して 使ってたもんで実際のCコンパイラ(ライブラリ)がこれと同 じかどうかは別として、似たような性質のものは 多いとは思われる。

 なんでこんな式でいいのか理屈等は上記本なり専門書を 見てもらうとして(あたしにゃよーわからん)、 rand()は前回の乱数の種(rand_seed)の値に掛けたり足したり した結果を今回の値にして、その一部を乱数値として返している。

 srand(seed) は乱数の種(rand_seed)を設定しているだけ。

 rand()は乱数を返す関数だけれど、上記のように関数 で計算式にそった値を順に吐き出すんで、同じ初期値(乱数の種) から始めると、必ず同じ順に乱数が生成される(さらにいうと ずっと乱数を発生させていると、いつかはまた同じ種になり 同じ乱数を発生する周期関数になっている)。

 A.の場合のような(ゲームなどで)プログラム起動のたびに 同じ乱数が発生すると不都合な場合、まずは乱数の初期値を 時計(タイマー)など比較的不定な値を用いて設定する。 たとえばWinとかだと起動時のルーチンで

ti = timeGetTime(); // ※clock()やtime(NULL)等でも.
srand(ti);
などとする。

 ただ初期設定だけだと一定期間出目を観察して 乱数の種を類推し、以後の出目を予測される可能性 が0ではない (まずそんなことしないだろうが…… rand()の質もあるのだろうけれど 大昔(ソース公開の)ネットゲームであったらしい)。

 実際にはさらに、ユーザ操作があるプログラムの場合 ある程度の期間ごとに通過するルート、 例えば Windows ならば WinMain でのメッセージ(待ち)ループ中 (ゲーム機等ならばvsync同期の(メイン)ループ中)や タイマー割り込み、また、ユーザのキー操作判定ルーチンなどで rand() を呼び出すことで、実質バラバラにできる (というか、ばらばらになるような個所で rand() を呼び出す)。
 起動時のsrand()での初期化で時計がない(不定な値が得られない)場合でも この方法により、例えばゲームでタイトル画面の PUSH START BUTTON 待ちや キャラセレクトなどの操作の間に何度も rand() が使われ 実際の乱数取得でバラバラな値が得られる。

 B.は 逆に(神経衰弱やソリティアなど)ゲームの再挑戦等で 前回と全く同じ状況(乱数)を作りたい場合で、 再開する個所で 一旦 srand()で初期化することにし、 次回やり直すときは前回と同じ値(種)を与えればよい。

 育成ゲームやSLGなどで成長や移動に乱数を用いる場合でも、 上手く乱数を使えば、乱数の種(srandに渡す値)と 経過シーン数(何日目等)や 各シーンでのユーザ選択要素を控えておくだけで、 ロード時に再計算で状況を復元できるのでセーブデータを減らせるし、 場合によってはシーンの後戻りも可能だろう。

 もちろん B のような場合、全くの乱数を得る場合とは逆に 時間や不定なキー操作で乱数発生をしてはいけない。
 しかし、実際には、再現可能な乱数がほしい場合と、全くの乱数 が一つの(ゲーム)プログラム中でほしい場合がある。

 この場合どうするかというと、もう一つ別の乱数ルーチンを 用意して済ます。
 上記したようなルーチンを my_rand(), my_srand() なりの別の名で用意し、用途に合わせて使い分ける (標準ライブラリのを全くの乱数用に、自前のを 再現性有用に用いるのが無難だろう)。


※ 乱数ルーチン自体に関しては、乱数表をなめるやり方も 有るだろうし、こちら のような非常に周期の大きいルーチンも作られている。


※ 標準関数の rand() だけれども言語仕様上は 返す値は少なくとも 0〜0x7fff までを返すとなっていて、 たとえ int が 32ビットだったとしても コンパイラ(ライブラリ)によって 0x7fff までの値を返すモノ もあれば、より大きな範囲(例えば0〜0x7fffffff) を返すモノもあり、 rand()は int の範囲を返すわけでないので、 勘違いをしていると痛い目に会う場合もある(^^;)

2000-05-21



自前の printf 系ルーチン(vsprintfの使用例)


 C の printf に慣れた人間にとっては、printfの書式での 表示があると作業しやすい。  標準出力以外、例えばゲームのメッセージ表示とか、デバッグ出力とかでも 使いたくなる。
 自前の1行表示ルーチンと sprintf と一時バッファを用意して sprintfを用いて自前の1行表示ルーチンに渡せばなんとかなるけれど、 そこらじゅうに一時バッファとsprintfが挟みこまれるとソースの可読性が 悪くなることも多い(デバッグに用いるととくに)。
 使用量が増えてきたら、専用にprintfを用意したほうが断然楽になる。
 自前で printf を作ろうと思うと、可変引数の処理が大変でかなり苦労するハメに なるのだけれど、実際には標準ライブラリのほうで、そのようなルーチンを 作るためのライブラリ(関数やマクロ)が用意されて、結構お手軽だ。
 作り方はだいたい決めうちになる。

/*---------------------------------------------------*/
#include <stdio.h>
#include <stdarg.h>

void gr_xyc_printf(int x, int y, int co, char *fmt, ...)
{
    char buf[256];
    va_list app;

    va_start(app, fmt);
    vsprintf(buf, fmt, app);
    va_end(app);
    gr_xyc_puts(x, y, co, buf);    // 自前の1行文字列表示ルーチン
}
/*---------------------------------------------------*/
 これだけ。
 va_start,va_end はマクロ。vprintf や vsprintf はマニュアルで 見かけるわりに何に使うかわかりにくい関数だけれど、実は このような使い方をするためにある。

 使用上の注意としては、一時バッファを用意する必要があるので (ファイルにたいしてなら vsprintfでなくvfprintfを用いれば回避できるけれど) 生成される文字列が buf を溢れないように、利用者に気をつけて 使ってもらう必要がある。便利さに慣れてprintfと同等のつもりでいると、 つい、バッファ溢れバグのことを忘れてハマることが結構ある。 もし vsnprintf() があるならば vsprintf() の代りに使って、 bufへの書き込みサイズ制限をしたほうが断然よいだろう。

2000-05-21
2000-12-29改



自前の printf 系ルーチン 補足(winでprintfデバッグ)


 コマンドラインなツールだとprintf()などでデバッグメッセージを表示 するのはよくやることである。もちろんデバッガを使うこともあるけれど printf()のほうがお手軽なんでそれですましてしまうこともままある。

 Windowsアプリでのデバッグだと基本的にデバッガを使うことになってくる。 でも状況によってはデバッガで複数のブレークポイント張るより も printf で複数箇所網張るほうが楽チンなこともある。 DirectX を使用してたりスクリーンセーバのデバッグだったりすると、 デバッガではラチあかん場合もある。

で自前の printf系ルーチンを用意してみる。

/*---------------------------------------------------*/
#include <stdio.h>
#include <stdarg.h>

void dbg_printf(char *fmt, ...)
{
    FILE *fp;
    va_list app;

    fp = fopen("dbg.txt", "at");
    if (fp) {
        va_start(app, fmt);
        vfprintf(fp, fmt, app);
        va_end(app);
        fclose(fp);
    }
}
/*---------------------------------------------------*/
 画面でなくファイル(dbg.txt)に吐き出すので その場で視認できないし速度的にも遅い(しかも使うたびに ファイルのopen/closeをするのでさらに…… しかし下手にopenしっぱなしだとファイルをロックしたまま プログラムが以上終了した場合に困る)。
 まあ速度的な件のデバッグではもちろん使わない。用途次第。

 実際には、

/*---------------------------------------------------*/
void dbg_printf(char *fmt, ...)
{
#ifndef NDEBUG
  《同上》
#endif
}
/*---------------------------------------------------*/
だったり
/*---------------------------------------------------*/
/* ….h */
#ifdef NDEBUG
#define DBGF(x)
#else
#define DBGF(x)  dbg_printf x
#endif

/* ….c */
#ifndef NDEBUG
void dbg_printf(char *fmt, ...)
{
  《同上》
}
#endif
/*---------------------------------------------------*/
のようにしてデバッグ時とリリース時で使い分けたりする。
ちなみに2めの例では、DBGF(("@c=%02x\n",c));のような使い方になる。気持ち悪いかもだけど。
2000-05-21



複数バイトデータの検索(BM法)


 BM法を用いた複数バイトデータのサーチ.
BM法は検索単語の長さを利用して高速化を図った検索アルゴリズム (単純な頭から1バイトづつ比較する検索ルーチンよりかなり速くなる可能性大)。

/*----------------------------------------------------------*/
void *MemSearch(void *area0, int asiz, void *ptn0, int ptnlen)
{
    /*指定範囲のメモリから複数バイトのデータを検索し見つかったアドレスを返し無ければNULLを返す。*/
    /* area0, asiz : 検索開始アドレスとその範囲     */
    /* ptn0, ptnlen: 検索データとそのサイズ(byte数) */
    int skp[256];
    int i, j, k;
    int c, tail;
    unsigned char *area = area0, *ptn = ptn0;

    if (ptnlen <= 0 || asiz <= 0) {
        return NULL;
    }
    tail = ptn[ptnlen - 1];             /* 最後の文字(1byte) */
    if (ptnlen == 1) {                  /* 長さ1なら簡単! */
        for (i = 0; i < asiz; i++) {    /* 単純に同じ1byteが見つかるまで検索 */
            if (area[i] == tail)
                return area + i;        /* みつかった */
        }
    } else {                            /* 長さ2以上のとき表引き */
                                        /* skp[]に、その文字が検索文字外のときに次の検索開始位置へスキップするバイト数を設定する */
        for (i = 0; i < 256; i++)       /* まず256文字すべて不一致の場合として検索データサイズ分のスキップ値を設定 */
            skp[i] = ptnlen;
        for (i = 0; i < ptnlen-1; i++)  /* 次に検索データを構成する文字の場合は、その文字より後ろの検索データのバイト数分をスキップにする */
            skp[ptn[i]] = ptnlen - 1 - i;
        /* i = ptnlen - 1; */           /* for文の結果がこの式に等しいから不要 */
        /* 指定範囲の検索開始! */
        while (i < asiz) {
            c = area[i];                /* 検索データの尾穴の1byteをチェックする. */
            if (c == tail) {            /* ここで違えば、(最大)検索データバイト数は、比較せずスキップできる */
                j = ptnlen - 1;         /* 同じならば、さらに検索データを後ろから比較する */
                k = i;
                while (ptn[--j] == area[--k]) {
                    if (j == 0)         /* 検索データの先頭ならば */
                        return area + k;/* 見つかった */
                }
            }
            i += skp[c];                /* その位置でマッチしなかったので、次の位置までスキップ */
        }
    }

    /* 見つからなかった */
    return NULL;
}
/*----------------------------------------------------------*/
 例によって「C言語によるアルゴリズム辞典」のを改造利用。 元は探すべきデータが文字列指定だったのをメモリとバイト数指定に変更。
あと、
char *StrStr(char *dat, char *key)
{
    /* strstr(d,s); の代り */
    return MemSearch(dat, strlen(dat), key, strlen(key));
}
のように標準のstrstr(d,s)の代りになるかも(メリットがあるかどうかはライブラリしだい)。
2000-05-24



整列済みのデータのテーブルから指定のデータを探す(ニ分岐検索)


 ニ分岐検索で、整列済の文字列のテーブルからキー文字列を探し、見つかったらば そのテーブルの添え字番号を返す。

/*----------------------------------------------------------*/
int stbl_search(char *tbl[], int num, char *key)
{
    /* tbl:整列済の文字列へのポインタをおさめた配列 */
    /* num:tblの文字列の数                          */
    /* key:検索する文字列                           */
    /* 戻り値:見つかった文字列の番号(0〜)  なければ負 */
    int     low, mid, f;

    low = 0;
    while (low < num) {
        mid = (low + num - 1) / 2;          /* まず、テーブルの真中を選ぶ */
        f = strcmp(key, tbl[mid]);          /* 探す文字列が真中の文字列より */
        if (f < 0)                          /* 小さければ次回は小さい方半分を対象 */
            num = mid;                      /*  真中の番号を次回の範囲の最大値+1に */
        else if (f > 0)                     /* 大きければ次回は大きい方半分を対象 */
            low = mid + 1;                  /*  真中の番号+1を次回の範囲の最小値に */
        else                                /* 同じであれば */
            return mid;                     /* 見つかった */
    }
    return -1;                              /* みつからなかった */
}
/*----------------------------------------------------------*/
 基本的に、テーブルが固定のときのみに使う (テーブルの要素が追加や削除で変動するときは別の木などを利用する)。

 呼び出し例は以下の感じ……

/*----------------------------------------------------------*/
/* 例 */
static char *optbl[] = {	/*ソート済のテーブル */
    "else",
    "gosub",
    "goto",
    "if",
    "set",
    "then",
};

#define OP_NUM          (sizeof(optbl)/sizeof(optbl[0]))  /*テーブル中の単語の数 */
                                                          /*全体のバイト数÷一要素のバイト数*/
void foo(char *key)
{
  (略)
    n = stbl_search(optbl, OP_NUM, key);
    if (n < 0)
        printf("%s が見つからない\n", key);
  (略)
}
/*----------------------------------------------------------*/
 テーブル作成は、一旦、単語一覧を作って、それを sort.exe(sortf) などでソートし エディタのキーボードマクロでちょろちょろっと整形ちちゃうのが多いかな。

 あと、文字列じゃなく構造体にしたり、比較関数をstrcmpでなく 自前で用意したりして、用途に合わせて、ちょっとづつ書き換えて 用いたりしている。

 あまりにプログラムの基本中の基本ルーチンだけれど…… なぜか、この程度も、めんどくさがったり難しいと敬遠して、 線形検索で済ます人も結構いるような……

2000-05-25



CRC を使う(チェックサムのかわりに……)


 データのエラーチェックなどに用いる CRC を求めるルーチン(の一例)。

/*----------------------------------------------------------*/
typedef unsigned char uint8_t;
typedef unsigned      uint32_t;     //32ビット符号無整数型. 32bit以上のCPU。longが64bitの場合ありなので。
//typedef unsigned long uint32_t;   //32ビット符号無整数型. 16bit以下のCPU.

static uint32_t crctable[256] = {
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};

uint32_t MemCrc32(void *dat, int siz)
{
    //アドレスdatからsizバイト数分をうまく計算して、メモリ内容が
    //少しでも違えば、(なるべく)異なる(CRC)値を返す。
    uint8_t *s = (uint8_t*)dat;
    uint32_t r;

    r = 0xFFFFFFFFUL;
    while (--siz >= 0)
        r = (r >> 8) ^ crctable[((uint8_t)r) ^ *s++];
    return r;    /* 場合によっては r ^ 0xFFFFFFFFUL */
}
/*----------------------------------------------------------*/
 例によって「C言語によるアルゴリズム辞典」のを改造利用。 説明や特性についてはその本や他の文献なりで確認してね、と。 型や名前は都合で書き換えて、だし。

 CRC(巡回助長検査)の方法はいろいろあるけれど上記はそのうちの一種 (32ビットでなく 16ビットとか 24ビットなどもあるし、同じビット数でも 違う式のものもある)。

 用い方は例えば、 作成するデータファイルのCRC値を求めファイル末に余分に付加しておき、 読み込んだ側で CRC を計算してファイル末の値と等しいかをチェックして 違えばファイルが壊れている、と判断できる(*1).

 もちろん同じ CRC になったからといって間違いがない、とは言えないのだけれど (サイズが大きければ大きいほど同じ値になる率は増える訳だし)、 チェックサムに比べれば断然エラーチェックは厳しくなるので よほど速度やメモリに不自由してないなら CRCを用いたほうがお得でしょう。 見ての通りルーチンもそんな大きくなし (ちょっとテーブルはでかいけど4*256=1Kバイトな訳だし) (*2)。

 ようは、この程度のことめんどくさがらずに CRC を使おうよ、て話かな。


(*1)このルーチンの値の場合4バイトをリトルエンディアンで データの最後に付加すると、読み込んだ側で CRC 4バイトを含めて CRC計算をすれば、異常がなければ(CRC)値は 0になります…… て、イマイチ自信がないのでちょっと試す( テストルーチン ... これしたかったので上記ではビット反転せず値を返しているけれど ルーチンとしては反転して返しておいたほうがいいような気もする)。

(*2)参考書でテーブル生成ルーチンを見ちゃうと、つい、ややこしそう/ めんどくさそう、と思ってしまうかもだけど、計算しちゃっとけば 利用する分には上記のとおりですむ、ってわけで…… まあ、意味不明な数字の配列に化けちゃってて、アルゴリズムの 知りたい方にはこれでは不幸なんですが(いちおうテーブル生成ルーチンは これ)。

2000-05-31

[追加]

 ファイルのケツにCRC 4バイト追加するプログラム もあったので置いておきます。

2000-06-02



#if,#define,#include,#macroなどの展開機能付き1行入力ルーチン


 ネタ切れぎみにつき拙作の、コンパイラやアセンブラのプリプロセス向きの入力ルーチン。 #if,#define,#include当の C のプリプロセッサの機能に加え、#macro,#rept などのアセンブラのマクロ機能のようなものもあります。  結構ソースもでかく、でもって実際に拙作 acpp / dcasm で使っているので ソースや詳しい説明は、その中に入っているのを 見たり抜きだしてやってください。 該当ソースは filn.c, filn.h です。

 acppやdcasm自体がサンプルでもあるんだけれども、もっと単純な例として

/*----------------------------------------------------------*/
#include <stdlib>
#include "filn.h"

int main(int argc, char *argv[])
{
    char *p;

    if (argc < 2) {                 /* 引数の名前がなかったらヘルプ表示 */
        printf("usage> %s filename\n", argv[0]);
        return 1;
    }
    if (Filn_Init() == NULL) {      /* ソース入力ルーチンの初期化 */
        printf("メモリが足りません\n");
        return 1;
    }
    if (Filn_Open(argv[1]) < 0) {   /* ソースファイルオープン */
        return 1;
    }
    for (;;) {                      /* 入力がなくなるまで繰り返す */
        p = Filn_Gets();            /* マクロ展開済み1行入力. メモリをmallocして返す*/
        if (p == NULL)
            break;
        printf("%s", p);            /* 1行を標準出力 */
        free(p);                    /* 入力用に確保されたメモリは不要なので開放 */
    }
    return 0;
}
/*----------------------------------------------------------*/
たとえばこれを filn_tst.c としてセーブし、acpp|dcasmから filn.c, filn.h をとってきて同じフォルダにおき、
>bcc32 filn_tst.c filn.c
とすると実行ファイルができるので(警告が残ってますが勘弁してやってください^^;)、
>filn_tst filn_tst.c
でもして試してみてください (stdlib.hやstdio.hを読みこめないって怒られますが、filn.hをincludeした結果が 表示されます)。

※問題は、このルーチンは(修正後)まだあまり使い込まれていないので バグが多数?あるかも。

2000-09-27



[次] [戻る]