【问题标题】:CS50 PSET 5 Speller not working properly (and valgrind issues)CS50 PSET 5 Speller 无法正常工作(和 valgrind 问题)
【发布时间】:2020-08-18 22:37:05
【问题描述】:

我是 CS 新手,真的可以在拼写器 pset 上使用一些帮助。我有一个基本的大纲,似乎没有段错误,但我仍然有问题。它未能通过 check50:

  • 它不能正确处理大多数基本单词
  • 拼写检查不区分大小写
  • 它不能正确处理子字符串
  • 并且有内存错误

如果我通过它运行一个测试文件,计数器只显示字典中有 2 个单词,因此它几乎将文档中的每个单词都吐出为拼写错误(导致我认为加载有错误,但我不知道在哪里)。

还有一个 valgrind 错误。它显示以下内容:

堆摘要:在退出时使用:14 个块中的 1,300 个字节。 总堆使用量:15 次分配,1 次释放,分配了 5406 字节。

任何帮助将不胜感激;我已经坚持了 3 天!

#include <stdbool.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include "dictionary.h"

// Represents a node in a hash table
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

// Number of buckets in hash table
const unsigned int N = 26;

// Hash table
node *table[N];

// Returns true if word is in dictionary else false
bool check(const char *word)
{
    int numloc = hash(word);
    node *cursor = table[numloc];
    while (cursor != NULL)
    {
        if (strcasecmp(cursor->word, word) == 0)
        {
            return true;
        }
        cursor = cursor->next;
    }
    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    unsigned long hash = 5381;
    int c;
    while ((c = *word++))
    {
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
    }
    return hash % N;
}

// Loads dictionary into memory, returning true if successful else false
int counter = 0;
bool load(const char *dictionary)
{
    FILE *dictionary = fopen(dictionary, "r");
    if (dictionary == NULL)
    {
        return false;
    }
    char tempword[LENGTH + 1];
    for (int i = 0; i < N; i++)
    {
        table[i] = NULL;
    }
    while (fscanf(dict, "%s", tempword) != EOF)
    {
        node *n = malloc(sizeof(node));
        if (n == NULL)
        {
            return false;
        }
        strcpy(n->word, tempword);
        int A = hash(tempword);
        if (table[A] == NULL)
        {
            table[A] = n;
            n->next = NULL;
        }
        else
        {
            n->next = table[A];
            table[A] = n;
        }
        counter++;
    }
    fclose(dictionary);
    return true;
}


// Returns number of words in dictionary if loaded else 0 if not yet loaded
unsigned int size(void)
{
    if (counter == 0)
    {
        unload();
        return 1;
    }
    else
    {
        return counter;
    }
}

// Unloads dictionary from memory, returning true if successful else false
bool unload(void)
{
    for (int i = 0; i < N; i++)
    {
        node *cursor = table[i];
        node *tmp = table[i];
        while (cursor != NULL)
        {
            cursor = cursor->next;
            free(tmp);
            tmp = cursor;
        }
        return true;
    }
    return false;
}

【问题讨论】:

  • 为了不区分大小写,您应该只使用大写或小写字母进行散列(您选择哪个)。 tolower()toupper() 会有所帮助。
  • 另外,使用原始评论 hash * 33 而不是移位/添加。如果效率更高,编译器可以非常聪明地使用移位/加法。
  • BTW 的主要代码和其余代码在哪里?
  • 对于堆分配函数的每次调用:calloc()malloc()realloc() 必须在程序退出之前调用free(),使用与堆返回的指针值相同的指针值分配函数。
  • 编译时,始终启用警告。然后修复这些警告。 (对于gcc,至少使用:-Wall -Wextra -Wconversion -pedantic -std=gnu11)注意:其他编译器使用不同的选项来产生相同的结果。

标签: c cs50


【解决方案1】:

dictionary.h 的内容是什么?实际上,发布的代码无法编译!

以下是编译器运行的结果,其中包含一些关于如何解决问题的建议。

编译语句:

gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled1.c" -o "untitled1.o"

编译器的输出信息:

untitled1.c:20:7: error: variably modified ‘table’ at file scope
   20 | node *table[N];
      |       ^~~~~

this is not C++.. Therefore suggest replacing: 

const unsigned int N = 26;

#define N 26

