3

我遇到了以下代码:

int i;
for(; scanf("%s", &i);)
    printf("hello");

根据我的理解,如果我们提供整数输入scanf将无法读取并因此返回 0,因此循环甚至不应运行一次。但是,它通过接受所有类型的输入作为成功读取来无限运行。

有人会解释这种行为吗?

4

2 回答 2

9

那是int: should be的不正确格式说明符"%d"

它试图将字符串读入int变量,可能会覆盖内存。如"%s"指定的那样,将读取所有输入,因此scanf()返回一个大于零的值。

于 2012-06-18T08:00:28.407 回答
2

(编辑:我认为这个答案应该被接受。也许赞成,但不被接受。它根本没有解释无限循环,@hmjd 就是这样做的。)

(这实际上并没有回答这个问题,其他答案就是这样做的,但它很有趣并且很高兴知道。)

正如 hmjd 所说,scanf像这样使用会覆盖内存(“粉碎堆栈”),因为它开始写入i内存,然后继续运行,即使在i占用的 4 字节内存(或 8 字节,在 64-位平台)。

为了说明,请考虑以下代码:

#include<stdio.h>

int main() {
    char str_above[8] = "ABCDEFG";
    int i;
    char str_below[8] = "ABCDEFG";

    scanf("%s", &i);

    printf("i = %d\n", i);
    printf("str_above = %s\nstr_below = %s\n", str_above, str_below);

    return 0;
}

编译并运行它,然后输入1234567890会产生以下输出:

i = 875770417
str_above = 567890
str_below = ABCDEFG

几点:

  • i与整数几乎没有对应关系1234567890(它与字符的值'1',...'4'以及系统的字节序有关)。
  • str_above已被修改scanf:字符'5',...,,'0''\0'超出为 保留的内存块的末尾,i并已写入为 保留的内存str_above
  • 堆栈已被“向上”粉碎,即存储str_above在内存中的时间比存储在内存中的时间早。(换句话说,和。)istr_below&str_above > &i&str_below < &i

这是“缓冲区溢出攻击”的基础,通过将过多数据写入数组来修改堆栈上的值。这就是为什么gets是危险的(并且永远不应该使用)并且也不应该使用scanf通用%s格式说明符。

于 2012-06-18T08:32:41.633 回答