2

数日前、私はこの質問を投稿しました、そして、誰もが私に使用するように提案しましたvoid*、それは私がしました。それらのいくつかは私が世話をする必要があるいくつかのことも指摘したと思いますが、それらが正確に何であったかはわかりません。しかし、私はこれに関していくつかの問題を抱えています...

非常に大きいコードをすべて投稿するのではなく、重要だと思うものを投稿します。うまくいけば、あなたが私を助けてくれるのに十分です。

私のハッシュテーブル構造は次のようなものです。

typedef void * HashKey;
typedef void * HashValue;

typedef struct sHashItem {
    HashKey key;
    HashValue value;

    char status;
} HashItem;

typedef struct sHashTable {
    HashItem *items;

    int count;
    float load;
    int size;

    Bool (*compare)(HashKey, HashKey);
    unsigned (*hash)(void *);
} HashTable;

私の挿入関数の署名は次のようになります。

Bool hashInsert(HashTable * const table, HashKey key, HashValue value);

そして、その関数内のどこかで、ハッシュテーブルに空きバケットが見つかったら、次のようにします。

table->items[index].key = key;
table->items[index].value = value;
table->items[index].status = USED;
table->load = ++table->count / (float)table->size;

これにはいくつかの問題があります。

1)上記のように、フリーバケットの各キー/値ペアを、キー/値hashInsert関数の引数として渡されたのと同じポインターに設定しているだけです。すでにお気づきかもしれませんが、これは問題を引き起こします...たとえば、次のようなことをします。

char str[50];
scanf("%s%*c", str);
hashInsert(t1, (HashKey)str, (HashValue)5);
scanf("%s%*c", str);
hashInsert(t1, (HashKey)str, (HashValue)3);

また、入力が「KeyA」、次に「KeyB」の場合、両方のバケットキーとして「KeyB」が使用されます。キーだけでなく値にも同じことが当てはまります。これは、どのデータ型でもコードを完全にモジュール化するため、基本的に同じ型であるためです。

どうすればこれを解決できますか?

私の最初は、それを使用strdup(str)して関数に渡すことhashInsertです。それで問題は解決します。また、これはメインコードで処理されたmalloc()ため、値として渡す必要のある他のデータ型にも簡単に使用できます(キーはおそらく常に文字列または整数になります)。

しかし、この解決策には別の問題があります...

2)この割り当てられたメモリをどのように解放する必要がありますか?確かに、それは「ハッシュテーブルモジュールプログラマー」ではなく「メインプログラマー」によって割り当てられたので、「メインプログラマー」はメインコードでそれを解放する必要がありますよね?しかし、それは私にはモジュラーコードのようには見えません。

私のコードにはhashDestroy、割り当てられたすべてのメモリを解放する機能もあります。しかし、どうすればこの関数を使用してすべてを解放できますか?すべてのキー/値を繰り返し処理して使用することはできません。そもそもfree()それらの一部はmalloc'dプログラマーによるものではなく、解放する必要がないためです。

hashDestroy解放しなければならないものと解放してはいけないものをどのように見つけることができますか?

3)最後に、この問題をミックスに入れることもできると思います...ポイント1で、私の提案は、その特定の問題を使用strdup()またはmalloc「修正」することでしたが、それはあまりモジュール化されていないように見えます私に。このメモリ割り当ては、「メインプログラマ」によるメインコードではなく、ハッシュテーブルモジュールコードで行う必要があります。

私がこれを解決することをどのように提案しますか?つまり、データ型は何でもかまいません。を使用すると非常にstrdup()役立ちますが、文字列に対してのみ機能します。特定の構造またはintだけにメモリを割り当てる必要がある場合はどうなりますか?

大きな投稿で申し訳ありませんが、これらの質問はすべて関連していると思います。私のCの知識はそれほど極端ではないので、それらを理解するのに助けが必要です。私は最近そのことを知りましvoid*た...

4

5 に答える 5

2

うわー:これは完全にいくつかの答えが必要になります。ただし、必要になる重要なことの1つは、処理しているもののサイズです。voidポインターを使用することは問題ありませんが、受信するアドレスを持つオブジェクトの大きさを知る必要があります。

[...]誰もが私にvoid*を使用するように提案しました。[...]