因此 table[[] 将被正确声明

untitled1.c: In function ‘check’:

untitled1.c:25:18: warning: implicit declaration of function ‘hash’ [-Wimplicit-function-declaration]
   25 |     int numloc = hash(word);
      |                  ^~~~

此时代码中,编译器不知道函数的原型:hash()。建议将hash()函数移到包含此语句的函数之前。

untitled1.c: At top level:

untitled1.c:39:14: error: conflicting types for ‘hash’
   39 | unsigned int hash(const char *word)
      |              ^~~~

untitled1.c:25:18: note: previous implicit declaration of ‘hash’ was here
   25 |     int numloc = hash(word);
      |                  ^~~~

untitled1.c: In function ‘hash’:

untitled1.c:45:37: warning: conversion to ‘long unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
   45 |         hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
      |                                     ^

不要将局部变量命名为与函数名相同的名称。在上述情况下,编译器认为代码在递归调用函数:hash()

untitled1.c:47:17: warning: conversion from ‘long unsigned int’ to ‘unsigned int’ may change value [-Wconversion]
   47 |     return hash % N;
      |            ~~~~~^~~

untitled1.c: In function ‘load’:

untitled1.c:54:11: error: ‘dictionary’ redeclared as different kind of symbol
   54 |     FILE *dictionary = fopen(dictionary, "r");
      |           ^~~~~~~~~~

dictionary 已声明为字符串。所以在这里使用一些其他的变量名。

untitled1.c:52:23: note: previous definition of ‘dictionary’ was here
   52 | bool load(const char *dictionary)
      |           ~~~~~~~~~~~~^~~~~~~~~~

untitled1.c:54:30: warning: passing argument 1 of ‘fopen’ from incompatible pointer type [-Wincompatible-pointer-types]
   54 |     FILE *dictionary = fopen(dictionary, "r");
      |                              ^~~~~~~~~~
      |                              |
      |                              FILE * {aka struct _IO_FILE *}

In file included from untitled1.c:4:

/usr/include/stdio.h:246:14: note: expected ‘const char * restrict’ but argument is of type ‘FILE *’ {aka ‘struct _IO_FILE *’}
  246 | extern FILE *fopen (const char *__restrict __filename,
      |              ^~~~~

untitled1.c:60:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]
   60 |     for (int i = 0; i < N; i++)
      |                 
  ^

不要比较有符号和无符号值。它“可能”工作,但你不能依赖它工作。

untitled1.c:64:19: error: ‘dict’ undeclared (first use in this function)
   64 |     while (fscanf(dict, "%s", tempword) != EOF)
      |                   ^~~~

dict 必须是 FILE * 类型,但从未通过调用 fopen() 设置此类变量

untitled1.c:64:19: note: each undeclared identifier is reported only once for each function it appears in

untitled1.c:72:17: warning: conversion to ‘int’ from ‘unsigned int’ may change the sign of the result [-Wsign-conversion]
   72 |         int A = hash(tempword);
      |                 ^~~~

untitled1.c: In function ‘size’:

untitled1.c:100:16: warning: conversion to ‘unsigned int’ from ‘int’ may change the sign of the result [-Wsign-conversion]
  100 |         return counter;
      |                ^~~~~~~

untitled1.c: In function ‘unload’:

untitled1.c:107:23: warning: comparison of integer expressions of different signedness: ‘int’ and ‘unsigned int’ [-Wsign-compare]
  107 |     for (int i = 0; i < N; i++)
      |                       ^

Compilation failed.

【讨论】:

  • 您的编译器可能有不同的错误消息,因为在 pset5 拼写器中,它们为我们提供了一个名为 makefile 的文件,其中包含编译器的详细信息,因此在拼写器中编译器会这样做:
  • clang -ggdb3 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -c - o speller.o speller.c clang -ggdb3 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -c -o dictionary.o dictionary.c clang -ggdb3 -O0 -Qunused-arguments -std=c11 -Wall -Werror -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wshadow -o speller 拼写器。 o dictionary.o -lm
  • 不同于普通的编译器。
  • 更改使用的编译器或使用的选项不会使代码编译。我给了你几个(非常有用的)关于修复代码的建议。
  • 顺便说一句:来自评论,以:clang 开头,您使用的编译器与我使用的编译器相同。
【解决方案2】:

内存泄漏的原因是在卸载的最后一步,您还没有释放最后一个节点。试试这个版本的 while 循环。您希望将 free(temp) 作为循环的最后一行,以便在 cursor = NULL 时它不会被初始化/占用内存

bool unload(void)
{
    for (int i = 0; i < N; i++)
    {
        node *cursor = table[i];

        while (cursor != NULL)
        {
            node *tmp = cursor;
            cursor = cursor->next;
            free(tmp);
        }
        free(cursor);
        return true;
    }
    return false;
}

      

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-23
    • 2011-06-28
    相关资源
    最近更新 更多