You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言CS50 pset5 speller运行提示killed报错问题求助

问题:CS50 pset5 speller运行输出killed,调试/valgrind无法正常运行

做CS50 pset5的speller练习时程序异常:运行后终端输出killed,尝试调试、用valgrind做内存检测时程序直接停止退出,无法正常运行。
原实现代码如下:

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

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

// TODO: Choose number of buckets in hash table
const unsigned int N = 150000;

// Hash table
node *table[N];

//Declare variables here so that they can be used in the different functions below
unsigned int HASH_INDEX;
unsigned int NO_OF_WORDS = 0;
node *CURSOR;

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

    return false;
}

// Hashes word to a number
unsigned int hash(const char *word)
{
    // TODO: Improve this hash function
    // close address, each bucket in the hash table is a pointer
    unsigned int hash_value = 0;
    for (int i = 0; i < strlen(word); i++)
    {
        int c = tolower(word[i]);
        hash_value = hash_value + c;
    }

    hash_value = hash_value % 31;
    return hash_value;
}

// Loads dictionary into memory, returning true if successful, else false
bool load(const char *dictionary)
{
    // TODO
    // open dictionary file
    FILE *d = fopen(dictionary, "r");
    if (d == NULL)
    {
        return false;
    }

    char word[LENGTH + 1];
    while (fscanf(d, "%s", word) != EOF)
    {
        node *w = malloc(sizeof(node));
        if (w == NULL)
        {
            return false;
        }
        else
        {
            strcpy(w->word, word);
            w->next = NULL;
            HASH_INDEX = hash(word);

            if (table[HASH_INDEX] == NULL)
            {
                table[HASH_INDEX] = w;
            }
            w->next = table[HASH_INDEX];
            table[HASH_INDEX] = w;
            NO_OF_WORDS++;
        }
    }
    fclose(d);
    return true;
}

// Returns number of words in dictionary if loaded, else 0 if not yet loaded
unsigned int size(void)
{
    return NO_OF_WORDS;
}

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

    return true;
}

问题原因&修复方案

程序输出killed是被系统发送SIGKILL信号强制终止导致的,核心原因是代码存在多个逻辑bug,引发无限死循环占满系统资源,同时还有其他会触发崩溃的隐患:

  • 核心bug:哈希表插入逻辑错误,产生链表自环引发死循环
    插入节点时多余的if判断会导致每个哈希桶里第一个插入的节点next指针指向自身,形成自环:

    // 错误逻辑
    if (table[HASH_INDEX] == NULL)
    {
        table[HASH_INDEX] = w;
    }
    w->next = table[HASH_INDEX];
    table[HASH_INDEX] = w;
    

    当桶为空时,if块内先把桶头指针指向新节点w,随后执行w->next = table[HASH_INDEX]等价于w->next = w,节点自引用。后续遍历链表到这个节点时,指针永远不会走到NULL,陷入无限死循环,长时间占用CPU资源最终被系统强制杀死。
    头插法插入链表不需要这个if判断,直接写两行即可:

    w->next = table[HASH_INDEX];
    table[HASH_INDEX] = w;
    
  • check函数存在空指针解引用风险
    用do...while循环遍历链表会先执行循环体再判断终止条件,如果对应哈希桶为空(桶头指针为NULL),进入循环第一行就会访问CURSOR->word,直接触发空指针解引用的段错误。另外CURSOR、HASH_INDEX这类临时变量没必要设为全局变量,容易出现跨函数的变量污染,改成函数内局部变量即可,遍历逻辑改成先判空再访问:

    bool check(const char *word)
    {
        unsigned int hash_idx = hash(word);
        node *cursor = table[hash_idx];
        while (cursor != NULL)
        {
            if (strcasecmp(cursor->word, word) == 0)
            {
                return true;
            }
            cursor = cursor->next;
        }
        return false;
    }
    

    unload函数里的CURSOR也建议改成局部变量,不要复用全局变量。

  • 哈希函数返回值范围不合理,冲突严重
    定义了150000个哈希桶,但哈希函数最后用31取模,所有单词只会落到0~30共31个桶里,剩下的桶完全闲置,哈希冲突极多,链表过长会大幅降低程序运行效率。把取模值改成和桶数量一致即可:hash_value = hash_value % N;,也可以替换成分布更均匀的哈希函数。

  • 存在缓冲区溢出风险
    用fscanf(d, "%s", word)读取单词时没有限制最大读取长度,一旦遇到长度超过LENGTH的字符串就会溢出word数组,破坏栈内存引发未定义行为,改成fscanf(d, "%45s", word)(和LENGTH定义的长度匹配)限制读取长度即可。

把以上问题修复后,程序就能正常运行,不会再出现killed或者崩溃的问题。

内容的提问来源于stack exchange,提问作者jj7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:49:12