最近重读了一下《C陷阱与缺陷》,发现一道题挺有意思的,这道题是一段简单的代码,如下所示:
#include <stdio.h>
int main(){
int i;
char c;
for(i = 0;i < 5;i++){
scanf("%d",&c);
printf("%d",i);
}
printf("\n");
return 0;
}
注意scanf(“%d”,&c),在读入时分两种情况:
(1)读入时是数字,此时程序会陷入死循环,读入一个数字就输出一个0,造成这种情况的原因是在字符c的地址上,程序以“%d”存储了一个整型数据,而字符c本来是一个字节,假设int占四个字节(我的机器上这样的),那么字符c的地址附近的内存会被覆盖。下面以图形化的形式说明这种情况
(gdb调试程序)
若当前时刻i = 1,打印i的地址是0xbffff3fc,c的地址是0xbffff3fb,未执行scanf时的内存内容如下
内容(从内存地址开始的四个字节内容) | 内存地址 |
---|---|
0x01 0x00 0x00 0x00 | 0xbffff3fc |
0x01 0x01 0x00 0x00 | 0xbffff3fb |
执行了scanf以后(假设读入了数字5),内存的情况如下图所以
内容(从内存地址开始的四个字节内容) | 内存地址 |
---|---|
0x00 0x00 0x00 0x00 | 0xbffff3fc |
0x05 0x00 0x00 0x00 | 0xbffff3fb |
显然,在执行完scanf后,从地址0xbffff3fb开始的四个字节被0x05 0x00 0x00 0x00 覆盖了,变量i的低端部分被置0,而i的高端部分本身就是0,那么此时i的值就变成了0,i的值始终都在0和1之间,就形成了最后的死循环。
(2)读入的是非数字的字符,那么scanf就会遇到读入数据与scanf期待的%d类型不符,导致stdin流出现堵塞,下次循环将不再读入数据除非清空stdin缓冲区,最终导致的结果就是直接输出01234