【问题标题】:Segmentation fault when using fscanf in c在 c 中使用 fscanf 时出现分段错误
【发布时间】:2018-02-24 08:36:34
【问题描述】:

我真的很想知道是否有人不介意教育我了解我可能在这里错过的原则。我以为我已经涵盖了所有内容,但似乎我做错了什么。

下面的代码给了我一个分段错误,我不知道为什么?我在传递给fscanf的参数名称前添加&。

int word_size = 0;

#define HASH_SIZE 65536

#define LENGTH = 45

node* global_hash[HASH_SIZE] = {NULL};

typedef struct node {
  char word[LENGTH + 1];
  struct node* next;
} node;

int hash_func(char* hash_val){
    int h = 0;
    for (int i = 0, j = strlen(hash_val); i < j; i++){
        h = (h << 2) ^ hash_val[i];
    }
    return h % HASH_SIZE;
}

bool load(const char *dictionary)
{
    char* string;
    FILE* dic = fopen(dictionary, "r");
    if(dic == NULL){
        fprintf(stdout, "Error: File is NULL.");
        return false;
    }
    while(fscanf(dic, "%ms", &string) != EOF){
        node* new_node = malloc(sizeof(node));
        if(new_node == NULL){
            return false;
        }
        strcpy(new_node->word, string);
        new_node->next = NULL;
        int hash_indx = hash_func(new_node->word);
        node* first = global_hash[hash_indx];
        if(first == NULL){
            global_hash[hash_indx] = new_node;
        } else {
            new_node->next = global_hash[hash_indx];
            global_hash[hash_indx] = new_node;
        }
        word_size++;
        free(new_node);
    }
    fclose(dic);
    return true;
}

dictionary.c:25:16: runtime error: left shift of 2127912344 by 2 places cannot be represented in type 'int'
dictionary.c:71:23: runtime error: index -10167 out of bounds for type 'node *[65536]'
dictionary.c:73:13: runtime error: index -10167 out of bounds for type 'node *[65536]'
dictionary.c:75:30: runtime error: index -22161 out of bounds for type 'node *[65536]'
dictionary.c:76:13: runtime error: index -22161 out of bounds for type 'node *[65536]'

Segmentation fault

【问题讨论】:

  • 调试器告诉我们错误发生在哪一行?
  • 你确定new_node-&gt;word够长吗
  • 关于:while(fscanf(dic, "%ms", &amp;string) != EOF){ 1) '%ms' 无效,2) 当使用 '%s' 输入格式说明符时,始终包含一个比长度小一的 MAX CHARACTERS 修饰符输入缓冲区,因为“%s”总是将 NUL 字节附加到输入。这避免了缓冲区溢出的任何可能性。这种溢出是未定义的行为,可能导致段错误事件。顺便说一句:发布的代码缺少必要的分配(可能通过malloc())指针string指向的任何缓冲区。
  • 您的错误不在显示的代码中。了解'm' 修饰符%s 是不可移植的。它甚至是在基于 Linux 的编译器中定义的实现。

标签: c scanf


【解决方案1】:

在 OP 发布更多代码后更新

问题是您的hash_func 使用有符号整数并且它会溢出。因此,您会得到一个负的返回值(或者更确切地说是未定义的行为)。

这也是这几行告诉你的:

dictionary.c:25:16: 运行时错误: 2127912344 左移 2 位不能用“int”类型表示

这里告诉你有符号整数溢出

dictionary.c:71:23: runtime error: index -10167 out of bounds for type 'node *[65536]'

这里告诉你你在数组中使用了一个负索引(即global_hash)

尝试使用无符号整数

unsigned int hash_func(char* hash_val){
    unsigned int h = 0;
    for (int i = 0, j = strlen(hash_val); i < j; i++){
        h = (h << 2) ^ hash_val[i];
    }
    return h % HASH_SIZE;
}

然后这样称呼它:

unsigned int hash_indx = hash_func(new_node->word);

原答案

我不确定这是所有问题的根本原因,但您似乎在内存分配方面遇到了一些问题。

每次调用fscanf 时,都会为string 分配新的动态内存到%ms。但是,你从来没有free 那个内存,所以你有泄漏。

此外,这看起来像是一个主要问题:

        global_hash[hash_indx] = new_node;  // Here you save new_node
    } else {
        new_node->next = global_hash[hash_indx];
        global_hash[hash_indx] = new_node;  // Here you save new_node
    }
    word_size++;
    free(new_node);  // But here you free the memory

因此,您的表似乎包含指向已释放内存的指针。

这是使用指针时可能导致段错误的主要问题。

也许改变一下

free(new_node); 

到

free(string);

一般来说,我建议您避免使用%ms,同时避免使用fscanf。请改用char string[LENGTH + 1] 和fgets。

【讨论】:

  • 感谢您的回答,但这对我不起作用。我仍然收到runtime errors 和一个无限循环。
  • @rebbailey - 你确定你在发布的函数中得到它吗?
  • @rebbailey 也许试试这个改变:(fscanf(dic, "%ms", &amp;string) == 1)
  • 是的,肯定在里面。我尝试了建议,但结果完全相同。
  • @rebbailey - 我不确定“部分”有效是什么意思。也许“新”问题与此处描述的问题无关。如果是这样的话,我认为你应该问一个新问题,你应该在哪里发布更新的代码并描述哪里出了问题。
【解决方案2】:

