【问题标题】:Segmentation fault loading dictionary. Is it being caused by calloc, sys/stat.h or something else?分段错误加载字典。它是由 calloc、sys/stat.h 还是其他原因引起的?
【发布时间】:2014-10-08 18:37:37
【问题描述】:

这个函数应该将字典加载到 trie 中。我想知道字典文件有多大,这样我就可以同时calloc 所有内存。这样做的原因是所有的内存都可以靠近在一起,因此可以利用有助于加快搜索速度的硬件。我也找到了 2 种方法的建议。其中之一是使用sys/stat.h,您将在我的代码中看到。

当我运行此代码时,我收到一个“分段错误”,我知道这意味着我正在尝试访问我没有权限的内存。通过使用 GDB,我发现分段错误发生在第 116 行(又名:读取“else if (cur->children[key] == NULL)”的行)我发现键中的值在那时间是 12 点。起初我认为问题在于我使用了 callocsys/stat.h,因为这是我最不了解的两件事。然而,我研究得越多,这似乎就越不可能。如果它不是其中之一,那么我什至不知道该往哪里看。

以下只是我认为相关的代码:

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>

#include "dictionary.h"

typedef struct node
{
    bool end[26]; 

    struct node* children[26]; 
} node;

node* start;

int key;
int last;
int dic_count;

bool load(const char* dictionary)
{
    struct stat s;
    stat(dictionary, &s);
    int size = s.st_size;

    dic_count = 0;

    int z = 1;

    FILE* dic = fopen(dictionary, "r");
    if (dic == NULL)
    {
        return false;
    }

    start = calloc(size, sizeof(node));

    if (start == NULL)
    {
        return false;
    }

    int l = 0;
    int d;

    node* cur = &start[0];

    while (0 != (d = fgetc(dic)))
    {
        int d = fgetc(dic);

        if (l > 0)
        {
            last = key;
        }

        l = 1;

        key = d - 'a';

        if (d == '\n')
        {
            cur->end[last] = true;
            cur = &start[0];
            dic_count++;
        }
        else if (cur->children[key] == NULL)
        {
            node* new = &start[z];

            cur->children[key] = new;

            z++;

            if (cur->children[key] == NULL)
            {
                return false;
            }

            cur = cur->children[key];
        }
        else
        {
            cur = cur->children[key];
        }
    }
    return true;
}

非常感谢任何帮助。

【问题讨论】:

  • 什么是start 指向整个所有这些代码? 一个动态节点 ?我认为你需要的不止这些。
  • 起始点位于被调用内存中的第一个动态节点。其他节点在该内存中创建。 start 只是起点,而不是整个存储。
  • 我不确定文件大小是否能很好地估计您需要的节点数。当您阅读时,您将换行符与其他字符区分开来,您认为这些字符是小写字母。这对我来说似乎很容易出错。

标签: c dictionary dynamic-arrays trie calloc


【解决方案1】:

您确定您的文件包含二进制 0 吗?如果您尝试读取到文件末尾,请针对 EOF 测试 fgetc 结果,而不是 0。否则您的循环永远不会终止。

除此之外,您只处理每个第二个字符。

按要求扩展

来自man fgetc

fgetc()、getc() 和 getchar() 将读取的字符作为 unsigned char 强制转换为文件末尾的 int 或 EOF 或错误

您可能将它与fgets 返回值混淆了。

while ((ch = fgetc(fp)) != EOF)

安然无恙。同样,可能造成混淆的根源是不健全

while (!feof(fp))

现在,关于未处理的字符:你写了

    while (0 != (d = fgetc(dic)))
    {
        int d = fgetc(dic);

代码读取while 表达式中的一个字符,将其与0 进行比较,然后读取一个(下一个)字符。第一个字符丢失。

【讨论】:

  • fgetc 将在没有任何内容可返回时返回 0。我已经读过使用 EOF 不好,因为它的制作方式在它到达文件末尾后会额外运行 1 次,所以你最终会得到垃圾值。至于唯一处理每秒一个字符的问题,您能更全面地解释一下吗?
  • @stjwiv:你听说你不应该在你的代码中对feof 使用额外的测试。 EOF 是一个特殊的整数常量,在某些&lt;stdio.h&gt; 函数中指示文件结束(或读取错误)。 (如果您读取的文件是二进制文件,其中很可能有一个零,因此fgetc 返回 0-255 范围内的 char 值或特殊常量 EOF,通常为 -1。)
  • @user58697:你说得对,这解决了问题。疯狂的部分是,我在之前进行更改时注意到了这一点,并且打算修复它。这就是为什么 int d 在 while 循环之前和之后都定义的原因。我想我从来没有完成修复它。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2012-07-20
  • 2013-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-01
相关资源
最近更新 更多