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);
}
}
答案 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,因为你设定了它。在我看来,最好发现其他人需要通过测试和使用你的代码来添加,而不是“厨房沉没”你的代码以避免问题发生之前。