-3

[name][number][amount] number は文字列として取得されます。strcmpで使用しています。問題は、セグメンテーション違反が発生することです。ほとんどの場合、strcmp がセグメンテーション違反に署名する場合、パラメーターの 1 つが null であるか、その「終了」('\0') が見つからないことを意味します。gdbで確認しましたが、これが問題かどうかはわかりません。見てください:

> (gdb) bt full
> #0  0x08048729 in lookup (hashtable=0x804b008, hashval=27, 
>     number=0x804b740 "6900101001") 
>         list = 0xffffffff
> #1  0x080487ac in add (hashtable=0x804b008, 
>     number=0x804b740 "9900101001", name=0x804b730 "Smithpolow",
> time=6943) 
>         new_elem = 0xffffffff
>         hashval = 27
> #2  0x08048b25 in main (argc=1, argv=0xbffff4b4) 
>         number = 0x804b740 "9900101001"
>         name = 0x804b730 "Smithpolow"
>         time = 6943
>         i = 2

コード:

        typedef struct  HashTable
        {
            int length;
            struct  List *head; 

        } HashTable;

        //(resolving collisions using chaining)
        typedef struct  List
        {
            char *number;
            char *name;
            int time;
            struct List *next;
        } List;

    int primes[]={17,29,51,79,163,331,673,1361,2729,5471,10949,21911,43853,87719,175447,350899};
    *int PrimesIndex=1;* **int PrimesIndex=0;**  **//changed.**


     HashTable *createHashTable(size)
    {
         HashTable *new_table = malloc(sizeof(*new_table)*size);

        if (new_table == NULL)
        {   return NULL;
        }

        int i=0;
        for(i; i<size; i++)
        {   new_table[i].length=0;
            new_table[i].head=NULL;
        }
        return new_table;
    }

    int hash ( HashTable *hashtable,char* number)
    {
        int hashval = 0;
        int i = 0;
        for ( i = 0; i < 10; i++)
        {   hashval = (hashval << 5)|(hashval >> 27);
            hashval += ( int)number[i];
        }

        return hashval % primes[PrimesIndex];
    }

         List *lookup ( HashTable *hashtable,int hashval,char number[10])
        {
         printf("NUMBER:%s\n",number);
          List *list=hashtable[hashval].head;
         for(list; list!=NULL; list=list->next){
           if (strcmp(number,list->number)==0)    
            return list;

         }
         return NULL;
        }


        int add ( HashTable* hashtable,char number[10],char* name,int time)
        {
             List *new_elem;
            int hashval=hash (hashtable,number);

            new_elem=hashtable[hashval].head;
            if(hashtable[hashval].length>0)
            {                   
                  if ((lookup (hashtable,hashval,number))!=NULL) {return 0;}    
            }

            if (!(new_elem=malloc(sizeof(struct  List)))){ return -1;}

            //insert values for the new elem
            new_elem->number=strdup(number);    
            new_elem->name=strdup(name);
            new_elem->time=time;

            hashtable[hashval].head=new_elem;
            new_elem->next=NULL;
            hashtable[hashval].length++;

            /* rehash existing entries if necessary */
            if(hashTableSize(hashtable)>= 2*primes[PrimesIndex])    
            {    
                 hashtable = expand(hashtable);
                 if (hashtable ==NULL){
                   return 0;
                 }

            }

            return 1;
        }

 HashTable* expand( HashTable* h )
{   printf("EXPAND \n");
     HashTable* new;
     List *temp;
    int n;
     List *node,*next;
    PrimesIndex++;
    int new_size= primes[PrimesIndex];      /* double the size,odd length */

    if (!(new=malloc((sizeof(  List*))*new_size))) return NULL;

    for(n=0; n< h->length; ++n) {
        for(node=h[n].head; node; node=next) {
            add (new, node->number, node->name,node->time);
            next=node->next;
            //free(node);
        }
    }
    free(h);
    return new;
}

