C gets/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: %in", 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);
    }
}

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

您可以使用 getchar() 读取main中的选项,但在键入 Enter 键后,字节将返回到程序。 仅返回行中的初始字符,其余字符保留在流中。

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

int main() {
    Person *pers1 = (Person*)malloc(sizeof(Person));
    pers1->next = NULL;
    pers1->unit = 0;
    pers1->name[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,在列表的末尾保留一个虚拟的未初始化结构是不正确的。 您可以通过将指向列表头的指针传递给 neuePerson 来处理对列表头的更新。

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

int main()
{
   /** your While loop */
   Person *nextp = pers1;
   do {
       free(nextp);
       nextp = nextp->next;
   } while (nextp != NULL);
}

链接列表逻辑与其他所有内容分开是个好主意。你会很高兴你现在做了,当你的程序变得更大时。

另外,与瓦尔格林德成为朋友。

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

永远不要使用 gets()。因为如果不事先知道数据,就不可能知道 gets() 将读取多少个字符,并且因为 gets() 会在缓冲区结束时继续存储字符,所以使用起来非常危险。它已被用于破坏计算机安全性。请改用 fgets()。

回答您的问题之前,我将冒昧地将您的代码重写为最小集。您正在测试 gets,因此我可以在之后删除所有内容,以及代码中在 get 之前未调用的所有内容。我还会把你的电话从neuePerson转移到main。我还将避免堆内存,以简单地进一步,我相信您能弄清楚如何正确使用堆。最后,我真的不喜欢使用没有退出代码的未初始化结构或主例程,所以我也会这样做。看起来像这样:

#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 来称呼 fget?您已经知道"50 是我允许输入的最多"。如果最终有理由进行优化,您可以稍后再回来进行优化。

我还想提一下,确实,这句台词,

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

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

最新更新