私のハッシュテーブル構造は次のようなものです。

typedef void * HashKey;
typedef void * HashValue;

typedef struct sHashItem {
    HashKey key;
    HashValue value;
    char status;
} HashItem;

typedef struct sHashTable {
    HashItem *items;
    int count;
    float load;
    int size;

    Bool (*compare)(HashKey, HashKey);
    unsigned (*hash)(void *);
} HashTable;

size_t key_sz;にとのsize_t val_sz;メンバーが必要になる可能性がありHashItemます。ハッシュ関数ポインタは、ハッシュされるキーの大きさを知る必要があります。

私はHashKeyがどうあるべきかについて2つの考えを持っています。それはあなたがこのようなものをどのように使っているかに部分的に依存します。あなたが望むように見えます:

  • 私が選んだこの重要な価値を考えると、
  • それに関連付けられているこのデータを保存/返します。

その場合、おそらくハッシュ番号をHashItem;のどこかに格納する必要があります。これは、ハッシュ関数によって返される値です。明らかに符号なし整数です。compare関数(関数ポインター)のシグニチャーがどうあるべきかわかりません。HashKeyとsizeの値のペア、またはHashItemポインターのペアのいずれかを取る必要があるのではないかと疑っています。

私の挿入関数の署名は次のようになります。

Bool hashInsert(HashTable * const table, HashKey key, HashValue value);

そして、その関数内のどこかで、ハッシュテーブルに空きバケットが見つかったら、次のようにします。

table->items[index].key = key;
table->items[index].value = value;
table->items[index].status = USED;
table->load = ++table->count / (float)table->size;

これにはいくつかの問題があります。

1)上記のように、フリーバケットの各キー/値ペアを、キー/値hashInsert関数の引数として渡されたのと同じポインターに設定しているだけです。すでにお気づきかもしれませんが、これは問題を引き起こします...たとえば、次のようなことをします。

char str[50];
scanf("%s%*c", str);
hashInsert(t1, (HashKey)str, (HashValue)5);
scanf("%s%*c", str);
hashInsert(t1, (HashKey)str, (HashValue)3);

使用するための鍵void *は、アドレスを渡すことです。Cではキャストは不要です。物のサイズも渡す必要があります。したがって:

Bool hashInsert(HashTable * const table, HashKey key, size_t key_sz,
                HashValue value, size_t val_sz);

char str[50];
scanf("%s%*c", str);
int value = 5;
hashInsert(t1, str, strlen(str)+1, &value, sizeof(value));

内部的には、データをコピーします。内部NUL'\ 0'バイトが含まれていないことがわからないため、'strdup()'を使用しません。

また、入力が「KeyA」、次に「KeyB」の場合、両方のバケットキーとして「KeyB」が使用されます。キーだけでなく値にも同じことが当てはまります。これは、どのデータ型でもコードを完全にモジュール化するため、基本的に同じ型であるためです。

どうすればこれを解決できますか?

誰が何を所有し、コンテナがデータをコピーするかどうか(およびどのように)を定義する必要があります。C ++では、コンテナーは格納しているものすべてのコピーを作成します。

私の最初の考えは、strdup(str)を使用して、それをhashInsert関数に渡すことです。それで問題は解決します。また、これはメインコードで処理されたため、値として渡す必要のある他のデータ型にもmalloc()を簡単に使用できました(キーはおそらく常に文字列またはintになります)。

一般に、値もキーも文字列ではないため、「strdup()」は使用できません。それらが常に文字列である場合、なぜ「char*」の代わりに「void*」を使用しているのですか?

サイズがわかっている限り、値とキーをコピーすることを決定できます。

しかし、この解決策には別の問題があります...

2)この割り当てられたメモリをどのように解放する必要がありますか?確かに、それは「ハッシュテーブルモジュールプログラマー」ではなく「メインプログラマー」によって割り当てられたので、「メインプログラマー」はメインコードでそれを解放する必要がありますよね?しかし、それは私にはモジュラーコードのようには見えません。

私のコードには、割り当てられたすべてのメモリを解放するためのhashDestroy関数もあります。しかし、どうすればこの関数を使用してすべてを解放できますか?すべてのキー/値を繰り返し処理してfree()を使用することはできません。そもそも、それらの一部はプログラマーによってmallocされておらず、解放する必要がないためです。

