2

衝突の問題を解決するためにリンクされたリストを使用してハッシュ テーブルを実装しようとしています。ハッシュ テーブルを初期化するためのコードでいくつかの問題に直面しています。セグメンテーション違反が発生します。問題が正確にどこにあるかを確認しようとして、valgrind を使用しました。このツールを使用すると、次の警告が表示されます。

「アドレス 0x8 は、スタック、malloc、または (最近) 解放されていません」

ハッシュテーブルを「編集」しようとするほとんどすべての場合。たとえば、サイズ、sth の挿入、削除などです。コードを何度も調べましたが、何が問題なのかわかりません。すべてを正しくmallocしてスタックしたと思いました。しかし、このメッセージでは、明らかに sth が間違っています。これに関するアイデアはありますか?

私のコード:

     //hash table structure 
    typedef struct HashTable 
    {
     int size;   //size of table with connections
     struct List **table; //table elements
    }HashTable;

    typedef struct List
    {
     char* number;
     struct List *next;
    }List;

    struct HashTable *initHashTable(int size)
    {
       struct HashTable *blankTable=(struct HashTable *)malloc(sizeof(struct HashTable));

       if (size<1) 
       {
            return NULL;
       }

       if ((blankTable=malloc(sizeof(HashTable)))==NULL) 
       { 
           return NULL; 
       }
       if ( (blankTable->table=malloc(size*sizeof(List))) == NULL) 
       { 
           return NULL;
       }
       int i;
       for (i=0; i<size; i++) //initializes hash table
       {
         blankTable->table[i]=malloc(sizeof(List));
         blankTable->table[i]=NULL;     //Valgrind :: Invalid write of size 8
       }
       blankTable->size = size;
       //printf("blankTable size:%d\n",blankTable->size);
       return blankTable;
   }

その他の注意: 次のコードを使用して、数値がハッシュ テーブルに既に存在するかどうかを検索します。私はこれをvalgrindから取得します:

サイズ 8 の無効な読み取り ==3773== 0x40110E: lookup(360) ==3773== アドレス 0x8 は、スタック、malloc、または (最近) 解放されていません

struct List *lookup(HashTable *hashtable,char *number)
{
 struct List *list= (struct List *) malloc (sizeof(struct List )); ;
 unsigned int hashval= hash(number);

 if ( (hashtable->table[hashval])!=NULL)  
 {
  for( list=hashtable->table[hashval]; list!=NULL; list=list->next)
  { if(strcmp(number,list->number)==0)  //SEGMENTATION!
   { 
    return list;
   } 
  }
 }
 return NULL;
}

テーブルのサイズを確認するために呼び出すと、セグメンテーションも取得されるという事実は、さらに心配です。これを呼び出す:

unsigned int size = Array[pos].TableHead->size;

Array[pos].TableHead は、hashTable の構造体へのポインターです。

編集:

valgring を実行すると、次のレポートが表示されます。

           Invalid write of size 8
==8724==    at 0x4016D2: initHashTable (hash.c:524)
==8724==    by 0x4019CE: main (hash.c:792)
==8724==  Address 0x5199180 is 8 bytes after a block of size 8 alloc'd
==8724==    at 0x4C25153: malloc (vg_replace_malloc.c:195)
==8724==    by 0x4016B6: initHashTable (hash.c:522)
==8724==    by 0x4019CE: main (hash.c:792)

==8724== Use of uninitialised value of size 8
==8724==    at 0x4C264C4: strcmp (mc_replace_strmem.c:412)
==8724==    by 0x4017A0: lookup (hash.c:551)
==8724==    by 0x401820: add(hash.c:566)
==8724==    by 0x401AAB: main (hash.c:817)
==8724== 
==8724== Invalid read of size 1
==8724==    at 0x4C264C4: strcmp (mc_replace_strmem.c:412)
==8724==    by 0x4017A0: lookup (hash.c:551)
==8724==    by 0x401820: add (hash.c:566)
==8724==    by 0x401AAB: main (hash.c:817)
==8724==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==8724== 
==8724== 
==8724== Process terminating with default action of signal 11 (SIGSEGV)
==8724==  Access not within mapped region at address 0x0
==8724==    at 0x4C264C4: strcmp (mc_replace_strmem.c:412)
==8724==    by 0x4017A0: lookup (hash.c:551)
==8724==    by 0x401820: add (hash.c:566)
==8724==    by 0x401AAB: main (hash.c:817)
==8724==  If you believe this happened as a result of a stack
==8724==  overflow in your program's main thread (unlikely but
==8724==  possible), you can try to increase the size of the
==8724==  main thread stack using the --main-stacksize= flag.
==8724==  The main thread stack size used in this run was 8388608.

これを読んで、私の番号にはヌルターミネータがないと最初に思いました。だから、私はそれを再初期化し、最後のインデックスにnullを追加しました。残念ながら、ご覧のとおり問題が残っています。最初の実行 (ルックアップ関数) で、数値をリストの数値と比較します。これは null です。セグメンテーションがあります。しかし、私はなぜさまよいます。NULL を返すことはできませんか?

