【问题标题】:Scanf is being skipped after the first iteration of the for loop在 for 循环的第一次迭代后跳过 Scanf
【发布时间】:2018-03-08 22:57:41
【问题描述】:

当 for 循环第一次运行时,程序等待用户输入。但是在第一次之后似乎跳过了两条 scanf 行。

我已经注释掉了杂项代码:

#include <stdio.h>
int n = 0;
struct student {
    int age;
    char name[20];
};

void enterStudents() {
    printf("How many students do you want to enter? \n");
    scanf("%d", &n);
    struct student list[n];
    for(int i=0; i<n; i++){
        printf("Enter student number %d's age: ", i+1);
        scanf("%d", &list[i].age);
        printf("Enter student number %d's name: ", i+1);
        scanf(" %c", list[i].name);
        }
    listSort(list);
    }

/**int listSort(struct student list[n]) {
    char tempName[20];
    int tempAge;
    for(int i=0; i<n-1; i++){
        if(list[n].age < list[n+1].age) {
            tempAge = list[n].age;
            strcpy(tempName, list[n].name);
            list[n].age = list[n+1].age;
            strcpy(list[n].name, list[n+1].name);
            list[n+1].age = tempAge;
            strcpy(list[n+1].name, tempName);
        }
    }
}**/

int main() {
    enterStudents();

}

【问题讨论】:

  • 你的意思可能是scanf(" %19s", list[i].name);读取学生的名字
  • @Pablo 哇,它成功了!谢谢
  • @Pablo scanf(" %19s", list[i].name); 中的前导空格是不必要的 - 但并不令人反感。
  • @chux 我知道,我已经从 OP 复制和粘贴并将 %c 更改为 %19s 却没有意识到有一个空白空间。当我意识到这一点时,已经过了 5 分钟,我无法再编辑评论了。

标签: c scanf


【解决方案1】:

scanf 初学者经常忽略的一个问题是,如果转换 失败或转换转换的字符少于用户输入的字符(例如 例如使用%c 而不是%s),然后scanf 留下未转换的 输入缓冲区中的字符。随后调用scanf 将首先尝试 在再次读取之前转换输入缓冲区中的那些字符 来自用户。

在您的情况下,您使用了%c,但用户输入的内容比 单个字符。转换中仅使用了 1 个字符,其余字符被保留 在输入缓冲区中。在接下来的for 迭代中scanf("%d") 尝试转换 留在输入缓冲区中的字符,并且由于用户输入的名称不是数字,scanf 失败,下一个 scanf 仅读取第一个字符和 留下其余的,等等。这就是为什么 scanf 似乎跳过了调用。

你应该检查scanf的返回值,它返回的数量 它进行了成功的转换。这是很好的信息,如果你得到的更少 转换超过预期,那么您知道scanf 调用失败,您可以 对此作出反应。如果您正在读取字符串,您也可以清理缓冲区, 空格后的换行符和单词留在缓冲区中,这可能导致 后续调用scanf 时遇到一些麻烦。你可以使用这样的函数:

void clean_stdin(void)
{
    int c;
    while((c = getchar()) != '\n' && c != EOF);
}

所以你的函数应该是这样的:

int enterStudents() {
    printf("How many students do you want to enter? \n");
    if(scanf("%d", &n) != 1)
    {
        fprintf(stderr, "Could not read from the user\n");
        return -1; // failure
    }

    if(n <= 0)
    {
        fprintf(stderr, "invalid number of students\n");
        return -1;
    }

    struct student list[n];
    for(int i=0; i<n; i++){
        printf("Enter student number %d's age: ", i+1);
        if(scanf("%d", &list[i].age) != 1)
        {
            fprintf(stderr, "could not read age from the user\n");
            return -1;
        }

        printf("Enter student number %d's name: ", i+1);
        if(scanf("%19s", list[i].name) != 1)
        {
            fprintf(stderr, "could not read name from the user\n");
            return -1;
        }

        // cleaning stdin
        clean_stdin();
    }

    ...
    return n; // sucess
}

请注意,我已经更改了函数,使其在失败时返回 -1,并且 学生数量阅读成功,因此呼叫者可以知道出了问题并得到 同时学生人数。一般来说,为了让你的代码 更强大,您应该从不信任用户并且您应该仔细检查 用户输入。您必须检查用户没有输入负数 学生人数。 “正确”的思维定势是“用户试图打破你的 通过输入不正确的数据来编码,我必须处理”。我知道,代码 变得稍微大一点,但它更健壮,如果出现故障,你 可以更快地缩小范围(基于错误消息) 错误的。在您的代码中,当某些事情失败时,您真的不知道它在哪里 可能发生了。

还要注意,在名称扫描中,我使用了scanf("%19s", ..) 而不是%s。这 原因是使用 %s 如果名称较长,您可能会溢出缓冲区 超过缓冲区可以容纳。如果名称超过 19 个字符,它将 溢出缓冲区。使用"%19s",您将限制应该有多少个字符 被阅读,在这种情况下,scanf 将最多转换 19 个字符并忽略 休息。为什么是 19 而不是 20?你必须使用 19,因为 C 中的字符串是 '\0'-terminated,所以数组中的最后一个空格应该用于 '\0'-终止字节,因此为 19。

【讨论】:

    【解决方案2】:

    每当您从用户那里获取输入时,输入的序列首先是任何其他数据类型,然后是字符或字符数组,如您的情况,序列是整数,然后是字符。首先,您现在输入学生的年龄,例如 21,当您按下“回车键”时,此“回车”将作为下一个字符输入的输入。然后您的输入将如下所示:

      age=21
      name='enter' (enter key that you pressed)
    

    现在我们可以使用 fflush(stdin) 函数,这将清除输入键并允许您输入名称。此代码将起作用:

    void enterStudents() 
    {
        printf("How many students do you want to enter? \n");
        scanf("%d", &n);
        struct student list[n];
        for(int i=0; i<n; i++){
        printf("Enter student number %d's age: ", i+1);
        scanf("%d", &list[i].age);
    
        //enter this line after your first input
        fflush(stdin);
    
        printf("Enter student number %d's name: ", i+1);
        scanf(" %s", list[i].name);
        }
         listSort(list);
        }
    

    【讨论】:

      猜你喜欢
      • 2020-06-13
      • 2022-01-07
      • 1970-01-01
      • 2013-01-16
      • 2010-12-28
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多