在for循环的第一次迭代之后跳过Scanf

时间:2018-03-08 22:57:42

标签: c scanf

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

我已经注释掉了misc代码:

#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();

}

2 个答案:

答案 0 :(得分:1)

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

在您的情况下,您使用了%c,但用户输入的内容比a长 单个字符。转换中只使用了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并且 学生阅读成功的数量,因此呼叫者可以知道出了什么问题并得到了 同时的学生人数。一般来说,为了使你的代码 更强大,你应该永远信任用户,而你应该仔细检查 用户输入。您必须检查用户是否输入了否定号码 学生数量。 &#34;正确&#34;心灵设置是&#34;用户试图打破你的 代码输入错误的数据,我必须处理#34;。我知道,代码 变得稍微大一点,但它更强大,如果出现问题,你就可以了 可以更快地缩小范围(基于错误消息) 错误。在你的代码中,当某些东西失败时,你真的不知道它在哪里 可能发生了。

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

答案 1 :(得分:0)

每当你输入来自用户的输入,其中输入有序列作为第一个是任何其他数据类型,然后字符或字符数组,如你的情况序列是整数,然后是字符。首先你输入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);
    }