hashDestroyが解放する必要があるものと解放してはならないものをどのように見つけることができますか?

できません。ポリシーを定義する必要があり、そのポリシーで破棄を実行できる場合にのみ、それを実行する必要があります。すべてをコピーすれば、簡単な時間を過ごすことができます。何もコピーしない場合は、別の簡単な時間(おそらく簡単)がありますが、リリースする必要があるものを追跡するための構造が必要なため、消費者は非常に時間がかかります-おそらくハッシュリスト...

3)最後に、この問題をミックスに入れることもできると思います...ポイント1で、私の提案は、strdup()またはmallocを使用して、その特定の問題を(別の問題を導入しながら)「修正」することでしたが、それもしません。私には非常にモジュール式に見えます。このメモリ割り当ては、「メインプログラマ」によるメインコードではなく、ハッシュテーブルモジュールコードで行う必要があります。

はい...それは基本的に私の推奨事項です。

私がこれを解決することをどのように提案しますか?つまり、データ型は何でもかまいません。strdup()を使用すると非常に役立ちますが、文字列に対してのみ機能します。特定の構造またはintだけにメモリを割り当てる必要がある場合はどうなりますか?

コピーは浅いコピーのみを行うことに注意してください。コピーする構造にポインターが含まれている場合、複製コードはポインターのみをコピーし、データをポイントすることはありません。

したがって、一般的なソリューションには、ある種のコピー機能が必要になります。アイテムのメモリを解放する「リリース」機能をユーザーに提供するように要求する必要がある場合があります。すでに完全に割り当てられているデータをユーザーに提供してもらう必要がある場合があります。検索関数が返すものを誰が所有しているかを考える必要があります-それはまだハッシュテーブルにあるのか、それとも削除されているのか。C ++ STLシステムをよく見てください。一般的に言って、それは優れた仕事をし、必要なものに基づいて要件をモデル化することは理にかなっているかもしれません。ただし、C++にはそれを支援するコンストラクタとデストラクタがあることを忘れないでください。

于 2010-03-07T05:10:59.643 に答える
2

すべてのデータをmallocし、クライアントがitem_free()ハッシュテーブルの初期化時に関数をハッシュ関数に登録できるようにします。そうすれば、それをどのように扱うかは「メインプログラマー」次第です。

于 2010-03-07T05:11:09.013 に答える
1

ハッシュテーブルで衝突を処理するための2つの一般的な解決策があります。

  1. 代わりに、次の空きバケットを使用してください。
  2. バケットにはリンクリストが格納されるため、同じバケットに複数のアイテムを格納できます。

いずれの場合も、すべての種類のデータがハッシュテーブルまたはハッシュテーブルのクライアントのいずれかによって割り当てられるため、発生しないものをいつ解放するかという問題が発生します。それでも興味がある場合は、そのジレンマに対する簡単な答えは、スマートポインタを使用することです。

于 2010-03-07T04:56:32.643 に答える
1

うーん、あなたの例で私が見ているのは、問題はハッシュテーブルの衝突ではなく(この問題もあるようですが)、テーブルに格納されているアイテムのメモリを管理する方法です。この種のことを行う標準的な方法は、データ構造(ハッシュテーブル)のユーザーに、テーブルに配置されるすべてのものにスペースを割り当てる作業を強制することだと思います。ハッシュテーブルはポインタについてのみ心配する必要があります。割り当てを行ってからデータ構造をコピーするとします。アイテムがhastableから削除されたときに、ユーザーはメモリの割り当てを解除する方法をどのように知ることができますか?

于 2010-03-07T04:58:38.193 に答える
0

ハッシュテーブルを実装するには、バケットのセットが必要です。また、複数の要素が同じバケットにハッシュできるため、各バケットにはリンクリストが必要です。

しますか

HashItem *items;

上記の2番目の要件を実行しますか?

あなたの説明から、それがそうであるかどうかは明らかではありません。

優れた例については、K&Rセクション6.6を参照してください。name=HashKeyおよびdefn=HashValueの リンク。代替テキストhttp://www.goldfish.org/books/The%20C%20Programming%20Language%20-%20K&R/pic64.gif

于 2010-03-07T04:57:55.323 に答える