编辑:我添加了所有文件(.c,.h和Makefile)。该程序有效,但有很多内存泄漏。
http://dl.dropbox.com/u/21926200/Ngrams.tar.gz
如何遍历哈希表来调用free()?我想知道我是否需要创建一个单独的函数来调用htable和结果上的free(),或者我可以在这段代码中完成它。有任何想法吗?我只需要在htable和结果上调用free()。
h_ptr *htable;
int tsize;
void new_table(int size)
{
tsize = size;
htable = (h_ptr *) calloc(size, sizeof(h_ptr));
if (!htable) {
fprintf(stderr, "Couldn't allocate hash array, exiting\n");
exit(1);
}
// At the beginning, each element in the hash table
// is a NULL pointer
for(int i=0; i<size; i++)
{
htable[i]=NULL;
}
}
// Allocate new element to store in hash table
h_ptr new_element(char *s)
{
h_ptr result = (h_ptr) malloc(sizeof(h_rec));
int wlen = strlen(s);
if (wlen > llen) {
lstring = s;
llen = wlen;
lcnt = 1;
} else if (wlen == llen)
lcnt++;
if (!result) {
fprintf(stderr, "Couldn't allocate hash element, exiting\n");
exit(1);
}
result->word = s;
result->freq = 1;
result->next = NULL;
return result;
}
由于
答案 0 :(得分:3)
鉴于new_element()
函数中的信息:
result->word = s;
result->freq = 1;
result->next = NULL;
我们必须推断(因为你没有包含实际信息)你的哈希表是一个数组,其中元素是链接的单独分配元素的列表包含分配的名称,频率和指向下一个的指针项目。因此,释放哈希表涉及依次访问数组的每个元素,追查链表,释放名称和元素。
void free_hashtable(void)
{
if (htable != 0)
{
for (int i = 0; i < tsize; i++)
{
h_ptr next = 0;
for (h_ptr curr = htable[i]; curr != 0; curr = next)
{
next = curr->next;
free(curr->word);
free(curr);
}
}
free(htable);
htable = 0;
tsize = 0;
}
}
我们仍然没有数据结构,因此我们仍然无法确切地说出正在发生的事情。因此,我们可以将其添加到代码的顶部:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct hash_table_entry
{
int freq;
struct hash_table_entry *next;
char *word;
} *h_ptr;
但是,我们添加的一个问题是:
h_ptr result = (h_ptr) malloc(sizeof(h_rec));
编译器说:
error: 'h_rec' undeclared (first use in this function)
识别该行。现在,可能是你有一些类型如:
typedef struct hash_table_entry h_rec;
但我们不应该猜测。请创建一个SSCCE(Short, Self-Contained, Correct Example),这样我们就不用猜了。
我使用以下代码编译代码:
gcc -O3 -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition ht.c -o ht
编译器警告我:
ht.c:27:6: warning: no previous prototype for ‘lowercase’ [-Wmissing-prototypes]
ht.c:41:6: warning: no previous prototype for ‘new_table’ [-Wmissing-prototypes]
ht.c:59:7: warning: no previous prototype for ‘new_element’ [-Wmissing-prototypes]
ht.c:82:10: warning: no previous prototype for ‘hash_function’ [-Wmissing-prototypes]
ht.c:103:7: warning: no previous prototype for ‘save_string’ [-Wmissing-prototypes]
ht.c:116:7: warning: no previous prototype for ‘find_element’ [-Wmissing-prototypes]
ht.c:142:7: warning: no previous prototype for ‘sort_words’ [-Wmissing-prototypes]
ht.c:176:6: warning: no previous prototype for ‘insert_string’ [-Wmissing-prototypes]
ht.c:191:6: warning: no previous prototype for ‘init_token’ [-Wmissing-prototypes]
ht.c:201:7: warning: no previous prototype for ‘get_word’ [-Wmissing-prototypes]
ht.c:224:7: warning: no previous prototype for ‘get_token’ [-Wmissing-prototypes]
ht.c:276:6: warning: no previous prototype for ‘word_freq’ [-Wmissing-prototypes]
ht.c: In function ‘main’:
ht.c:322:5: warning: implicit declaration of function ‘add_string_option’ [-Wimplicit-function-declaration]
ht.c:323:5: warning: implicit declaration of function ‘add_int_option’ [-Wimplicit-function-declaration]
ht.c:328:5: warning: implicit declaration of function ‘parse_options’ [-Wimplicit-function-declaration]
ht.c:329:5: warning: implicit declaration of function ‘show_options’ [-Wimplicit-function-declaration]
对于最后四个名称,链接器无法找到它们。我们需要的代码不能使用不可用的代码。将代码减少为SSCCE(注意自包含!)。
我已经删除了对这些功能的调用并相应地简化了main。当在自己的源(./ht < ht.c
)上运行时,它会产生:
N-gram size 1
Running analysis... (This can take several minutes or more!)
Initializing hash table...
Inserting all n-grams into hash table in lowercase form...
Sorting all hash table elements according to frequency...
Analysis Details:
(Top 10 list of n-grams)
82 '='
30 'int'
27 '0'
23 'if'
23 'char'
22 's'
16 '1'
16 'i'
14 'ls'
14 'h_ptr'
Analysis Summary:
817 total n-grams
211 unique n-grams
94 singleton n-grams (occur only once)
Most common n-gram (with 82 occurrences) is '='
Longest n-gram (1 have length 16) is 'hash_table_entry'
Total time = 0.002225 seconds
看起来不错......但是在valgrind
下运行优化版本并显示另一个故事......但我已经得出结论,问题出现在strlen()
的优化实现中我使用的GCC的特定版本(因为,部分地,在相同的代码上使用不同版本的GCC会导致错误消失,并且因为错误发生在strlen()
的调用中没有业务在5字节的分配中从偏移4开始读取4个字节。)
我添加了一个函数print_hashtable()
。我认为,对于几乎任何非平凡的结构,值得拥有一个&#39; print&#39;或者&#39; dump&#39;功能可以这样。
static void print_hashtable(FILE *fp, const char *tag)
{
fprintf(fp, "Print hash table: %s\n", tag);
fprintf(fp, "Size = %d; Address: %p\n", tsize, htable);
if (htable != 0)
{
for (int i = 0; i < tsize; i++)
{
printf("Entry %d (%p)\n", i, htable[i]);
h_ptr next = 0;
for (h_ptr curr = htable[i]; curr != 0; curr = next)
{
next = curr->next;
printf("Word: %s (freq %d; next %p)\n", curr->word, curr->freq, next);
}
}
}
}
我在sort_words()
之前插入了对此的调用。这表明尽管哈希算法效果不是很好,但数据结构仍然完好无损。
我还在print_hashtable()
之后插入了对sort_words()
的调用,这表明散列表被排序过程全面破坏了。排序阶段将哈希表作为哈希表消除;唯一要做的就是整个表格(free(htable)
)。释放所有表条目必须在函数word_freq()
:
static void free_hashlist(h_ptr head)
{
while (head != 0)
{
h_ptr next = head->next;
printf("Free: %d (%s) %p -> %p\n", head->freq, head->word, (void *)head, (void *)next);
free(head->word);
free(head);
head = next;
}
}
static void word_freq(FILE *src, int details, int size)
{
char *s;
h_ptr list_head, ptr;
printf(" Initializing hash table...\n");
init_token(src);
new_table(size);
printf(" Inserting all n-grams into hash table in lowercase form...\n");
while ((s = get_word()))
insert_string(s);
print_hashtable(stdout, "After reading data");
printf(" Sorting all hash table elements according to frequency...\n");
list_head = sort_words();
if (details > 0)
{
printf("\nAnalysis Details:\n(Top %i list of n-grams)\n", details);
ptr = list_head;
while (ptr && details--)
{
printf("%d\t'%s'\n", ptr->freq, ptr->word);
ptr = ptr->next;
}
}
printf("\nAnalysis Summary:\n");
printf("%d total n-grams\n", wcnt);
printf("%d unique n-grams\n", ucnt);
printf("%d singleton n-grams (occur only once)\n", scnt);
printf("Most common n-gram (with %d occurrences) is '%s'\nLongest n-gram (%d have length %d) is '%s'\n",
mcnt, mstring, lcnt, llen, lstring);
free_hashlist(list_head);
}
并且必须简化free_hashtable()代码:
static void free_hashtable(void)
{
if (htable != 0)
{
free(htable);
htable = 0;
lcnt = 0;
lstring = 0;
mcnt = 0;
mstring = 0;
scnt = 0;
tsize = 0;
wcnt = 0;
ucnt = 0;
}
}
int main(void)
{
printf("\nRunning analysis... (This can take several minutes or more!)\n");
word_freq(stdin, 10, 1024);
printf("Total time = %f seconds\n", (double) clock() / CLOCKS_PER_SEC);
free_hashtable();
return 0;
}
在这个程序中,重置指针并计数为零/零并不重要。此外,lstring
和mstring
指针由free_hashlist()
无效,因此可以说这些指针应该在该函数中被置零/归零。
这对我来说是无泄漏的,并且最后只有系统分配的内存仍然在使用。
答案 1 :(得分:0)
首先,在分配htable[i]
至htable
之后,您不需要清除每个人calloc
(因为calloc
会为您提供已清除的内存区域或失败)。我建议calloc
使用new_element
result
。
然后,您可能希望有一些功能来删除htable
。像
void delete_htable(void) {
if (!htable) return;
for (int i=0; i<tsize; i++)
free (htable[i]);
free (htable);
htable = NULL;
}
请记住,您可以free
NULL
指针(不会发生任何有害的事情)。
您可能希望在delete_htable
之前致电exit
(只是为了避免内存泄漏,并让valgrind满意)。
最后,您可以考虑在现有库中使用现有的散列函数。考虑例如Glib(在GTk中,但您可以在任何GTK应用程序之外使用 Glib )。它已经为您提供hash-tables(并且考虑到这些经常使用,您可能对它们充满信心)。不要忘记GTK(因此是Glib)是free software library下的LGPL2.1 license,因此您可以下载source code,进行研究和改进。您想查看glib/ghash.h标题和glib/ghash.c代码源文件(以及其他一些代码)。