两个类似的 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);
)
问题
以下是可能导致分段错误的一些问题:
- 缓冲区
c
和 c2
都在堆栈上声明,而不是 null-terminated。您不安全地假设它们是 null-terminated。当您尝试 printf
这些缓冲区时,这很容易成为段错误的来源(printf
总是搜索空字符作为缓冲区结束的指示)
- 您在为 2D 板 (
strcat(boards[count][sub_board_count], c);
) 分配的 non-nulled 缓冲区上以相同的方式使用 strcat
。您 malloc
使用了这些缓冲区,但不能保证内存为零。 strcat
还会查找空字符以指示从何处开始连接。
- 您无限期地从文件
boards_file
中读取板,并且从不检查您是否有容量。您已将棋盘数固定为 730
(#define BOARDS 730
)。如果文件包含更多内容,您还会遇到分段错误,因为您没有执行边界检查。
修复
- 始终将堆栈缓冲区归零。你可以这样做:
char c[N] = {0}; // Zeroes the entire buffer
.
- 如果您要在其中存储字符串并使用 C 字符串函数,请始终将分配的内存归零。因此,要么使用
memset
要么 calloc
将它们分配为零。
我正在尝试将两个文件加载到内存中,一个作为二维字符数组(即 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
提前感谢您的宝贵时间。抱歉,如果我是愚蠢的并且遗漏了一些明显的东西,但是一些 google 挖掘表明像 char c[BOARD_SIZE + 1]
这样的行和一些堆东西一样有效(例如 char *c = malloc(BOARD_SIZE + 1); memset(c, 0, BOARD_SIZE + 1);
)
问题
以下是可能导致分段错误的一些问题:
- 缓冲区
c
和c2
都在堆栈上声明,而不是 null-terminated。您不安全地假设它们是 null-terminated。当您尝试printf
这些缓冲区时,这很容易成为段错误的来源(printf
总是搜索空字符作为缓冲区结束的指示) - 您在为 2D 板 (
strcat(boards[count][sub_board_count], c);
) 分配的 non-nulled 缓冲区上以相同的方式使用strcat
。您malloc
使用了这些缓冲区,但不能保证内存为零。strcat
还会查找空字符以指示从何处开始连接。 - 您无限期地从文件
boards_file
中读取板,并且从不检查您是否有容量。您已将棋盘数固定为730
(#define BOARDS 730
)。如果文件包含更多内容,您还会遇到分段错误,因为您没有执行边界检查。
修复
- 始终将堆栈缓冲区归零。你可以这样做:
char c[N] = {0}; // Zeroes the entire buffer
. - 如果您要在其中存储字符串并使用 C 字符串函数,请始终将分配的内存归零。因此,要么使用
memset
要么calloc
将它们分配为零。