fgets()接收来自C中stdin的输入的分段错误

时间:2017-05-09 11:45:40

标签: c segmentation-fault fgets

我的gdb调试器出现问题,每次尝试运行程序时,调试器都会在我使用“fgets”()的行中发出以下错误:_IO_fgets(buf = 0x7fffffffe330“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()有什么问题。有人有想法吗?

2 个答案:

答案 0 :(得分:1)

您对fgets的使用看起来对我来说是正确的,但您的代码中还有另一个问题:n已关闭。

替换

int n = 1;

通过

int n = 0;

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

但无论如何这更好:

您应该在for循环中使用此功能。

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

该列表由NULL字段中的next指针终止。因此,您应检查条件,而不是测试元素数量。

答案 1 :(得分:0)

使用Valgrind我得到了

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

如果p为NULL,则问题是指令sum += p->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 ;

超过n必须从int get_nums(node ** head)函数

中的0开始

问候!

@Walz在这种情况下,您需要将指针传递给指针,否则 如果 你释放了打印功能里面的节点,从print_avg函数之后的函数外部的头指针不会指向你的列表的右头。 现在让我们说我有一个指向MY_LIST的指针A然后我在print_avg中复制指针A来处理MY_LIST,因为我释放MY_LIST中的节点并且我从print_avg返回ponter A没有改变!因此它将指向前一个头(已被释放),因此如果我尝试通过指针A访问它,则会出现一个分段错误。

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

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

问候!

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