首页 > 解决方案 > 从命令行读取最后 10 行的程序中的分段错误

问题描述

我目前正在尝试创建一个程序,该程序打印通过命令行传入的另一个文本程序的最后 10 行。函数 read() 应该从文本文件中读取一行,然后返回字符串或 NULL,而 main 函数应该继续分配 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;
}

标签: c

解决方案


主要

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) {

你需要检查fgets的结果,而不是如果line是 NULL 除了检查malloc成功

这不是line[strlen(line)-1] = '\0';没用的,因为fgets放了一个最终的空字符,幸运的是,否则strlen如何工作?

所以阅读可以是:

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

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

   return line;
}

因为 read 可以返回一个 NULL 你需要改变

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

这也是为什么你在 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)

推荐阅读