2

このコードによって生成されたハッシュ テーブルをクリーンアップする関数を作成しようとしています。

/*
* Markov chain random text generator.
*/

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "eprintf.h"

enum {
NPREF   = 2,    /* number of prefix words */
NHASH   = 4093, /* size of state hash table array */
MAXGEN  = 10000 /* maximum words generated */
};

typedef struct State State;
typedef struct Suffix Suffix;

struct State {  /* prefix + suffix list */
char*   pref[NPREF];    /* prefix words */
Suffix* suf;            /* list of suffixes */
State*  next;           /* next in hash table */
};

struct Suffix { /* list of suffixes */
char*   word;           /* suffix */
Suffix* next;           /* next in list of suffixes */
};

State* lookup(char *prefix[], int create);
void build(char *prefix[], FILE*);
void generate(int nwords);
void add(char *prefix[], char *word);

State* statetab[NHASH]; /* hash table of states */

char NONWORD[] = "\n";  /* cannot appear as real word */

/* markov main: markov-chain random text generation */
int main(void)
{
int i, nwords = MAXGEN;
char *prefix[NPREF];        /* current input prefix */

int c;
long seed;

setProgName("markov");
seed = time(NULL);

srand(seed);
for (i = 0; i < NPREF; i++) /* set up initial prefix */
    prefix[i] = NONWORD;
build(prefix, stdin);
add(prefix, NONWORD);
generate(nwords);
return 0;
}

const int MULTIPLIER = 31;  /* for hash() */

/* hash: compute hash value for array of NPREF strings */
unsigned int hash(char* s[NPREF])
{
unsigned int h;
unsigned char *p;
int i;

h = 0;
for (i = 0; i < NPREF; i++)
    for (p = (unsigned char *) s[i]; *p != '\0'; p++)
        h = MULTIPLIER * h + *p;
return h % NHASH;
}


/* lookup: search for prefix; create if requested. */
/*  returns pointer if present or created; NULL if not. */
/*  creation doesn't strdup so strings mustn't change later. */
State* lookup(char *prefix[NPREF], int create)
{
int i, h;
State *sp;

h = hash(prefix);
for (sp = statetab[h]; sp != NULL; sp = sp->next) {
    for (i = 0; i < NPREF; i++)
        if (strcmp(prefix[i], sp->pref[i]) != 0)
            break;
    if (i == NPREF)     /* found it */
        return sp;
}
if (create) {
    sp = (State *) emalloc(sizeof(State));
    for (i = 0; i < NPREF; i++)
        sp->pref[i] = prefix[i];
    sp->suf = NULL;
    sp->next = statetab[h];
    statetab[h] = sp;
}
return sp;
}

/* addsuffix: add to state. suffix must not change later */
void addsuffix(State *sp, char *suffix)
{
Suffix *suf;

suf = (Suffix *) emalloc(sizeof(Suffix));
suf->word = suffix;
suf->next = sp->suf;
sp->suf = suf;
}

/* add: add word to suffix list, update prefix */
void add(char *prefix[NPREF], char *suffix)
{
State *sp;

sp = lookup(prefix, 1);  /* create if not found */
addsuffix(sp, suffix);
/* move the words down the prefix */
memmove(prefix, prefix+1, (NPREF-1)*sizeof(prefix[0]));
prefix[NPREF-1] = suffix;
}

/* build: read input, build prefix table */
void build(char *prefix[NPREF], FILE *f)
{
char buf[100], fmt[10];

/* create a format string; %s could overflow buf */
sprintf(fmt, "%%%ds", sizeof(buf)-1);
while (fscanf(f, fmt, buf) != EOF)
    add(prefix, estrdup(buf));
}

/* generate: produce output, one word per line */
void generate(int nwords)
{
State *sp;
Suffix *suf;
char *prefix[NPREF], *w;
int i, nmatch;

for (i = 0; i < NPREF; i++) /* reset initial prefix */
    prefix[i] = NONWORD;

for (i = 0; i < nwords; i++) {
    sp = lookup(prefix, 0);
    if (sp == NULL)
        eprintf("internal error: lookup failed");
    nmatch = 0;
    for (suf = sp->suf; suf != NULL; suf = suf->next)
        if (rand() % ++nmatch == 0) /* prob = 1/nmatch */
            w = suf->word;
    if (nmatch == 0)
        eprintf("internal error: no suffix %d %s", i, prefix[0]);
    if (strcmp(w, NONWORD) == 0)
        break;
    printf("%s\n", w);
    memmove(prefix, prefix+1, (NPREF-1)*sizeof(prefix[0]));
    prefix[NPREF-1] = w;
}
}

これが私のきれいな機能のためにこれまでに持っているものです

/*Clean Function*/
void clean_up(State *sp)
{
State *temp;
Suffix *temp2, temp3;

for(int h = 0; h < NHASH; h++)
{
    for (sp = statetab[h]; sp != NULL; sp = sp->next)
    {
        while(sp->suf != NULL) 
        {
            temp2= sp->suf;
            temp3= *temp2->next;
            free(temp2);
            sp->suf= &temp3;
        }

    }
}
}

私は正しい軌道に乗っていると思います。ハッシュ テーブルの各インデックスを調べてから、状態から状態へと移動し、接尾辞を解放します。各状態を解放する前にプレフィックスを解放する必要があるため、プレフィックスをどうすればよいかわかりません。どんな助けでも大歓迎です。

4

2 に答える 2

1

あなたのコードでは、自動メモリ (「スタック上」) に存在する temp3 ノードにコピーしています。 sp->suf がこのメモリを指すと、(ループの次の繰り返しで) free がアドレスで呼び出されます。このオブジェクトの ( malloc によって取得されていないため、 free() によって解放することはできません)

