feof() 和 fgetc() 的 C 分段错误

C segmentation fault errors with feof() and fgetc()

提问人:learning 提问时间:4/2/2011 最后编辑:Lou Francolearning 更新时间:4/2/2011 访问量:5158

问:

谁能帮我解决我的困境?当我编译我的程序时,我没有收到任何错误或警告。但是,当我实际运行可执行文件时,我收到分段错误。如果我理解正确,发生这种情况是因为指针被错误地使用。我在feof(srcIn)行上遇到一个特定的错误,我不知道为什么。除了程序开头的 srcIn = fopen(argv[0], “r”) 值之外,FILE* srcIn 永远不会被分配一个新值。我最初是用 C++ 实现这个解决方案的,但由于某些原因需要将其更改为 C。无论如何,在C++中,除了使用srcIn.eof()作为条件和srcIn.get(something)作为读取方法之外,我基本上做了完全相同的事情。它编译和运行没有任何问题。

int chara;
int line[maxLineLength+1];

void nextch(void){
    const int charPerTab = 8;
    if(charCounter == charLineCounter){
      if(feof(srcIn)){
          printf("\n");
          isEOF = TRUE;
          return;
      }

      printf("\n"); lineCounter++;
      if(chara != '\0'){ printf("%c", line[charLineCounter-1]); } // first character each line after the first line will be skipped otherwise
      charLineCounter = 0; charCounter = 0;
      while(chara != '\n'){
         chara = fgetc(srcIn);
         if(chara >= ' '){
            printf("%c", chara);
            line[charLineCounter] = chara; charLineCounter++;
         }
         else if(chara == '\t'){  // add blanks to next tab
            do{ printf(" "); line[charLineCounter] = ' '; charLineCounter++; }
            while(charLineCounter % charPerTab != 1);
         }
      }
      printf("\n"); line[charLineCounter] = chara; charLineCounter++; line[charLineCounter] = fgetc(srcIn); charLineCounter++;
                                                                      // have to get the next character otherwise it will be skipped
   }
   chara = line[charCounter]; charCounter++;
}

编辑: 我忘了提到,当我遇到 seg 故障时,我什至没有真正进入主线。这让我相信可执行文件本身存在某种问题。gdb 告诉我 SEG 故障发生在 Line 上:有什么想法吗?if(feof(srcIn))

C 编译器错误 feof fgetc

评论

2赞 Oliver Charlesworth 4/2/2011
您的问题标题显示您遇到编译错误。你的问题本身说它编译得很好。它是什么?
0赞 Ronny Brendel 4/2/2011
调试器说什么?段错误在哪一行?
1赞 eat_a_lemon 4/2/2011
srcIn 是全局变量吗?它在哪里定义?
0赞 learning 4/2/2011
调试器在 feof(srcIn) 的行上说我遇到了 seg 错误。是的,srcIn 是一个全局变量,在 main 中设置为 fopen() 的值。哦,一开始我无法编译它,但现在它正在编译,对不起标题。
0赞 Lou Franco 4/2/2011
可执行文件中的问题几乎肯定是你的代码,而 C 程序从 main() 开始——所以没有它它就无法到达 if 行。对于分段错误,它发生的线是一个非常小的线索 - 它是最终检测到内存损坏的地方 - 不一定与它发生的位置有关。

答:

1赞 mu is too short 4/2/2011 #1

argv[0]是程序的名称,因此您的程序可能失败。我猜你想打开。当然,在尝试使用其返回值之前,请检查是否成功。fopen(argv[0], 'r')argv[1]fopen

1赞 Lou Franco 4/2/2011 #2

它可能不在这个函数中,但如果问题出在这里,我最怀疑的是在线越界。你有没有写过比字更多的字?在索引到行之前,您应该进行检查。maxLineLength

编辑:您似乎对这个错误意味着什么感到困惑 - 我会尝试清除它。

当您遇到分段错误时,发生分段错误的行只是最终检测到内存损坏的代码行。它不一定与真正的问题有任何关系。您需要做的是首先弄清楚腐败发生的位置。

非常常见的原因:

  1. 多次在指针上调用 free 或 delete
  2. 在指针上调用错误的删除(delete 或 delete[])
  3. 使用未初始化的指针
  4. 在调用 free 或 delete 后使用指针
  5. 超出数组的边界(我认为你就是这样做的)
  6. 将指针转换为错误的类型
  7. 执行无法正确重新解释目标类型的reinterpret_cast
  8. 使用不正确的调用约定调用函数
  9. 保留指向临时对象的指针