发布的代码中有多个问题。以下是主要的:

  • 您应该使用无符号算法进行哈希码计算,以确保哈希值为正。当前实现具有未定义的行为,因为超过 15 个字母的单词会导致算术溢出,这可能会产生负值并导致模数也为负,索引超出 global_hash 的范围。

  • 您使用free(new_node); 释放新分配的节点。它已存储到global_hash 数组中:稍后将其取消引用以获取具有相同哈希值的另一个单词将导致未定义的行为。您可能打算使用 free(string); 来释放已解析的单词。

以下是其他问题:

  • 您应该在将string 复制到带有strcpy(new_node-&gt;word, string); 的节点结构数组之前检查其长度

  • fscanf(dic, "%ms", &amp;string) 不可移植。 m 修饰符导致fscanf 为单词分配内存,但它是glibc 支持的扩展,在其他环境中可能不可用。您可能想编写一个简单的函数以获得更好的可移植性。

  • 主循环应该使用while(fscanf(dic, "%ms", &amp;string) == 1) 测试转换是否成功,而不是使用EOF 测试文件结尾。在这种特定情况下它可能不会导致问题,但它是其他转换说明符未定义行为的常见原因。

  • 定义#define HASH_SIZE 65536; 有一个额外的;,如果在表达式中使用HASH_SIZE,可能会导致意外行为。

  • #define LENGTH = 45; 的定义不正确:代码没有按照发布的那样编译。

这是修改后的版本:

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

#define HASH_SIZE 65536
#define LENGTH 45

typedef struct node {
    char word[LENGTH + 1];
    struct node *next;
} node;

int word_size = 0;
node *global_hash[HASH_SIZE];

unsigned hash_func(const char *hash_val) {
    unsigned h = 0;
    for (size_t i = 0, j = strlen(hash_val); i < j; i++) {
        h = ((h << 2) | (h >> 30)) ^ (unsigned char)hash_val[i];
    }
    return h % HASH_SIZE;
}

/* read a word from fp, skipping initial whitespace.
   return the length of the word read or EOF at end of file
   store the word into the destination array, truncating it as needed
*/
int get_word(char *buf, size_t size, FILE *fp) {
    int c;
    size_t i;
    while (isspace(c = getc(fp)))
        continue;
    if (c == EOF)
        return EOF;
    for (i = 0;; i++) {
        if (i < size)
           buf[i] = c;
        c = getc(fp);
        if (c == EOF)
            break;
        if (isspace(c)) {
            ungetc(c, fp);
            break;
        }
    }
    if (i < size)
        buf[i] = '\0';
    else if (size > 0)
        buf[size - 1] = '\0';
    return i;
}

bool load(const char *dictionary) {
    char buf[LENGTH + 1];
    FILE *dic = fopen(dictionary, "r");
    if (dic == NULL) {
        fprintf(stderr, "Error: cannot open dictionary file %s\n", dictionary);
        return false;
    }
    while (get_word(buf, sizeof buf, dic) != EOF) {
        node *new_node = malloc(sizeof(node));
        if (new_node == NULL) {
            fprintf(stderr, "Error: out of memory\n");
            fclose(dic);
            return false;
        }
        unsigned hash_indx = hash_func(buf);
        strcpy(new_node->word, buf);
        new_node->next = global_hash[hash_indx];
        global_hash[hash_indx] = new_node;
        word_size++;
    }
    fclose(dic);
    return true;
}

【讨论】:

    【解决方案3】:

    以下建议的代码:

    1. 干净编译
    2. 功能还是有大问题:hash_func()
    3. 为了清晰和灵活,将结构的定义与该结构的 typedef 分开。
    4. 正确格式化#define 语句
    5. 正确处理来自fopen() 和malloc() 的错误
    6. 适当限制从“字典”文件中读取的字符串长度
    7. 假定“字典”文件中的任何文本都不会超过 45 个字节。

    现在,建议的代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <string.h>
    
    //prototypes
    bool load(const char *dictionary);
    int hash_func(char* hash_val);
    
    
    #define HASH_SIZE 65536
    #define LENGTH  45
    
    
    struct node
    {
        char word[LENGTH + 1];
        struct node* next;
    };
    typedef struct node node;
    
    
    node* global_hash[HASH_SIZE] = {NULL};
    int word_size = 0;
    
    int hash_func(char* hash_val)
    {
        int h = 0;
        for ( size_t i = 0, j = strlen(hash_val); i < j; i++)
        {
            h = (h << 2) ^ hash_val[i];
        }
        return h % HASH_SIZE;
    }
    
    
    bool load(const char *dictionary)
    {
        char string[ LENGTH+1 ];
        FILE* dic = fopen(dictionary, "r");
        if(dic == NULL)
        {
            perror( "fopen failed" );
            //fprintf(stdout, "Error: File is NULL.");
            return false;
        }
    
        while( fscanf( dic, "%45s", string) == 1 )
        {
            node* new_node = malloc(sizeof(node));
            if(new_node == NULL)
            {
                perror( "malloc failed" );
                return false;
            }
    
            strcpy(new_node->word, string);
            new_node->next = NULL;
    
            int hash_indx = hash_func(new_node->word);
    
            // following statement for debug:
            printf( "index returned from hash_func(): %d\n", hash_indx );
    
            if( !global_hash[hash_indx] )
            {
                global_hash[hash_indx] = new_node;
            }
    
            else
            {
                new_node->next = global_hash[hash_indx];
                global_hash[hash_indx] = new_node;
            }
    
            word_size++;
        }
        fclose(dic);
        return true;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-25
      • 2020-03-24
      • 1970-01-01
      • 1970-01-01
      • 2017-03-29
      • 2015-02-09
      • 1970-01-01
      • 2011-05-25
      相关资源
      最近更新 更多