void clean_up(State *sp)
{
State *temp;
Suffix *temp2, **pp;

for(int h = 0; h < NHASH; h++)
{
    for (sp = statetab[h]; sp != NULL; sp = sp->next)
    {
        for (pp = &sp->suf; *pp; *pp = temp2) 
        {
            temp2 = (*pp)->next;     
            free(*pp);
        }

    }
}
}
于 2012-11-04T19:23:43.633 に答える
0

サンプル コードは、Kernighan と Pike によるThe Practice of Programmingの Markov プログラムから派生したもので、非常に優れた本です。

をクリーンアップしようとしているstatetab場合、メインのクリーンアップ関数には引数は必要ありません。で状態を直接解放しないように注意するstatetab必要がありますが、連鎖している補助状態を解放する必要がありますstatetab[i].next

typedef struct State State;
typedef struct Suffix Suffix;

struct State {  /* prefix + suffix list */
char*   pref[NPREF];    /* prefix words */
Suffix* suf;            /* list of suffixes */
State*  next;           /* next in hash table */
};

struct Suffix { /* list of suffixes */
char*   word;           /* suffix */
Suffix* next;           /* next in list of suffixes */
};

State* statetab[NHASH]; /* hash table of states */

static void free_state(State *state);
static void free_suffix(Suffix *suffix);

static void cleanup(void)
{
   for (int i = 0; i < NHASH; i++)
      free_state(statetab[i]);
}

static void free_state(State *state)
{
    if (state != 0)
    {
        for (int i = 0; i < NPREF; i++)
            free(state->pref[i]);
        free_suffix(state->suf);
        if (state->next != 0)
        {
            free_state(state->next);
            free(state->next);
        }
    }
}

static void free_suffix(Suffix *suffix)
{
    if (suffix != 0)
    {
        free(suffix->word);
        free_suffix(suffix->next);
        free(suffix);
    }
}

構造free_xxxx()の設計に基づいてコードをどのように設計したかがわかりますか?xxxx

注意事項: コンパイルされていないコードであり、テスト済みのコードではありません。


TPOPサイトからコードを掘り起こし、適用してみました。上記の解放コードにいくつかの修正を加えました (構文エラーが修正され、null チェックインfree_state()およびfree_suffix()) が、コード全体として、データを解放できるように設計されていませんでした。

いくつかの問題があります。まず、いくつかのプレフィックスが割り当てられていません (NONWORD)。プレフィックスが NONWORD であるかどうかをテストすることで、それらのリリースを回避できる可能性がありますが、それは厄介です。これらのプレフィックスも割り当てることができる場合があります (NONWORD を に置き換えますestrdup(NONWORD))。どこかに、割り当てられていないポインターが状態テーブルのプレフィックスに隠されている別の場所があると思います。「割り当てられていないメモリを解放する」という不平を言うとクラッシュしmalloc()ます(これは「割り当てられたメモリを二重に解放する」とは異なると思います)が、それを解決することはできませんでした。

ただし、それは別の問題に変わります。プレフィックスは再利用されます。つまり、システム内のほぼすべての接頭辞が、1 つの接頭辞の 2 番目の単語として使用され、次に次の接頭辞の最初の単語として使用されます。したがって、プレフィックスを簡単に解放することはできません。

メモリを解放できるようにこれを設計する場合は、おそらく、各単語が一度割り当てられ、必要に応じて再利用されるような「アトム」(不変文字列) のシステムが存在するように設計するでしょう ( C Interfaces and Implementations: Techniques for Making Reusable Code by D Hanson の用語の出典)。状態テーブルを解放するコードは、非単語データのみに集中します。アトムの完全なセットを解放するコードもあるでしょう。

valgrindクリーンアップなしでMarkov プログラムを実行しました。メモリ アクセスの問題やデータの漏洩はありません。プログラムの終了時にすべてアクセスできます。私は約 15,000 語 (および約 2,900 語) のデータ ファイルを使用しており、統計は次のとおりです。

==9610== HEAP SUMMARY:
==9610==     in use at exit: 695,269 bytes in 39,567 blocks
==9610==   total heap usage: 39,567 allocs, 0 frees, 695,269 bytes allocated
==9610== 
==9610== LEAK SUMMARY:
==9610==    definitely lost: 0 bytes in 0 blocks
==9610==    indirectly lost: 0 bytes in 0 blocks
==9610==      possibly lost: 0 bytes in 0 blocks
==9610==    still reachable: 695,269 bytes in 39,567 blocks

それで、あなたは自分自身に興味深い演習を設定しました。ただし、データをきれいに解放できるように、メモリ割り当てメカニズムの一部を作り直さないと実現できないと思います。

(BSD では、したがって Mac OS X でも、関数のペアが<stdlib.h>calledsetprogname()と にありgetprogname()ます。BSD では、setprogname()が開始する前に自動的に呼び出されますmain()(とargv[0]私は信じています)。 の宣言は の宣言とeprintf.h衝突し<stdlib.h>ます。問題のコードがsetProgName()元の の代わりに使用する理由です.引数を取るようsetprogname()に修正することを選択しsetprogname()たため、 の宣言と一致しました.)eprintf.hconst char *<stdlib.h>


TPOP は以前は http://plan9.bell-labs.com/cm/cs/tpophttp://cm.bell-labs.com/cm/cs/tpopにありましたが、現在は両方とも (2015-08-10)壊れた。ウィキペディアのTPOPも参照してください。

于 2012-11-04T20:33:27.870 に答える