ありがとうございました。

4

2 に答える 2

5
blankTable->table[i]=malloc(sizeof(List));
blankTable->table[i]=NULL;

リストアイテムにメモリを割り当ててからNULL(0x0)に設定します。

于 2010-11-27T17:42:16.520 に答える
2

次のおもちゃのプログラムは、投稿した構造体の定義と関数で正しく動作します (少なくともsegfault は発生しません)。

#include <stdlib.h>
#include <assert.h>

/* code from question omitted */

int main(void) 
{
    HashTable * hash = initHashTable(5);
    int i;

    assert(hash->size == 5);

    for ( i = 0; i < hash->size; i++ )
        assert(hash->table[i] == NULL);

    free(hash);

    return EXIT_SUCCESS;
}

上記は正しいと思います.NULLポインターは「空の」リストであると考えていますよね?(insertしたがって、関数は適切な NULL を新しいリストの最初の要素に置き換えます。)

もしそうなら、物事を大幅に簡素化することができます。上記のおもちゃのプログラムを valgrind でクリーンに実行するイニシャライザを作成することは可能です。この発見をごまかすつもりはありませんが、柔軟な配列とは何か、どのように機能するのかを調べてみてください。

初期化関数の問題を無視すると (valgrind は、アプリケーションが segfault を起こさなければ、何が問題なのかをかなり正確に教えてくれるはずです)、hash()ルックアップ関数の関数の境界は何ですか?

の値を読み取ろうとするhashtable->table[hash(number)]ためhashtable、少なくともその数の要素で初期化する必要があります。そうしないと、割り当てていないメモリから読み取ることになります (したがって、セグメンテーション違反)。

おそらくあなたが意味した

List * lookup(HashTable *hashtable, char *number)
{
    assert(hashtable != NULL);
    assert(number != NULL);

    unsigned int hashval = hash(number) % hashtable->size;
    List * list = hashtable->table[hashval];

    assert( list == NULL || list->number != NULL );

    while ( list != NULL && strcmp(number,list->number)!=0)
    {
        list = list->next;
        assert ( list == NULL || list->number != NULL );
    }

    return list;
}

更新: 投稿した valgrind ログから にヌル ポインターを渡すことができないstrcmpため、セグメンテーション エラーが発生します。上記のルックアップ関数は、これが起こらないことを確認するためにいくつかのアサートで更新されました。

また、valgrind は、初期化されていない値を に渡したというヒントを示しますstrcmp。これは null ポインターである可能性があります (初期化されていない値がたまたまゼロになった場合)。ただし、この質問に適切に答えるにはまだいくつかの重要な情報が不足しています。ファイル全体をhash.cどこかに投稿していただけませんか?

ファイルを読んだ後: 正直に言うと、そのコードにはいくつかの非常に深刻な問題があります:-) 手動のメモリ処理と初期化の概念をまだ理解していないと思います.Javaを最初に学んだ可能性はありますか? ?

いずれにせよ、ここに私が発見した問題を順不同で示します。

  1. では、グローバル変数ではなく、ローカル変数 MyArrayinit_array_for_antennas()を初期化します。ローカル変数を削除するだけでOKです。
  2. 静的変数とグローバル変数は暗黙的にゼロに初期化されるため、 のほとんどすべてinit_array_for_antennas()が冗長になります。
  3. いくつかの場所で、ポインターを「初期化」してmalloc()から、ポインターを実際の値に設定します。これは典型的なメモリ リークです。そのようなメモリへの参照を上書きするため、そのようなメモリを解放することはできません。
  4. ステートメントnumber[strlen(number)]='\0';は、まあ、冗長です。strlen() がどのように機能するかを考えてみてください。自分で確認する必要があります。
  5. まだ読んでいない場合は、アサーションとは何かを読んで、それらを使用して仮定を確認する方法を学んでください。コメントアウトするassert(pointer_you_dereference_later != NULL);のは間違っています。
  6. 文字列へのポインターが既にある場合は、内容をローカル配列にコピーして、その配列を基になる関数に渡す必要はありません。ポインターを渡すだけです。
  7. 一部の場所ではグローバル定数bucket_sizeを使用し、他の場所では hashtable 構造体フィールドsizeを使用します。これは、サイズ フィールドを実際のコンパイル時の定数にするか、実行時の値を適切に使用するかのいずれかで発生するのを待っているエラーです。
  8. 本当にスタイルの問題ですが、多くの構造体を人間が読める素敵な名前に型定義し、その結果、とにかく「構造体」キーワードを追加します。
  9. calloc()代わりに使用すると、malloc()メモリがゼロになります。たとえば、ポインターの配列を割り当てると、calloc()NULL に初期化されるため、明示的に行う必要はありません。

まだ他にもあるかもしれませんが、今夜はここまでです。リストの最初のポイントが実際の問題の原因だと思います:-)

于 2010-11-27T18:44:56.707 に答える