【问题标题】:Why the second scanf also changes the variate presented in the first scanf?为什么第二个 scanf 也改变了第一个 scanf 中呈现的变量?
【发布时间】:2021-01-05 09:41:24
【问题描述】:

我想用scanf函数输入一个整数和一个字符,但它没有按我的意愿工作。

代码如下。

#include <stdio.h>
int main()
{
    int a;
    char c;
    scanf("%d",&a);
    scanf("%2c",&c);
    printf("%d%c",a,c);
    return 0;
}

我尝试从终端输入12a (a后面有一个空格),但输出的不是“12a”而是“32a”。

我也试过一步步运行上面的代码,发现第一个scanf的时候a的值为12,而第二个scanf的a的值为a ” 32 岁了。

我想弄清楚为什么第二个scanf 改变了a 的值,没有呈现出来。

【问题讨论】:

  • 你应该检查scanf()的返回值是否成功。
  • 您正试图将两个符号读入单个 char 变量

标签: c scanf


【解决方案1】:

问题是编译器将变量a 放在变量c 后面。当您执行第二个scanf() 时,您指定将两个字符 读入一个只有一个空间的变量。您发生了缓冲区溢出,并覆盖了变量 c 之后的内存(而 a 恰好在那里)。该空间已写入a,这就是您得到32 输出的原因(a 已存储了 ASCII SPACE 的值,即 32)。

发生的事情被称为未定义的行为,当您犯这种错误时很常见。您可以通过定义一个包含至少两个单元格的char 单元格数组来解决此问题,以读取这两个字符。然后使用类似的东西:

#include <stdio.h>
int main()
{
    int a;
    char c[2];
    scanf("%d", &a);
    scanf("%2c", c);   /* now c is a char array so don't use & */
    printf("%d%.2s", a, c); /* use %.2s format instead */
    return 0;
}

注意:

使用%.2s 格式说明符是因为c 是一个由两个完全填充的字符组成的数组(不允许空格包含\0 字符串结束分隔符),这将导致未定义的行为如果我们不能确保格式化将在第二个字符处结束(或之前,如果在第一个或第二个数组位置找到真正的\0)

【讨论】:

    【解决方案2】:

    引用 C11,第 7.21.6.2 章,fscanf 函数(强调我的)

    c

    [...]如果存在l 长度修饰符,则输入应为以初始移位状态开始的多字节字符序列。序列中的每个多字节字符都被转换为宽字符,就像调用mbrtowc 函数一样,在转换第一个多字节字符之前,mbstate_t 对象描述的转换状态初始化为零。 相应的参数应该是指向wchar_t 数组的初始元素的指针,该数组的大小足以接受生成的宽字符序列。 [...]

    您提供的是char *。提供的参数与预期的参数类型不匹配,因此这是未定义的行为。

    因此结果无法证明。

    要保存像"a " 这样的输入,您需要一个(足够长的)char 数组,char 变量是不够的。

    【讨论】:

    • 你引用了%lc的文字
    • 这就是scanf不安全的原因吗?好像不检查是否访问了不合适的地方。
    • @Sirly C 根本不检查像数组这样的目标是否足够大。这是速度和尺寸的折衷方案,而且在历史上也是如此。不仅scanf()不安全,其他很多标准函数也是不安全的,任何不执行反制措施的函数都是你自己写的..
    猜你喜欢
    • 2021-04-07
    • 1970-01-01
    • 2013-10-16
    • 2012-05-21
    • 2023-04-09
    • 2021-11-24
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    相关资源
    最近更新 更多