c得到/ fgets不起作用

时间:2016-01-23 20:37:45

标签: c list fgets

gets在函数neuePerson中不起作用, 它在for循环中工作,但后来我改变它,现在编译器说不是未定义的。

我用fgets尝试过,现在没有警告,但它仍然忽略fgets,所以我不能在控制台中写任何东西。

main函数gets中的

有效。我有点困惑......:o

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "readline.h"

//typedef struct Person {
//    char name[50];
//    char unit;
//    int number;
//} Person;

typedef struct person {
    char name[50];
    char unit;
    int number;
    struct person *next;
} Person;

void neuePerson(Person *firstPerson) {
    time_t t; 
    time(&t);
    srand((unsigned int)t);
    while (firstPerson->next != 0)
        firstPerson = firstPerson->next;
    printf("Gib  einen Namen ein \n");
    fgets(firstPerson->name, 50, stdin);                        
    firstPerson->number = rand() % 99 + 1; 
    firstPerson->unit = rand() % 3 + 65;
    firstPerson->next = (Person*)malloc(sizeof(Person));
    firstPerson = firstPerson->next;
    firstPerson->next = 0;
}

void ausgabe(Person *anfang) {
    while (anfang->next != 0) {
        printf("Name: %s", anfang->name);
        printf("   Abteilung: %c", anfang->unit);
        printf("   Tel.Nummer: %i\n", anfang->number);
        anfang = anfang->next;
    }
}

int main() {
    Person* pers1 = (Person*)malloc(sizeof(Person));
    //Person* test = (Person*)malloc(sizeof(Person));
    //gets(test->name, 50);
    //printf("%s", test->name);
    pers1->next = 0;
    char z = 'n';
    while (z != 'e') {
        printf("[n]eue Person, [a]usgabe,  [e]nde");
        z = getchar();
        if (z == 'n') neuePerson(pers1);
        else if (z == 'a') ausgabe(pers1);
    }
}

3 个答案:

答案 0 :(得分:2)

问题来自标准输入的行缓冲:

您使用StateChange读取了main中的选项,但在输入回车键后,该字节将返回到您的程序。只返回行中的初始char,其余的都保留在流中。

当您随后使用getchar()读取此人的姓名时,它会返回一个空行,因为它会获取仍在流中的fgets()。与流行的看法相反,\n 解决方案,因为它具有未定义的行为。更好的解决方案是以这种方式阅读选项:

fflush(stdin)

您应该改进列表处理:空列表应该只是int main() { Person *pers1 = (Person*)malloc(sizeof(Person)); pers1->next = NULL; pers1->unit = 0; pers1->name[0] = '\0'; for (;;) { int z, c; printf("[n]eue Person, [a]usgabe, [e]nde "); z = c = getchar(); while (c != EOF && c != '\n') c = getchar(); if (z == EOF || z == 'e') break; else if (z == 'n') neuePerson(pers1); else if (z == 'a') ausgabe(pers1); } } ,在列表末尾保留未初始化的虚拟结构是不正确的。您可以通过将指针传递到NULL来处理列表头的更新。

答案 1 :(得分:1)

我同意chqrlie的回答;另外,在退出主循环后,不要忘记释放列表:

int main()
{
   /** your While loop */

   Person *nextp = pers1;
   do {
       free(nextp);
       nextp = nextp->next;
   } while (nextp != NULL);
}

将链表逻辑与其他所有逻辑分开是个好主意。你现在和你的计划变得更大时,你会很高兴。

此外,与valgrind成为朋友。

答案 2 :(得分:0)

首先,既然你询问了get和fgets,那么我就可以从手册页引用:

  

永远不要使用gets()。因为在不事先知道数据的情况下无法判断get()将读取多少个字符,并且因为gets()将继续存储超出缓冲区末尾的字符,所以使用它是非常危险的。它已被用来打破计算机安全。改为使用fgets()。

在回答你的问题之前,我将冒昧地将你的代码重新编写到最小集合中。你正在测试获取,所以我可以在之后删除所有内容,并且代码中之前未调用的所有内容都会获得。我也会把你的呼叫从neuePerson转移到主要的。我还会进一步避免堆内存,我相信你能弄清楚如何正确使用堆。最后,我真的不喜欢使用没有退出代码的未初始化的结构或主程序,所以我也会这样做。看起来像这样:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "readline.h"

typedef struct person{
    char name[50];
    char unit;
    int number;
    struct person* next;
} Person;

int main() {
    Person _pers1, *pers1 = &_pers1;
    char z = 'n';

    memset(pers1, 0, sizeof(Person));

    while (z != 'e') {
        z = getchar();
        pers1->name = fgets(pers1->name, 50, stdin);
    }

    return 0;
}

在较高的层面上,问题是你有两种方法以不同的方式处理字符串。您已经看到了一个解决方案,它采用了一种方法 - getchar - 并使其像另一种方法一样工作 - 在这种情况下缓冲区大小为1的fgets。但是在很多情况下你可能没有足够的信息来解释这两种方法。在这种情况下,例如,如果您根本不知道换行符中是否存在换行符,或者您使用的语言编程中fgets具有可编程停止而不是仅停留在换行符上,则您的原始方法可能具有更加明智。

所以在这种情况下,当两种方法不合作时,通常总是使用相同的方法。看起来像这样:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "readline.h"

typedef struct person{
    char name[50];
    char unit;
    int number;
    struct person* next;
} Person;

int main() {
    Person _pers1, *pers1 = &_pers1;
    char z[50];

    memset(pers1, 0, sizeof(Person));
    memset(z, 0, sizeof(char) * 50);

    while (z[0] != 'e') {
        fgets(z, 50, stdin);
        fgets(pers1->name, 50, stdin);
    }

    return 0;
}

使z 50字节大当然是矫枉过正。我这样做是为了说明一个原则。如果你在任何地方以相同的方式使用相同的方法,你就不会遇到问题。你不需要问诸如“等待z需要1或2个字节?我应该用2还是1调用fgets?”之类的问题。你已经知道“50是我允许输入的最多”。如果最终成为优化的理由,您可以稍后再回来进行优化。

我也想提一下,这一行确实如此,

    while (z[0] != 'e') {

有一些缺陷。查看“e”以外的值会更正确。我推荐0,EOF,'\ n'和'\ r'。但是你提前知道的唯一一个是0,因为你设定了它。在我看来,最好发现其他人需要通过测试和使用你的代码来添加,而不是“厨房沉没”你的代码以避免问题发生之前。