我遇到了以下代码:
int i;
for(; scanf("%s", &i);)
printf("hello");
根据我的理解,如果我们提供整数输入scanf
将无法读取并因此返回 0,因此循环甚至不应运行一次。但是,它通过接受所有类型的输入作为成功读取来无限运行。
有人会解释这种行为吗?
那是int
: should be的不正确格式说明符"%d"
。
它试图将字符串读入int
变量,可能会覆盖内存。如"%s"
指定的那样,将读取所有输入,因此scanf()
返回一个大于零的值。
(编辑:我认为这个答案不应该被接受。也许赞成,但不被接受。它根本没有解释无限循环,@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
在内存中的时间比存储在内存中的时间早。(换句话说,和。)i
str_below
&str_above > &i
&str_below < &i
这是“缓冲区溢出攻击”的基础,通过将过多数据写入数组来修改堆栈上的值。这就是为什么gets
是危险的(并且永远不应该使用)并且也不应该使用scanf
通用%s
格式说明符。