我不知道为什么它不会按应有的方式运行,我使用了 gets() 和 fgets(),发生相同的结果 [重复]

I don't know why it would not function as it should be, I used gets() and fgets(), the same result happen [duplicate]

提问人:tdm2k 提问时间:11/11/2023 最后编辑:Joshuatdm2k 更新时间:11/11/2023 访问量:64

问:

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

struct student{
    char name[20];
    char studentNumber[5];
    int yearOfBirth;
    float avgPoint;
};

int main(){
    struct student hssv[3];
    int i;
//input info for students, this case its 3 students
    for(i=0; i<3; i++){
        printf("Input element value: ");
        printf("\nInput your name: ");
        fflush(stdin);
        fgets(hssv[i].name, sizeof(hssv[i].name), stdin); //this line doesnt work for the loop
        printf("Input your student code: ");
        fflush(stdin);
        fgets(hssv[i].studentNumber, sizeof(hssv[i].studentNumber), stdin);
        printf("Input your year of birth: ");
        scanf("%d", &hssv[i].yearOfBirth);
        printf("Input your point: ");
        scanf("%f", &hssv[i].avgPoint);
    }
//output info
    for(i=0; i<3; i++){
        printf("\nOutput element value: ");
        printf("\nYour name: %s", hssv[i].name);
        printf("\nInput your student code: %s", hssv[i].studentNumber);
        printf("\nInput your year of birth: %d", hssv[i].yearOfBirth);
        printf("\n\nInput your point: %f", hssv[i].avgPoint);
    }

    return 0;
}

我不知道为什么它不能按应有的方式运行,我使用了 get(),但随后有一个警告说我应该使用 fgets()。然后我查了一下,用了它,但同样的结果不断发生。 name 的输入在循环中的第 2 次以后不起作用,我猜是缓冲区溢出?但是我已经在使用 fflush()。请帮忙...

c 扫描

评论

3赞 Allan Wind 11/11/2023
gets()是不安全的,是要走的路。请修改问题并解释问题所在。fgets()
2赞 yano 11/11/2023
不要 fflush(stdin),这是未定义的行为
0赞 yano 11/11/2023
至于你的问题,这个和它的重复项应该会有所帮助
0赞 Joshua 11/11/2023
@AllanWind:我普遍认为这是一个过拟合问题。事实上,我们可以使 scanf 工作并不意味着我们应该这样做。
2赞 phuclv 11/11/2023
为什么 gets 函数如此危险,以至于不应该使用它? 很久以前就从C标准中删除了gets

答:

1赞 Joshua 11/11/2023 #1

不要使用 .它已被撤回。gets()

fgets()将在结束时停止(这就是参数获取的目的)。 如果用户输入太多并践踏之后的任何内容,则会继续运行。服务器以这种方式被征用。hssv[i].namesizeof(hssv[i].name)gets()

您的代码不起作用,因为调用在输入缓冲区中留下了换行符。有一些方法可以解决这个问题;我的建议是永远不要使用,但只能这样:scanf()scanfsscanf

char linebuf[21]; // Reuse for all number reads
// ...
fgets(linebuf, sizeof(linebuf), stdin);
sscanf(linebuf, "%d", &hssv[i].yearOfBirth);

摆脱 ;它并不总是有效。fflush(stdin);

mod 注意事项:虽然我们通常会删除感谢评论;在这种情况下,尝试这样做会使评论线程毫无意义。

评论

0赞 tdm2k 11/11/2023
我一直在使用 fgets(),但结果保持不变。你有什么建议吗?
0赞 Joshua 11/11/2023
@tdm2k:如果你注释掉你的 scanf 行,你将能够观察到不是 fgets 不起作用。
0赞 tdm2k 11/11/2023
谢谢!!它工作得很好!
0赞 Owen DeLong 11/11/2023 #2

我不认为你想调用fflush(stdin)。通常,fflush() 对输入流不是特别有用。它对 seek() 可用的输入文件有相当奇怪的应用程序,并且主要用于输出流(发送缓冲数据)。

我注意到您没有检查 fgets() 的返回值,并且您没有提到您得到什么而不是预期结果。我还要注意,在您当前的配置中,您最多允许名称中包含 19 个字符。这是预期的值吗?(fgets() 需要一个字符作为终止 null)。此外,很可能是使用 scanf() 来读取循环上一次迭代中的最后一个元素,而不使用换行符。fgets() 将使用换行符,我强烈建议使用 fgets() 读取缓冲区,然后使用 sscanf() 解析该缓冲区,以避免这种不必要(和令人不快)的意外。

试着用正在发生的事情的细节发表评论,而不是你所期望的(不仅仅是“它不起作用”)......它是否返回 NULL?字符串的一部分是否被传递而其余部分被截断?这到底是怎么回事?

尝试删除 fflush() 也,这很可能是问题的一部分,因为它可能会转储用户在尝试读取它之前键入的输入缓冲区。

评论

0赞 tdm2k 11/11/2023
我对此很陌生,所以我的解释中缺少一些信息。谢谢你的建议!我会在下一个问题中注意到这一点。
0赞 John Bollinger 11/11/2023
这是一个非常软的步骤。语言规范要牢固得多:“如果指向未输入最新操作的输出流或更新流,则该函数会导致将该流传递到主机环境的任何未写入数据写入文件;否则,行为是未定义的“(强调后加)。就规格而言,用于冲洗输出。fflush()streamfflushfflush
0赞 David C. Rankin 11/11/2023
然后 MS 通过明确地做出符合要求的行为来搞砸这一切......采用标准...fflush(stdin)
0赞 Weather Vane 11/12/2023
@DavidC.Rankin MSVC 曾经做过,但几年后就没有了。他们关于 fflush 的文档说:“如果流是在读取模式下打开的,或者如果流没有缓冲区,则对 fflush 的调用不起作用,并且会保留任何缓冲区。
0赞 David C. Rankin 11/12/2023
是的,VS2017 仍在吹捧,而现在 VS2022 已经清理了它——感谢上帝。我无法告诉你有多少吊坠指出它不是 UB,因为......fflush(stdin)