关于fgetc()的C原因的分段错误

时间:2019-03-08 18:03:39

标签: c arrays pointers

我正在尝试使用C语言编写寻路算法。我正在尝试扫描试图找到最短路径的地图,并将其放入2D动态数组中。

这是我的基本地图:

5 5
11111
01001
00101
00010
00001

5是行数,其他5是列数。 1是方式,0是墙。 这是我的代码:

#include <stdio.h>
#include <stdlib.h>
int row;
int column;

void main()
{
    FILE *fp;
    int **array;
    fp = fopen("file.path", "r");
    fscanf(fp, "%d", &row);
    fgetc(fp);
    fscanf(fp, "%d", &column);
    array = (int **)malloc(row * sizeof(int));
    for(int i=0; i<row; i++)
        array[i] = malloc(column * sizeof(int));

    for(int i=0; i<row; i++)
    {
        for(int j=0; j<column; j++)
        {
            char tmp = fgetc(fp);
            array[i][j] = (tmp - '0');
        }
    }
}

此代码返回分段错误。

2 个答案:

答案 0 :(得分:1)

  

此代码返回分段错误。

请参阅下面有关 malloc 的评论


代码顶部到底部的一些注释

int row;
int column;

为什么这两个全局变量?它们可以在 main

中是本地的

fp = fopen("file.path", "r");
fscanf(fp, "%d", &row);
fgetc(fp);
fscanf(fp, "%d", &column);

您需要检查 fp 不是NULL才能确保打开成功

您需要检查 fscanf 返回1以确保您能够读取数字

它们之间的 fgetc 是没有用的,第二个 fscanf

会绕过该空间

请注意,您可以执行独特的 fscanf 来读取两个数字:

if (fscanf(fp, "%d %d", &row, &column) != 2) {
  ... manage error to not continue the execution
}

 array = (int **)malloc(row * sizeof(int));

如前所述,您分配了int*数组而不是 int 数组,这些数组解释了稍后执行中的分段错误(可见于64b),强制转换没有用,所以array = malloc(row * sizeof(int*));

您还需要检查malloc不返回NULL

       char tmp = fgetc(fp);
        array[i][j] = (tmp - '0');

tmp 必须是 int 才能检查EOF情况

请注意,您也可以读取字符串,而不是逐字符读取

警告当前 tmp 会获得换行符,因此您错误地初始化了数组


提案:

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

int main()
{
    FILE *fp;
    int ** array;
    int row;
    int column;

    fp = fopen("file.path", "r");
    if (fp == NULL) {
      puts("cannot open file.path");
      return -1;
    }

    if (fscanf(fp, "%d %d", &row, &column) != 2) {
      puts("cannot read row column");
      fclose(fp);
      return -1;
    }

    array = malloc(row * sizeof(int*));

    if (array == NULL) {
      puts("not enough memory");
      fclose(fp);
      return -1;
    }

    for (int i=0; i<row; i++) {
      array[i] = malloc(column * sizeof(int));
      if (array[i] == NULL) {
        puts("not enough memory");
        fclose(fp);
        return -1;
      }
    }

    for (int i=0; i<row; i++)
    {
      /* finish to read previous line */
      int c;

      while ((c = fgetc(fp)) != '\n') {
        if (c == EOF) {
          fclose(fp);
          puts("unexpected EOF");
          return -1;
        }
      }

      for(int j=0; j<column; j++)
      {
        c = fgetc(fp);

        if (c == EOF) {
          puts("unexpected EOF");
          fclose(fp);
          return -1;
        }
        if ((c != '0') && (c != '1')) {
          puts("invalid value");
          fclose(fp);
        }

        array[i][j] = (c - '0');
      }
    }
    fclose(fp);

    /* show the content for at least debug */
    puts("array is:");

    for (int i=0; i<row; i++)
    {
      for (int j=0; j<column; j++)
        printf("%d ", array[i][j]);
      putchar('\n');
    }

    /* free resources */
    for (int i=0; i<row; i++)
    {
      free(array[i]);
    }
    free(array);
}

编译和执行:

pi@raspberrypi:~ $ gcc -pedantic -Wextra b.c
pi@raspberrypi:~ $ cat file.path 
5 5
11111
01001
00101
00010
00001
pi@raspberrypi:~ $ ./a.out
array is:
1 1 1 1 1 
0 1 0 0 1 
0 0 1 0 1 
0 0 0 1 0 
0 0 0 0 1 
pi@raspberrypi:~ $ 

valgrind 下执行:

pi@raspberrypi:~ $ valgrind ./a.out
==4841== Memcheck, a memory error detector
==4841== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4841== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==4841== Command: ./a.out
==4841== 
array is:
1 1 1 1 1 
0 1 0 0 1 
0 0 1 0 1 
0 0 0 1 0 
0 0 0 0 1 
==4841== 
==4841== HEAP SUMMARY:
==4841==     in use at exit: 0 bytes in 0 blocks
==4841==   total heap usage: 9 allocs, 9 frees, 5,592 bytes allocated
==4841== 
==4841== All heap blocks were freed -- no leaks are possible
==4841== 
==4841== For counts of detected and suppressed errors, rerun with: -v
==4841== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)

注意:我先使用 fscanf 然后使用 fgetc ,由于混合的原因,有必要管理换行符,另一种方法是始终使用 getline 读取每行的内容,让昏暗的人使用 sscanf

答案 1 :(得分:0)

它在没有segfault的情况下可以在我的测试中编译并运行(使用VC ++ 2017),您需要在调试器中运行它以确定目标失败的位置和原因。

但是,无论在任何情况下,您都应通过多种方式修改或使用不正确的代码。

  • 它将无法在目标sizeof(int) != sizeof(int*)上分配正确的空间量。
  • 由于未处理换行符,包括第一行5 5之后的换行符,您的地图将被错误地加载。
  • 两个fgetc(fp);之间的fscanf毫无用处,因为在任何情况下fscanf()都会丢弃前导空格。
  • rowcolumn不一定是全局的。
  • 检查任何内容都没有错误。
  • 在C语言中,不需要铸造malloc()

考虑:

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

int main()
{
    FILE* fp = fopen("file.path", "r") ;

    if( fp != 0 )
    {
        int row = 0;
        int column = 0;
        int check = fscanf( fp, "%d%d\n", &row, &column ) ;

        if( check == 2 )
        {
            bool map_valid = false;
            int** array = malloc( row * sizeof(*array) ) ;
            if( array != NULL )
            {   
                map_valid = true ;
                for( int i = 0; map_valid && i < row ; i++ )
                {
                    array[i] = malloc( column * sizeof(*array[i]) ) ;
                    map_valid = (array[i] != NULL) ;
                }
            }

            for( int i = 0; map_valid && i < row; i++ )
            {
                int c = 0 ;
                for( int j = 0; map_valid && j < column; j++ )
                {
                    c = fgetc(fp);
                    map_valid = c != EOF ;
                    array[i][j] = (c - '0');
                }
                    while( map_valid  && (c = fgetc(fp)) != '\n' && c != EOF ){}
            }

            if( map_valid )
            {
                // ...
            }

            if( array != NULL )
            {
                for( int i  = 0; i < row; i++ )
                {
                    if( array[i] != NULL )
                    {
                        free( array[i] ) ;
                    }
                }
                free( array ) ;
            }
        }
    }

    return 0 ;
}