从命令行读取最后10行的程序中的分段错误

时间:2019-02-23 19:08:44

标签: c

我当前正在尝试创建一个程序,该程序打印通过命令行传入的另一个文本程序的最后10行。函数read()应该从文本文件中读取一行,然后返回字符串或NULL,而主函数应继续分配read()的输出,直到分配了NULL。

我已经看过该程序的许多不同版本,但是它总是最终导致分段错误。我将如何开始调试细分错误?

我已经在下面添加了程序。

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

char *read(){
   char *line = (char *) malloc (80 * sizeof(char));
   fgets(line,80,stdin);
   if (line != NULL) {
      line[strlen(line)-1] = '\0';
      return line;
   }
   else {
      free(line);
      return NULL;
   }
}

int main()
{
   int i=0,j,k,l;
   char **arr = (char **) malloc (100 * sizeof(char *));
   for (k = 0; k < 100; k++) {
      arr[k] = malloc (80 * sizeof(char));
   }

   while(1){
      strcpy(arr[i],read());
      if (arr[i]=NULL) break;
      i++;
      //printf("%s", arr[i]);  //DEBUG
   }

   for (l = 0; l < 100; l++){
      free(arr[l]);
   }
   free(arr);

   for (j = i-11; j < i; j++) {
      printf("%s\n", arr[j]);
   }
   printf("\n");

   return 0;
}

1 个答案:

答案 0 :(得分:1)

主要

while(1){
  strcpy(arr[i],read());
  if (arr[i]=NULL) break;

您永远不会退出循环,因为arr[i]=NULL没有理由从 read 中得知(由于 read 中的错误),因此您编写 arr 中出现不确定的行为(崩溃)

您不能很好地管理read的文件结尾:

fgets(line,80,stdin);
if (line != NULL) {

除了 malloc 成功

,您需要检查 fgets 的结果,而不是 line 为NULL的情况。

不是说line[strlen(line)-1] = '\0';没用,因为 fgets 放置了最后一个空字符,还好 strlen 如何工作?

所以 read 可以是:

char *read(){
  char *line = (char *) malloc (80 * sizeof(char));

   if (line != NULL) {
     if (fgets(line,80,stdin) == NULL) {
       free(line);
       return NULL;
     }
   }

   return line;
}

由于读取可以返回NULL,因此您需要更改

  strcpy(arr[i],read());

并且这也是为什么您同时分配读状态时预分配行的原因吗?当前,您会释放每次读取时分配的内存

似乎最好将循环替换为

while((arr[i] == read()) != NULL)
   //printf("%s", arr[i]);  //DEBUG
   i += 1;
}

并删除行的预分配

另一个问题是,当文件的行数超过100行(或者在很长的时间内可能会切掉某些行)时,这种情况下您会写出 arr 。实际上,您不需要保存太多行,只需要最多保存10行


提案:

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

char *read() {
  char line[80];

  if (fgets(line, 80, stdin) == NULL)
      return NULL;

   return strdup(line);
}

int main()
{
  char * arr[10] = { NULL };
  char * p;
  int i = 0, n = 0;

  while((p = read()) != NULL) {
    n += 1;
    free(arr[i]);
    arr[i] = p;
    if (++i == 10)
      i = 0;
  }

  /* get older line index and line count to print */
  if (n <= 10) 
    i = 0;
  else
    n = 10;

  while (n-- != 0) {
    puts(arr[i]);
    free(arr[i]);
    if (++i == 10)
      i = 0;
  }

   return 0;
}

Compilation and execution :
pi@raspberrypi:/tmp/d $ gcc -g -pedantic -Wextra q.c
pi@raspberrypi:/tmp/d $ ./a.out < q.c
    n = 10;



  while (n-- != 0) {

    puts(arr[i]);

    if (++i == 10)

      i = 0;

  }



   return 0;

}

请注意,我没有删除\ n是读取行,而是使用 puts ,因此打印了空行,但由于太长而没有剪切读取行

如果文件短于10行:

pi@raspberrypi:/tmp/d $ tail -n 5 q.c | ./a.out
      i = 0;

  }



   return 0;

}

valgrind 下:

pi@raspberrypi:/tmp/d $ valgrind ./a.out < q.c
==18496== Memcheck, a memory error detector
==18496== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18496== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18496== Command: ./a.out
==18496== 


  while (n-- != 0) {

    puts(arr[i]);

    free(arr[i]);

    if (++i == 10)

      i = 0;

  }



   return 0;

}

==18496== 
==18496== HEAP SUMMARY:
==18496==     in use at exit: 0 bytes in 0 blocks
==18496==   total heap usage: 43 allocs, 43 frees, 5,699 bytes allocated
==18496== 
==18496== All heap blocks were freed -- no leaks are possible
==18496== 
==18496== For counts of detected and suppressed errors, rerun with: -v
==18496== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 3)