两个类似的 while 循环之一导致 fread 出现段错误

One of two similar while loops is causing segfault at fread

我正在尝试将两个文件加载到内存中,一个作为二维字符数组(即 char ***)的数组,另一个作为散列 table。我的整个文件在下面,如果需要,我可以在 files/ 中共享文件。我的目标是实现一个 minimax 算法来解决一个叫做 WordBord 的游戏(wordbord.com,我认为 C 将是最有效的语言。如果您对其他语言有建议(我精通 Python 并且可能知道的 Java 足以完成这项工作),请告诉我。我这样做主要是为了挑战自己。

计划:

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define BOARDS 730
#define BOARD_SIZE 5
#define NUM_BUCKETS 3001

/*
 * Plan: Board layout:
 * Array of 2d arrays, so a char *** array (lol)
 * Array of strings (words)
 */

struct node {
    struct node *next;
    char *word;
};

char ***boards;
struct node **words;

// Djb2 hash function
// Code from:
// https://gist.github.com/MohamedTaha98/ccdf734f13299efb73ff0b12f7ce429f
unsigned long hash(char *str) {
        unsigned long hash = 5381;
        int c;
        while ((c = *str++))
            hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
        return hash % NUM_BUCKETS;
}

bool isword(char *str) {
    unsigned long int idx = hash(str);
    struct node *node_ptr = words[idx];
    while (node_ptr != NULL) {
        if (!strcmp(node_ptr->word, str)) {
            return true;
        }
        node_ptr = node_ptr->next;
    }
    return false;
}

void add(char *str) {
    // Add string to hash table
    printf("%s\n", str);
}

int main() {
    FILE *boards_file = fopen("files/boards", "r");
    char c[BOARD_SIZE + 1];
    int count = 0;
    int sub_board_count = 0;
    boards = malloc(sizeof(char**) * BOARDS);
    for (int i = 0; i < BOARDS; i++) {
        boards[i] = malloc(sizeof(char*) * BOARD_SIZE);
        for (int j = 0; j < BOARD_SIZE; j++) {
            boards[i][j] = malloc(BOARD_SIZE + 1);
        }
    }

    printf("loading...\n");
    printf("here\n");
    while (fread(&c, 1, BOARD_SIZE, boards_file)) {
        printf("here\n");
        strcat(boards[count][sub_board_count], c);
        printf("here\n");
        printf("%s -> %s (%i, %i)\n", c, boards[count][sub_board_count], count, sub_board_count);

        fseek(boards_file, 1, SEEK_CUR);
        sub_board_count++;
        if (sub_board_count == 5) {
            // We reached the end of the board!
            count++;
            sub_board_count = 0;
        }
    }

    printf("done loading, printing...\n-----\n");
    for (int i = 0; i < BOARDS; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) {
            printf("%s\n", boards[i][j]);
        }
        printf("-----\n");
    }
    fclose(boards_file);
    printf("done loading boards, loading words...\n");

    FILE *words_file = fopen("files/words", "r");
    char c2[BOARD_SIZE + 1];
    printf("here\n");
    while (fread(&c2, 1, BOARD_SIZE, words_file)) {
        printf("here\n");
        add(c2);
    }

    printf("program done, freeing...\n");
    // don't forget to close and free everthing!
    for (int i = 0; i < BOARDS; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) {
            free(boards[i][j]);
        }
        free(boards[i]);
    }
    free(boards);
}

当用 gcc 编译时(完整命令:gcc -Wall -Werror -Wextra -Wno-sign-compare -Wshadow wordbord-solver.c -lcrypt -lm -lgmp -o wordbord-solver(我知道我不需要这些链接,但它们对其他程序很有用,即 make ),第 95 行出现段错误, 第二个 while 循环。

提前感谢您的宝贵时间。抱歉,如果我是愚蠢的并且遗漏了一些明显的东西,但是一些 google 挖掘表明像 char c[BOARD_SIZE + 1] 这样的行和一些堆东西一样有效(例如 char *c = malloc(BOARD_SIZE + 1); memset(c, 0, BOARD_SIZE + 1);

问题

以下是可能导致分段错误的一些问题:

  1. 缓冲区 cc2 都在堆栈上声明,而不是 null-terminated。您不安全地假设它们是 null-terminated。当您尝试 printf 这些缓冲区时,这很容易成为段错误的来源(printf 总是搜索空字符作为缓冲区结束的指示)
  2. 您在为 2D 板 (strcat(boards[count][sub_board_count], c);) 分配的 non-nulled 缓冲区上以相同的方式使用 strcat。您 malloc 使用了这些缓冲区,但不能保证内存为零。 strcat 还会查找空字符以指示从何处开始连接。
  3. 您无限期地从文件 boards_file 中读取板,并且从不检查您是否有容量。您已将棋盘数固定为 730 (#define BOARDS 730)。如果文件包含更多内容,您还会遇到分段错误,因为您没有执行边界检查。

修复

  • 始终将堆栈缓冲区归零。你可以这样做:char c[N] = {0}; // Zeroes the entire buffer.
  • 如果您要在其中存储字符串并使用 C 字符串函数,请始终将分配的内存归零。因此,要么使用 memset 要么 calloc 将它们分配为零。