分段错误:读取文件时为11

时间:2016-10-25 22:19:58

标签: c

我不知道为什么但是我无法读取txt文件而我收到了分段错误11错误,我不知道为什么。我必须阅读之前在txt文件上编写的迷宫。显然一切对我来说都很好。有没有人看错了什么?

我非常感激。

enum directions {DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT};

typedef struct {
int y, x;
enum directions d;

int lins, cols;
char **maze;
} t_maze;



t_maze *read_maze (char *file) {

    FILE *f = fopen (file, "r");

    if (!f) {
        return NULL;
    }

    t_maze *my_maze = (t_maze *) malloc (sizeof (t_maze));
    fscanf (f, "%d %d %d %d\n", &(my_maze->lins), &(my_maze->cols), &(my_maze->x), &(my_maze->y));


    int lin;
    my_maze->maze = (char **) malloc (my_maze->lins * sizeof (char *));
    for (lin = 0; lin < my_maze->lins; lin++) {
        my_maze->maze[lin] = (char *) malloc ((my_maze->cols) * sizeof (char));
    }


    lin = 0;
    while (!feof (f)) {
        int read, col = 0;
        do {
            read = getc (f);

            if (read != '\n') {
                my_maze->maze[lin][col++] = read;
            }
        } while (read != '\n');
        lin++;
    }

    fclose (f);
    return my_maze;
}