还有很多其他方法。

弄清楚这一点的关键是

  1. 假设您的代码是错误的
  2. 通过在代码路径中检查(如果很短)来查找此类问题
  3. 使用可以告诉你在代码行中遇到这些问题的工具
  4. 意识到发生分段错误的代码行不一定是错误。
1赞 Null Set 4/2/2011 #3

它可能应该是相反的。main 获取的 th string 参数通常是程序的名称,st 参数是传递给程序的第一个命令行参数。srcIn = fopen(argv[1], "r")01

评论

0赞 learning 4/2/2011
在主要情况下,可能不是最佳实践,我递增了 arg 并递减了 argc,然后检查了 argc>0。如果是,那么我将现在的 argv[0] 分配给文件流 srcIn。这是因为程序将采用带有文件名的第二个参数,或者假设用户将通过 stdin 输入信息。
2赞 sarnold 4/2/2011 #4

我有一个挥之不去的怀疑,即你的两个或四个字符的缩进不足以让你看到程序的真正范围;它可能就像@mu太短@Null一样简单 Set 指出,你有一个 when you mean ,它可能像 Franco 指出的那样@Lou你正在写过数组的末尾,但这段代码确实闻起来很有趣。下面是您的代码,运行以获得更大的选项卡和每行一个语句:argv[0]argv[1]Lindent

int chara;
int line[maxLineLength + 1];

void nextch(void)
{
    const int charPerTab = 8;
    if (charCounter == charLineCounter) {
            if (feof(srcIn)) {
                    printf("\n");
                    isEOF = TRUE;
                    return;
            }

            printf("\n");
            lineCounter++;
            if (chara != '\0') {
                    printf("%c", line[charLineCounter - 1]);
            }               // first character each line after the first line will be skipped otherwise
            charLineCounter = 0;
            charCounter = 0;
            while (chara != '\n') {
                    chara = fgetc(srcIn);
                    if (chara >= ' ') {
                            printf("%c", chara);
                            line[charLineCounter] = chara;
                            charLineCounter++;
                    } else if (chara == '\t') {     // add blanks to next tab
                            do {
                                    printf(" ");
                                    line[charLineCounter] = ' ';
                                    charLineCounter++;
                            }
                            while (charLineCounter % charPerTab != 1);
                    }
            }
            printf("\n");
            line[charLineCounter] = chara;
            charLineCounter++;
            line[charLineCounter] = fgetc(srcIn);
            charLineCounter++;
            // have to get the next character otherwise it will be skipped
    }
    chara = line[charCounter];
    charCounter++;
}

您正在检查是否已在语句中读取了顶部文件的末尾,但您永远不会再次检查。从不。当您从循环中的输入读取时,您用作退出条件,如果字符在上面,则打印输出,如果您读取 ,则执行一些制表符扩展,并且您忘记处理返回 来自 。如果你的输入文件没有 ,那么这个程序可能会写入你的数组,直到你出现段错误。如果你的输入文件没有直接以 结束,这个程序可能会写入你的数组,直到你出现段错误。ifeofwhile()'\n'' ''\t'EOFfgetc(3)'\n'-1line'\n'-1line

大多数从输入流中读取一个字符并对其进行操作的循环是这样编写的:

int c;
FILE *f = fopen("foo", "r");

if (!f) {
    /* error message if appropriate */
    return;
}

while ((c=fgetc(f)) != EOF) {
    if (' ' < c) {
        putchar(c);
        line[counter++] = c;
    } else if ('\t' == c) {
        /* complex tab code */
    } else if ('\n' == c) {
        putchar('\n');
        line[counter++] = c;
    }
}

检查输入。如果可以的话,只能从一个位置读取输入。使用一个表或 /// 树来决定如何处理输入字符。一开始使用这个成语可能并不自然,但它在 C 中很常见。EOFifelse ifelse ifelsearray[index++] = value;

随意窃取我建议的循环格式作为您自己的代码,并弹出复杂的选项卡扩展代码。看起来你做对了,但我对此并不乐观,我不希望它分散循环的整体风格。我想你会发现扩展我的代码来解决你的问题比让你的问题更容易。(我完全希望你可以,但我认为维护起来不会很有趣。