【问题标题】:Segmentation fault with fgets() receiving input from stdin in Cfgets() 从 C 中的标准输入接收输入的分段错误
【发布时间】:2017-05-09 11:45:39
【问题描述】:

我的 gdb 调试器出现问题,每次我尝试运行程序时,调试器都会在我使用“fgets”() 的那一行给出以下错误:_IO_fgets (buf=0x7ffffffffe330 "P \343\377\377\377\177", n=2, fp=0x0)

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

typedef struct _node {
   int value ;
   struct _node * next ;
} node ;

void print_avg(node * head, int n)
{
    int sum = 0 , i = 0;
    node * p = head ;
    for (i = 0 ; i < n ; i++) {
            sum += p->value ;
            p = p->next ;
    }
    printf("%f\n", ((float)sum / (float)n)) ;
}

int get_nums(node ** head)
{
    int n = 1 ;
    char line[4]  ;

    while (fgets(line, sizeof(line), stdin) != NULL) {
            //strtok(line, "\n") ;

            node * curr ;
            curr = (node *) malloc(sizeof(node)) ;
            curr->value = atoi(line) ;
            curr->next = *head ;
            *head = curr ;

            n++ ;
    }
    return n ;
}


int main()
{
    int n ;
    node * head = NULL;

    n = get_nums(&head) ;
    print_avg(head, n) ;
    return 0 ;
}

我不知道我的 fgets() 出了什么问题。有人有想法吗?

【问题讨论】:

  • 也许用类似 valgrind 的东西运行代码?这会告诉你你在哪里浪费了内存。
  • 是的。我用双指针修复了每个迭代问题中的覆盖头(我编辑了帖子),但问题是为什么我会看到 fgets() 出现这种错误?因为我在该行中没有看到任何逻辑或语法错误。
  • @MasterGL 您对fgets 的使用对我来说看起来是正确的。在下面阅读我的答案。如果fgets 仍然存在段错误,请尝试char line[100]; 并报告问题是否消失。并且请检查您调试的代码是否是您编译的代码,并检查您编译的代码是否是您这里显示的代码,我们在这里经常看到这种错误。
  • "n=2" 很奇怪,因为您的代码将 line 定义为大小为 4。

标签: c segmentation-fault fgets


【解决方案1】:

您对fgets 的使用在我看来是正确的,但您的代码中还有另一个问题:n 差了一个。

替换

int n = 1;

通过

int n = 0;

因为最初列表包含 0 个元素,而不是 1 个元素。

不过这样更好:

你应该在你的for循环中使用这个。

for (node * p = head; p != NULL; p = p->next)

列表由next 字段中的NULL 指针终止。所以你应该检查 this 条件而不是测试元素的数量。

【讨论】:

  • Michael 我已经完全按照您告诉我的操作,将 char 行 [100] 中的索引值更改为 100。但是 fgets 错误仍然存​​在。它仍然显示相同的错误消息。您认为这里真正的问题是什么?
  • @MasterGL 我不知道可能出了什么问题。您使用fgets 的方式是正确的。你的平台是什么(操作系统、编译器、IDE、所有这些的版本等)?
  • 我只使用 gcc 编译器来编译文件,我目前正在使用 gdb 调试器进行调试。我将 main 函数中的 get_num() 行设置为第一个断点,当我转到 get_num 中的 fgets() 时,gdb 总是出错
  • 哪个版本,哪个操作系统(Linux、Windows 7、8、10....其他)。提供所有细节。
  • 使用 Windows 10 但我在 Ubuntu 实例上运行该程序。我只是将它用作我自己的代码服务器
【解决方案2】:

使用 Valgrind 我得到了

Access not within mapped region at address 0x0 at 0x4006D8: print_avg (main.c:16)

如果 p 为 NULL,则问题是指令 sum += p-&gt;value

