1

ハッシュテーブルで検索することに基づいて単語のアナグラムを見つけるCで簡単なプログラムを書いています。私が持っているのはハッシュテーブルの配列です。ここで、単語は長さによってインデックスが付けられ、文字の合計によってハッシュされます。たとえば、a = 1、b=2などです。

私はまだCでの動的メモリ管理に精通しているので、この質問は非常に単純かもしれませんが、各ハッシュテーブル(およびその内部データ)にメモリを割り当ておよび割り当て解除する関数があります。

私はもともと単一のハッシュテーブルを使用してこのプログラムを作成しましたが、valgrindでプログラムを実行した後、メモリが正しく解放され、リークがないことがわかりました。ただし、ハッシュテーブルの配列を使用するようにプログラムを拡張すると、valgrindはリークの可能性を見つけ始めました(ただし、まだ到達可能であると報告されています)。配列内の各ハッシュテーブルは元々使用されていた割り当て解除関数を介して実行されていますが、なぜメモリがハッシュテーブルの配列で正しく解放されないのか混乱しています。

フルコードの要点フルコード

typedef struct Bucket Bucket;
typedef struct HashTable HashTable;

struct Bucket {
  char* data;
  Bucket *next;
};

struct HashTable {
  int size;
  Bucket **buckets;
};

int main(int argc, char const *argv[])
{

  // Allocate memory for array of hash tables
  HashTable** hash_array = (HashTable**) malloc(sizeof(HashTable*) * WORD_SIZE);
  for(i = 0; i < WORD_SIZE; i++) {
    hash_alloc(&hash_array[i], BUCKET_COUNT);
  }

  // Main logic here...

  // Free memory
  for(i = 0; i < WORD_SIZE; i++) {
    hash_dealloc(hash_array[i]);
  }
  free(hash_array);
  return 0;
}

ハッシュテーブル割り当て機能

void hash_alloc(HashTable** a, unsigned int size) {
  *a = (HashTable*) malloc(sizeof(HashTable));
  (*a)->buckets = (Bucket**) malloc(sizeof(Bucket*) * size);
  (*a)->size = size;
}

ハッシュテーブルの割り当て解除機能

void hash_dealloc(HashTable* a) {
  int i;
  Bucket* current, *temp;
  for(i = 0; i < a->size; i++) {
    current = a->buckets[i];
    while(current != NULL) {
      temp = current;
      free(temp->data);
      current = current->next;
      free(temp);
    }
    free(current);
  }
  free(a->buckets);
  free(a);
}

ハッシュテーブル関数に追加

void add_to_hash_array(HashTable** a, char* data) {
    // Removed some other logic for readability...

    replace_str(data, "\n", "\0");
    newNode->data = strdup(data);
    newNode->next = currentTable->buckets[index];
    currentTable->buckets[index] = newNode;
  } else {
    return;
  }
}

Valgrindの出力

==39817== 261,120 bytes in 128 blocks are still reachable in loss record 5 of 7
==39817==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==39817==    by 0x400A38: hash_alloc (main.c:73)
==39817==    by 0x4008B0: main (main.c:39)
==39817== 
==39817== 286,936 bytes in 31,553 blocks are still reachable in loss record 6 of 7
==39817==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==39817==    by 0x4EBAD71: strdup (strdup.c:43)
==39817==    by 0x400D4D: add_to_hash_array (main.c:141)
==39817==    by 0x400914: main (main.c:51)
==39817== 
==39817== 504,848 bytes in 31,553 blocks are still reachable in loss record 7 of 7
==39817==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==39817==    by 0x400D16: add_to_hash_array (main.c:136)
==39817==    by 0x400914: main (main.c:51)
4

3 に答える 3

1

これはほぼ機能するので、私のコメントを回答に移動します。

mallocの前にNULLに初期化する必要はありませんが、後で解放する変数ポインターはすべてNULLに設定し、NULLでない場合にのみ解放する必要があります。また、構造体のポインター値をNULLに初期化して(callocを使用してこれを自動的に取得する方が簡単)、ガードが正しく機能するようにします。私はあなたのコードをもう少し調べました、そしてあなたのハッシュは私を心配します-あなたはあなたのハッシュ関数が0からBUCKET_COUNT-1の範囲の値だけを返すことを確認する必要があります。この種の簡単な変更をいくつか行い、replace_strメソッドを修正しましたが、valgrindで文句を言うことはなくなりました。

コードを実行すると、比較する正しい単語が見つかったのがわかりますが、並べ替え関数は並べ替えられた文字列を返さないため、アナグラムを識別していません。簡単に修正できるはずです。