そしてメイン:

  int main(int argc, char *argv[])  
    {
        char **token;
        FILE *delimitedFile;
        /*Here's an example of tokenizing lines from an actual file*/
        /*Open file for reading ("r"), and take a FILE pointer, 
          which you can use to fetch lines using fgets()*/

        my_hash_table = createHashTable(17);
        if(my_hash_table==NULL)
        {   return 1;
        }

        FILE * File2;
            if ( ( File2=fopen(" File.txt","r")) !=NULL ) 
            { // File.txt format:  [name number time]
                int li = 0;
                char *lin = (char *) malloc(MAX_LINE * sizeof(char));

                while(fgets(lin, MAX_LINE, File2) != NULL)
                {
                    token = my_linetok(lin, " ");
                    if(token != NULL)
                    {
          char* number ;
          char* name;
          int time;
          int i;
                        for(i = 0; token[i] != NULL; i++)
                        {
           name=strdup(token[0]);
           number=strdup(token[1]);
           time=atoi(token[2]);

           if (i==2)
           { int insertDone=0;
                 insertDone =add(my_hash_table,number,name,time);   

           } 
          }
          free(name); 
          free(number);
          free(token);

                    }
                    else 
             {
                        printf("Error reading line %s\n", lin);
                        exit(1);   
                    }
                }

            } 
            else 
            {
                printf("Error opening file \nEXIT!");
         exit(0);
            }

        return 1;
    }
4

3 に答える 3

3

ここでの根本的な問題は、17 個のバケットでハッシュテーブルを作成することです。

my_hash_table = createHashTable(17);

しかし、C 配列は 0 ベースであり、PrimesIndex0 ではなく 1 から始まるため、内部add()では、次の呼び出しが行われhash()ます。

int hashval=hash (hashtable,number);

は、0 から 16 までの数値ではなく、0 から 28 までの数値を返します。そのため、ある時点で、範囲外の値が に割り当てられhashval、後続のアクセスの 1 つが によってインデックス付けされますhashval

new_elem=hashtable[hashval].head;

初期化されていないメモリを読み取り、最終的に0xffffffff後で表面化するようなクレイジーなポインター値につながります。

解決策:に変更int PrimesIndex = 1;int PrimesIndex = 0;ます。

しかし、正直なところ、私が見逃している他の問題がある可能性があると思います. がある:

  • コメントで指摘したforループ内のwhileループの問題。main()
  • numberパラメータ toの疑わしい宣言lookup_on_Clients()
  • 関数が呼び出されることもあれば、lookup()( lookup_on_Clients()Oli が気づいたように) 呼び出されることもあります。
  • そして、(ソースを表示していない)が適切に機能するとは信じていませmy_linetok()ん-少なくとも、静的バッファを使用しない限りchar *、個々のトークンへのポインタを保持するために配列を割り当てる必要があります、決して解放されない - メモリリーク。
于 2011-01-16T13:32:48.790 に答える
2

にヌル ターミネータを入れる余地がありませんnumber。のサイズnumberを 10 文字に設定しましたが、数字が 10 桁で、\0 のスペースがありません。

編集:

私はあなたの更新されたコードを見ました。初期サイズ = 17 のハッシュテーブルを作成しましたが、hasval = 27 です。しかし、ハッシュテーブルのサイズを適切に拡張するコードがありません。

new_elem=hashtable[hashval].head;
if(hashtable[hashval].length>0) // <-- when hashval is out of array 
                                // hashtable[hashval] can have any value of length and head (not NULL)
于 2011-01-16T12:45:43.873 に答える
0

add()おそらく を呼び出すソースが実際には表示されず、バックトレースがの代わりにlookup_on_Clients()言及しているため、確信は持てませんが、私の診断は次のとおりです。lookup()lookup_on_Clients()

  • バックトレースによるとlist = 0xffffffff、これは間違いなく有効なアドレスではないためlist->name、SIGSEGV の原因となっているのはおそらくアクセスです。
  • numberパラメータ tolookup_on_Clients()が として宣言されているにもかかわらず、gdb は 10 桁の数字が含まれていることを示しているという事実にも悩まされていchar number[10]ます。これは、この引数を保持する変数が同じように宣言されていることを示唆しています。終端の 0 バイト。そして、それを呼び出しstrcmp()ているという事実は、ヌル終了文字列として扱っていることを意味するnumberため、渡される引数を保持する変数lookup_on_Clients()(numberおそらくadd()? で宣言されたローカル変数) は、サイズが の配列として宣言する必要があります。クラッシュを避けるために少なくとも 11。add()独自の引数を直接渡すだけでも安全です。これは、innumberを介して十分な大きさになるように動的に割り当てられるためです。strdup()main()ですが、それでも の宣言を変更しlookup_on_Clients()ます。
于 2011-01-16T12:45:05.050 に答える