当我们使用%d扫描一个字符时会发生什么?

3
我运行了这段代码。
#include <stdio.h>
int main()
{
    int u;
    scanf("%d",&u);
    printf("%d\n",u,u);
    return 0;   
}

以下是输入和输出内容:
input:a output:32765
input:b output:32765
input:c output:32767
input:/ output:32766

有人能解释一下这个行为吗?


3
scanf调用失败,返回0,并且您代码中的下一条语句会执行。这就好像scanf语句不存在一样。简而言之,由于尝试打印未初始化的变量,您的代码会引发未定义的行为。 - Spikatrix
2个回答

3
scanf()读取输入时,如果不能按照规范转换,则停止扫描并返回成功转换的数量。在您的情况下,字符仍然留在标准输入缓冲区中,scanf()返回0
您的代码具有未定义的行为,因为scanf()无法将输入转换为整数,导致u未初始化。将此未初始化的值传递给printf会产生未定义的行为。在您的情况下,会打印出一个随机值,可能不同,但任何其他行为都是可能的,包括崩溃。
您必须测试scanf()的返回值以检查是否成功转换。这是一个修改后的版本:
#include <stdio.h>

int main(void) {
    int u, res;
    res = scanf("%d", &u);
    if (res == 1) {
        /* conversion succeeded */
        printf("%d\n", u);
    } else
    if (res == 0) {
        /* conversion failed */
        printf("input is not a valid number\n");
    } else {
        /* res must be EOF */
        printf("premature end of file: no input given\n");
    }
    return 0;   
}

我知道这是UB,但是,代码输出徘徊在32767左右的原因可能是什么? - Spikatrix
@CoolGuy:未初始化的变量 u 偶尔会具有值 32767... 这是启动代码留下的堆栈或寄存器内容的副作用。根据操作系统和启动代码,该值可能是可重现的或不可重现的。试图理解 UB 是徒劳无功的,可能需要汇编级别的检查和一些神奇的技巧。 - chqrlie

2

您的程序行为是未定义的

您应该检查scanf的返回值,它告诉您读入传递参数的数据的数量。在您的情况下,返回值将为零,因为%d不能用于读取char。因此,u未初始化,因此不应读取其值。

请注意,您向printf传递多余的参数是无害的,尽管正式上仍会评估这些参数。请参阅“调用带有过多参数的printf是否未定义行为?”

故事的寓意:始终检查scanf的返回值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接