編集:最初の文字列ソートルーチンにグーグル検索で見つけたものを貼り付けて実行すると、次の出力が得られました。

./a.out trips
trips: 82 size: 5
strip

stirp

sprit

spirt
于 2013-01-17T23:13:16.227 に答える
1

generate_anagrams

void generate_anagrams(HashTable* a, char* word) {
    int index = hash(word);
    char* word_dup = strdup(word);
    char* current_word;
    string_sort(word_dup);
    printf("%s: %i size: %zi\n", word, index, strlen(word));
    Bucket* current = a->buckets[index];
    while(current != NULL) {
        if(current->data) {
            current_word = strdup(current->data);
            string_sort(current_word);
            if(strcmp(word_dup, current_word) == 0) {
                printf("%s\n", current->data);
            }
        }
        current = current->next;
    }
    free(current_word);
    free(word_dup);
}

strdupあなたはedを漏らしていますcurrent_word。に移動free(current_word);if (current->data)ます。

add_to_hash_array

void add_to_hash_array(HashTable** a, char* data) {
    int index = hash(data);
    HashTable* currentTable = a[strlen(data)];
    Bucket* existingNode = hash_lookup(currentTable, data);
    if(existingNode == NULL) {
        Bucket *newNode = (Bucket*) malloc(sizeof(Bucket));
        if(newNode == NULL) {
            exit(1);
        }
        replace_str(data, "\n", "\0");
        newNode->data = strdup(data);
        newNode->next = currentTable->buckets[index];
        currentTable->buckets[index] = newNode;
    } else {
        return;
    }
}

改行dataは検索後に削除するだけですが、改行を削除した後に挿入dataするため、重複は検出されません。

main、あなたはすべきではありません

 generate_anagrams(hash_array[strlen(arg_dup) + 1], arg_dup);

hash_array[strlen(arg_dup)]ただし、の先頭で改行を削除する場合に使用しますadd_to_hash_array

そしてもちろん、hash範囲外のインデックスが生成されないようにする必要があります。

そしてstrncpy(str, str, p-str);、未定義の振る舞いです。

于 2013-01-17T23:15:39.107 に答える
1

メモリリークはありませんが、コードにいくつかの問題があります(replace_str関数を正しいバージョンに置き換える必要があります)。Linuxボックスでのvalgrindの出力:

$ valgrind --leak-check=full --show-reachable=yes ./MEMORY_LEAK absurde
==13476== Memcheck, a memory error detector
==13476== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==13476== Using Valgrind-3.6.1-Debian and LibVEX; rerun with -h for copyright info
==13476== Command: ./MEMORY_LEAK absurde
==13476== 
==13476== Conditional jump or move depends on uninitialised value(s)
==13476==    at 0x8048A9A: hash_lookup (MEMORY_LEAK.c:132)
==13476==    by 0x80489BD: add_to_hash_array (MEMORY_LEAK.c:113)
==13476==    by 0x804871C: main (MEMORY_LEAK.c:50)
==13476== 
absurde: 70 size: 7
==13476== Conditional jump or move depends on uninitialised value(s)
==13476==    at 0x8048D1C: generate_anagrams (MEMORY_LEAK.c:169)
==13476==    by 0x8048795: main (MEMORY_LEAK.c:56)
==13476== 
==13476== Conditional jump or move depends on uninitialised value(s)
==13476==    at 0x8048890: hash_dealloc (MEMORY_LEAK.c:81)
==13476==    by 0x80487D8: main (MEMORY_LEAK.c:64)
==13476== 
==13476== Conditional jump or move depends on uninitialised value(s)
==13476==    at 0x4027BC2: free (vg_replace_malloc.c:366)
==13476==    by 0x804889C: hash_dealloc (MEMORY_LEAK.c:87)
==13476==    by 0x80487D8: main (MEMORY_LEAK.c:64)
==13476== 
==13476== 
==13476== HEAP SUMMARY:
==13476==     in use at exit: 0 bytes in 0 blocks
==13476==   total heap usage: 360 allocs, 360 frees, 133,424 bytes allocated
==13476== 
==13476== All heap blocks were freed -- no leaks are possible
==13476== 
==13476== For counts of detected and suppressed errors, rerun with: -v
==13476== Use --track-origins=yes to see where uninitialised values come from
==13476== ERROR SUMMARY: 65330 errors from 4 contexts (suppressed: 11 from 6)
于 2013-01-18T00:48:40.207 に答える