p 为 NULL,因为 head 为 NULL,因为 int get_nums(node * head) 创建了指向 head 的指针的副本,所以当您执行 head = curr ; 时,您实际上是将指针的值存储到副本中,因此当您返回 main 时第一个头指针仍然为 NULL,并且您有内存泄漏,因为您无法释放分配的内存(因为指针现在丢失了)。

这是你需要的真正的工作代码

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

typedef struct _node {
  int value ;
  struct _node * next ;
 node ;

void print_avg(node ** head, int n)

   int sum = 0 , i = 0;
   node *p = *head ;
   //better check p value here to avoid segmentation fault
   for (i = 0 ; i < n && p!=NULL; i++) {
           sum += p->value ;
           p = (*head)->next ;
           free(*head);
           *head = p;

   }
   printf("%f\n", ((float)sum / (float)n)) ;


int get_nums(node ** head)

   int n = 0 ;
   char line[4]  ;

   while (fgets(line, sizeof(line), stdin)) {
           //strtok(line, "\n") ;

           node * curr ;
           curr = (node *) malloc(sizeof(node)) ;
           curr->value = strtol(line,NULL,10) ;
           curr->next = *head ;
           *head = curr ;

           n++ ;
   }
   return n ;



int main()

   int n ;
   node * head = NULL;
   //here we pass the address of head pointer so that the function will be able to set it
   n = get_nums(&head) ;
   //here we pass the address of head pointer so that the function will be able to free your list of pointers
   print_avg(&head, n) ;
   return 0 ;

在你的int get_nums(node ** head)函数中,超过n必须从0开始

问候!

@Walz 在这种情况下,您需要将指针传递给指针,否则 如果 您释放 print 函数内部的节点,函数外部的头指针在 print_avg 函数之后不会指向列表的右头。 现在假设我有一个指向 MY_LIST 的指针 A,然后我在 print_avg 中复制指针 A 来处理 MY_LIST,因为我释放了 MY_LIST 中的节点并且我从 print_avg 返回时,指针 A 没有改变!所以它将指向前一个头(已被释放),所以如果我尝试通过指针 A 访问它,一个分段错误指日可待。

当您确定程序中不再需要分配的资源时,您应该始终释放它们。由于我们正在访问列表,因此释放列表的最有效方法是逐个节点释放它,因为您已使用该值来计算平均值,但您可以稍后随时释放它们,也许在另一个函数中。

最后,由于您的主要目的是释放资源,因此在这里释放资源并不是那么重要,但您应该习惯于在松开指向它们的指针之前释放已分配的资源。想象一下,您正在编写一个多线程服务器,并且每个连接甚至丢失一个字节。迟早你会内存不足。

问候!

@MasterGL 我不知道这是否是您的问题,但是如果我在 Eclipse 中运行调试器,并且当我收到 fgets 指令时我确实“进入”了,我也会收到一些错误(也许调试器没有有 fgets 源代码显示)。我用“Step over”做了,我只用“Step Into”进入我的功能。效果很好

【讨论】:

  • 不确定释放print_avg 中的节点是否是个好主意。以及为什么要传递指向print_avg 的指针?
  • 在您的代码 Whitefield 中出现此类错误。不确定此代码是否解决了 fgets() 问题。但是感谢您的帮助! _IO_fgets (buf=0x7ffffffffe330 "P\343\377\377\377\177", n=-136403616, fp=0x0) at iofgets.c:40
  • 您好 MasterGL,我认为您的库可能有问题。我必须说我运行了您的原始代码并发现了一些编程问题(最糟糕的是您分配然后设置然后松开指向内存区域的指针时的内存泄漏),但我无法重现您的错误由于 Valgrind(我建议您作为调试器)刚刚发现了一个分段错误,原因是您的 for 循环中缺少检查以及 n 的错误初始化。实际上在调试器上运行它没有错误,也没有内存泄漏。
猜你喜欢
  • 1970-01-01
  • 2018-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-19
相关资源
最近更新 更多