проблема с инициализацией хеш-таблицы - PullRequest
2 голосов
/ 27 ноября 2010

Пытаясь реализовать хеш-таблицу, используя связанные списки, чтобы решить проблему коллизий, я столкнулся с некоторыми проблемами с моим кодом для инициализации хеш-таблицы.Я получаю ошибку сегментации.Пытаясь понять, в чем именно проблема, я использовал вальгринд.С помощью этого инструмента я получаю предупреждение:

"Адрес 0x8 не является стековым, malloc'd или (недавно) free'd"

почти на каждом моемпопробуйте «отредактировать» хеш-таблицу.например, для размера , до вставки sth, до удаления и т. д. Я снова и снова просматривал свой код, но не могу найти, что не так.Я думал, что у меня есть malloc'd и сложил все правильно.Но с этим сообщением явно что-то пошло не так.Есть идеи по этому поводу?

Мой код:

     //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'd или (недавно) free'd

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?

Спасибо.

Ответы [ 2 ]

5 голосов
/ 27 ноября 2010
blankTable->table[i]=malloc(sizeof(List));
blankTable->table[i]=NULL;

Вы выделяете память для элемента списка и затем устанавливаете его на NULL (0x0).

2 голосов
/ 27 ноября 2010

Следующая игрушечная программа работает корректно (ну, не выполняет segfault *, по крайней мере, 1002 *) с определениями и функциями, которые вы опубликовали:

#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 должен довольно точно сказать вам, что не так, если у вас есть приложение, а не segfaulting), каковы границы для функции 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;
}

Обновления: вы не можете передать нулевой указатель на strcmp из журнала Valgrind, который вы опубликовали, что является причиной возникновения ошибки сегментации.Функция поиска, представленная выше, была обновлена ​​с некоторыми другими утверждениями, чтобы убедиться, что этого не происходит.

Кроме того, valgrind намекает, что вы передали неинициализированное значение в strcmp, это может быть нулевой указатель (если неинициализированное значение обнуляется).Тем не менее, по-прежнему не хватает важной информации, чтобы правильно ответить на этот вопрос. Не могли бы вы опубликовать весь файл hash.c где-нибудь?

После прочтения файла: я должен быть честным, у вас есть очень серьезныепроблемы в этом коде :-) Я думаю, вы еще не поняли концепцию ручной обработки и инициализации памяти - возможно ли, что вы сначала изучили Java?

В любом случае, вот проблемы, которые я определяю в произвольном порядке:

  1. В init_array_for_antennas() вы инициализируете локальную переменную MyArray, а не глобальные .Просто удалите локальную переменную, и все будет в порядке.
  2. Статические и глобальные переменные неявно инициализируются нулем, поэтому почти все в init_array_for_antennas() просто избыточно.
  3. В некоторых местах вы «инициализируете» указатели с помощью malloc(), а затем устанавливаете указатель на его фактическое значение.Это классическая утечка памяти;вы не сможете освободить такую ​​память, поскольку перезаписываете ссылку на нее.
  4. Оператор number[strlen(number)]='\0';, ну, просто избыточен.Подумайте о том, как работает strlen (), и вы сами должны это увидеть.
  5. Если вы еще этого не сделали, прочитайте, что такое утверждения, и узнайте, как их использовать, чтобы проверить свои предположения.Комментировать assert(pointer_you_dereference_later != NULL); просто неправильно.
  6. Если у вас уже есть указатель на строку, вам не нужно копировать содержимое в локальный массив, а затем передавать этот массив базовым функциям - просто передайте указатель!
  7. В некоторых местах вы используете глобальную константу bucket_size, а в других - поле структуры хеш-таблицы size.Эта ошибка просто ожидает, либо сделайте поле размера фактической константой времени компиляции, либо правильно используйте значение времени выполнения.
  8. На самом деле вопрос стиля, но вы вводите большую часть struct в красивые, понятные человеку имена, и, тем не менее, в любом случае добавляете ключевое слово struct.
  9. Если вы используете calloc() вместо malloc(), вы получите нулевую память.Например, выделение массива указателей с calloc() сделает их инициализированными в NULL, и вам не нужно делать это явно.

Что ж, возможно, есть еще кое-что, но на сегодня все.Я предполагаю, что первая точка в списке является причиной вашей реальной проблемы: -)

...