为什么scanf(“%d”,&number)在C编程中导致'a'为29?

问题描述 投票:0回答:4

我在C中有以下代码

#include <stdio.h>
int main()
{
    int number;

    // printf() displays the formatted output
    printf("Enter an integer: ");

    // scanf() reads the formatted input and stores them
    scanf("%d", &number);

    // printf() displays the formatted output
    printf("You entered: %d", number);
    return 0;
}

在这里,我希望输入一个数字,但我输入了一个字符'a',结果为29.我希望它会抛出异常,但我对输出感到惊讶。 Screen shots of an output of the program.

c scanf
4个回答
3
投票

对于初学者,没有参数的函数main应声明为

int main( void )

只需按以下方式更改您的程序即可

#include <stdio.h>

int main( void )
{
    int number;

    // printf() displays the formatted output
    printf("Enter an integer: ");

    // scanf() reads the formatted input and stores them
    if (scanf("%d", &number) == 1)
    {
        // printf() displays the formatted output
        printf("You entered: %d\n", number);
    }
    else
    {
        puts("Invalid input");
    }

    return 0;
}

并看看如果你尝试输入字符'a'而不是数字会发生什么。

根据C标准中的功能描述(7.21.6.2 fscanf功能)

d匹配可选带符号的十进制整数,其格式与strtol函数的主题序列的预期格式相同 基本参数的值10。相应的参数应该是指向有符号整数的指针。

并且(7.22.1.4 strtol,strtoll,strtoul和strtoull函数)

7如果主题序列为空或没有预期的形式,则不执行转换;如果endptr不是空指针,则nptr的值存储在endptr指向的对象中。

因此在演示程序中将执行else语句,因为(C标准,7.21.6.2 fscanf函数)

16如果在第一次转换(如果有)完成之前发生输入故障,fscanf函数将返回宏EOF的值。否则,该函数返回分配的输入项的数量,如果早期匹配失败,则可以少于提供的数量,甚至为零


2
投票

你陷入了同样的陷阱,几乎所有新的C程序员都没有花时间完全理解scanf函数系列,然后尝试用它来输入用户输入。

在了解具体细节之前,无论何时进行用户输入,都必须验证输入。这意味着检查用于验证输入的任何函数的返回实际上已接收,并且如有必要,验证收到的输入是否在您的代码的预期/可接受的值范围内。使用scanf也不例外。

用户输入的首选方法是fgets,因为它避免了scanf中固有的陷阱并完全消耗了每一行输入(假设缓冲区足够大,或者如果重复调用它直到你验证最后一个字符读取是'\n'

使用scanf,如果(1)成功输入或(2)输入或匹配失败,其中输入的其他尝试将随之而来,则由您来计算和删除保留在stdin中的任何字符。

要安全地使用scanf进行用户输入,您有三个条件要测试,(1)返回是否表示格式字符串中指定的所有转换都发生了?如果是这样,请清空stdin并检查根据需要收到的值范围。 (2)用户按下Ctrl + d(或在windoze上按Ctrl + z)取消输入。如果是这样,请优雅地退出确保输出中提供最终的'\n' - (取决于OS和编译器);最后(3)发生匹配或输入失败?如果是这样,请处理错误并清空stdin中的任何无效字符。

当您接受用户输入时,无效的输入对您没有好处。因此,您通常希望循环直到您收到有效输入或用户取消,这可以通过简单的无限循环相对容易地完成,例如,

/** empty all characters reamining in stdin */
void empty_stdin()
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}
...

    for (;;) {          /* loop until valid input or user cancels */
        int rtn = 0;    /* variable to capture scanf return */

        /* printf() displays the formatted output */
        printf ("Enter an integer: ");

        /* scanf() reads the formatted input and stores them */
        rtn = scanf ("%d", &number);

        if (rtn == 1) {         /* we got an integer value */
            empty_stdin();      /* empty any remaining characters */
            break;              /* continue with program */
        }
        else if (rtn == EOF) {  /* user canceled with ctrl+d (ctrl+z on win) */
            fprintf (stderr, "user canceled input.\n");
            return 1;
        }
        /* handle input or matching failure */
        fprintf (stderr, "error: matching or input failure occurred.\n");
        empty_stdin();          /* empty any remaining characters */
    }

通过输入循环步进,您将注意到必须检查的三个条件中的每一个都被处理,并且保留在stdin中的任何字符都被清空以准备下一个输入。您的完整代码只输出成功输入的结果(如果在Windows上,则添加最终的getchar();以防止终端窗口关闭,如果您正在运行IDE,例如

#include <stdio.h>

/** empty all characters reamining in stdin */
void empty_stdin()
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

int main (void) {

    int number = 0;

    for (;;) {          /* loop until valid input or user cancels */
        int rtn = 0;    /* variable to capture scanf return */

        /* printf() displays the formatted output */
        printf ("Enter an integer: ");

        /* scanf() reads the formatted input and stores them */
        rtn = scanf ("%d", &number);

        if (rtn == 1) {         /* we got an integer value */
            empty_stdin();      /* empty any remaining characters */
            break;              /* continue with program */
        }
        else if (rtn == EOF) {  /* user canceled with ctrl+d (ctrl+z on win) */
            fprintf (stderr, "user canceled input.\n");
            return 1;
        }
        /* handle input or matching failure */
        fprintf (stderr, "error: matching or input failure occurred.\n");
        empty_stdin();          /* empty any remaining characters */
    }

    /* printf() displays the formatted output */
    printf ("You entered: %d\n", number);

#if defined (_WIN32) || defined (_WIN64)
    getchar ();     /* to hold terminal open -- don't use getch() */
#endif

    return 0;
}

示例使用/输出

$ ./bin/scanf_int
Enter an integer: apples
error: matching or input failure occurred.
Enter an integer: a 10 b 50 c 90
error: matching or input failure occurred.
Enter an integer: 12345aaa
You entered: 12345

输入已取消的示例。 (注意:在Win10上,ctrl + z默认不生成EOF,请参阅CTRL+Z does not generate EOF in Windows 10 | PROC-X.com

$ ./bin/scanf_int
Enter an integer: NO!
error: matching or input failure occurred.
Enter an integer: user canceled input.

仔细看看,如果您有其他问题,请告诉我。


1
投票

我认为“scanf()”不会像数字那样读“a”而只是跳过将任意数字放在“number”中,而29是堆栈中找到的数字的初始值!

尝试用123之类的东西初始化“数字”,你将得到123输出。


1
投票

%d希望在输入流中看到一个或多个十进制数字; ’a’不是十进制数字,因此您得到匹配失败 - number未更新,a保留在输入流中,scanf返回0表示没有发生转换和赋值。

由于在声明时没有初始化number,因此它包含一个不确定的值,在本例中为29

C不会对意外输入使用异常 - 您必须检查scanf的返回值。

int result = scanf( “%d”, &number );
if ( result == 1 )
{
  // user entered one or more decimal digits for number
}
else if ( result == 0 )
{
  // user entered something that isn’t a decimal digit
}
else // result == EOF
{
  // EOF or error detected on input
}
© www.soinside.com 2019 - 2024. All rights reserved.