int main (int argc, char *argv[]) {
    if (argc > 1) {

        t_maze *m = read_maze (argv[1]);

        return 0;
    }

3 个答案:

答案 0 :(得分:1)

给定一个输入文件:

4 4 1 1
abcd
efgh
ijkl
mnop

您的代码崩溃尝试在我的计算机上分配给my_maze->maze[4][1];因人而异。这超出了数组的界限,因为你使用的是feof() - 所以不要!

您使用feof()是罪魁祸首,而不是检查您的数据是否有效。这段代码解决了最严重的问题;它远非精彩,但它确实能够成功阅读和释放迷宫。

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

enum directions { DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT };

typedef struct
{
    int y, x;
    enum directions d;
    int lins, cols;
    char **maze;
} t_maze;

static 
t_maze *read_maze(char *file)
{
    FILE *f = fopen(file, "r");

    if (!f)
    {
        return NULL;
    }

    t_maze *my_maze = (t_maze *) malloc(sizeof(t_maze));
    my_maze->d = DIR_UP;
    fscanf(f, "%d %d %d %d\n", &(my_maze->lins), &(my_maze->cols), &(my_maze->x), &(my_maze->y));
    if (my_maze->lins <= 0 || my_maze->lins >= 100 ||
            my_maze->cols <= 0 || my_maze->cols >= 100 ||
            my_maze->x < 0 || my_maze->x >= my_maze->cols ||
            my_maze->y < 0 || my_maze->y >= my_maze->lins)
    {
        fprintf(stderr, "Bogus maze parameters (%d, %d, %d, %d)\n",
                my_maze->lins, my_maze->cols, my_maze->x, my_maze->y);
        return NULL;
    }

    my_maze->maze = (char **) malloc(my_maze->lins * sizeof(char *));
    if (my_maze->maze == NULL)
        return NULL;
    for (int lin = 0; lin < my_maze->lins; lin++)
    {
        my_maze->maze[lin] = (char *) malloc((my_maze->cols) * sizeof(char));
        if (my_maze->maze[lin] == NULL)
        {
            fprintf(stderr, "Oops! Memory leaked too!\n");
            return NULL;
        }
    }

    int lin = 0;
    int col = 0;
    int read;
    while ((read = getc(f)) != EOF)
    {
        if (read != '\n')
        {
            if (lin >= my_maze->lins)
            {
                fprintf(stderr, "Too many lines (%d)\n", lin);
                return NULL;
            }
            if (col >= my_maze->cols)
            {
                fprintf(stderr, "Too many columns in line %d\n", lin);
                return NULL;
            }
            my_maze->maze[lin][col++] = read;
        }
        else
        {
            lin++;
            col = 0;
        }
    }

    fclose(f);
    return my_maze;
}

static void free_maze(t_maze *maze)
{
    if (maze != 0)
    {
        for (int i = 0; i < maze->lins; i++)
            free(maze->maze[i]);
        free(maze->maze);
        free(maze);
    }
}

int main(int argc, char *argv[])
{
    if (argc > 1)
    {
        t_maze *m = read_maze(argv[1]);
        free_maze(m);

    }
    return 0;
}

注意EOF检查是如何完成的 - 99.9%的时间,feof()是错误的使用功能(参见while (!feof(file)) is always wrong)。您可以争辩说,对行号太大的检查是在错误的地方 - 您也可以修复它。

请注意,您的代码初始化了除d元素之外的迷宫结构中的所有内容。此代码也为此分配值。它不是崩溃的一个因素 - 虽然它可能会在以后引起问题。

答案 1 :(得分:1)

您可以在getc之后添加ferror()以检查阅读错误。您还需要检查read != EOF。问题是即使在检查feof()后,您也可能会使用getc()到达文件结尾。因此,内部循环必须包含read != EOF条件。

此外,您必须检查lincol,以便不为未分配的内存分配值。

 lin = 0;
 while (!feof (f) && lin < my_maze->lins) {
   int read, col = 0;
   do {
        read = getc (f);

        if (ferror(f)) {
            perror("Reading error");
            exit (1);
        }

        if (read != '\n') {
            my_maze->maze[lin][col++] = read;
        }

    } while (read != '\n' && read != EOF && col <= my_maze->cols);
    lin++;
 }

答案 2 :(得分:0)

OPs代码中的段错误的原因与feof()的使用有关,以控制文件输入循环。关于这个问题有几个很好的讨论here,但我想谈谈手头的具体案例。

手册中说的关于feof()

的内容

The GNU C Library Reference Manual在12.15中说:

  

本章中描述的许多功能都会返回值   宏EOF表示操作完成失败。   由于EOF用于报告文件结尾和随机错误,因此它是   通常最好使用 feof 函数明确检查结束   文件和 ferror 检查错误。这些功能检查   指标是流对象内部状态的一部分,   如果该流上的先前I / O操作检测到适当的条件,则指示符设置。

这意味着在I / O操作尝试读取超过文件末尾之前,不会设置文件结束指示符。您必须尝试读取文件以确定是否可行。如果您需要区分文件结尾和I / O错误,在尝试 I / O操作后,请使用feof()ferror()

feof()如何导致问题

在OPs代码中我们有这个循环:

while (!feof (f)) {
    int read, col = 0;
    do {
        read = getc (f);

        if (read != '\n') {
            my_maze->maze[lin][col++] = read;
        }
    } while (read != '\n');
    lin++;
}

在由feof()控制的循环内部,有一个循环通过换行符读取字符。读取换行符时,将重新访问外部循环条件。因此,当读取最后一个换行符时,feof()计算结果为0,因为尚未设置文件结束指示符,并再次输入外部循环的主体。然后进入内循环,这就是麻烦所在的地方。没有更多新行可供阅读,因此无法退出循环。分段错误迫在眉睫。

还有另一个潜在问题:如果文件读取错误怎么办?这将设置错误指示符,但不会设置文件结束指示符,因此循环将继续尝试读取文件,直到出现段错误。

你可以做些什么

OPs代码中的问题是外部循环体在读取完最后一个字符之后但在文件指示符结束之前执行了一次。为避免此处出现问题,您必须确保在循环内部进行下一次文件读取尝试时不会发生任何错误。一种解决方案是测试EOF的下一个字符读取,并测试循环内的文件读取错误。但这似乎首先打败了使用feof()的目的。使用feof()控制循环然后需要在循环内进行进一步测试是一个好的迹象,表明应该重新考虑循环设计。

更基本的是,问题是while()循环中的条件指定了循环体执行的条件。通常情况是,只要读取尝试成功,我们就希望循环体执行。读取尝试失败可能意味着已达到文件末尾或发生错误,但在任何一种情况下,退出循环可能都是合适的。虽然使用feof()来控制循环可能听起来像是在测试是否已到达文件末尾,但您实际上只是在测试上次读取是否尝试读取文件末尾:读取文件的最后一个字符未设置文件结束指示符。以这种方式控制循环对I / O错误一无所知。

最后一段提示代码清晰度问题。这段代码不清楚,因为循环体中有意义的代码执行的条件在循环语句中不清楚:

while (feof()) {
    [ ... do stuff ... ]
    [ escape the loop if an I/O operation returns EOF or NULL ]
    [ oh, and also make sure to test for errors after an I/O operation ]
    [ and escape the loop if there is an I/O error ]
    [ ... do stuff ... ]
}

这段代码很清楚:

wnile ((c = getc(fp)) != EOF) {
    [ ... do stuff ... ]
}

最简单的解决方案是使用输入函数的返回值直接控制循环。例如,getc()在读取文件末尾或发生错误时返回EOF,fgets()在相同条件下返回NULL。这样,当I / O操作失败时,循环终止,代码可以正常继续。