一个古怪的bug调试过程

也许这也不能算是一个bug,只不过没有了解清楚一个函数的确切用法罢了。这一切都源于一个经典的程序:

#include <stdio.h>
#include <ctype.h>


int main(void)
{
char intput,upper;
while((input=getchar())!=EOF)
{
upper=toupper(input);
putchar(upper);
}
return 0;
}


当我输入abcd'\n'的时候,输出是ABCD'\n',这很正常;
当我输入ctrl+D(也就是EOF),结束程序,这也很正常;
但是,如果我输入abcd'EOF'呢?结果会在当前行输出ABCD,按照道理,程序读取到EOF是会结束的吗?这到底发生什么事了?
如果没有强大的调试工具GDB的帮助下,也许这就一直是我一个小小的困惑,但是GDB太好用了,所以我发现了问题的所在。
让我先GDB一下吧:
先把断点设置子line 8:break 8
接着run,display input和upper,你会发现这两个字符是会有初始值的:
1: input = 0 '\000'
2: upper = 40 '('
然后,step,程序会进行while语句,由于input!=EOF,所以会进行getchar函数,这时候可以输入字符,我输入
ab'EOF'
神奇的事情发生了,当我输入EOF之后,程序立马退出getchar函数,来到下一语句:


(gdb) s
ab10 upper=toupper(input);
2: upper = 40 '('
1: input = 97 'a' 我们可以看到这时候input已经接收了第一个字符a


我们接着s:


(gdb) s
11 putchar(upper);
2: upper = 65 'A' 执行过toupper函数之后,upper等于字符A
1: input = 97 'a'


接着s,我发现这时候字符A并没有输出,而是直接跳至while循环:
(gdb) s
8 while((input=getchar())!=EOF)
2: upper = 65 'A'
1: input = 97 'a'
接着s,跟之前类似:
(gdb) s
10 upper=toupper(input);
2: upper = 65 'A'
1: input = 98 'b'
接着s,还是跟之前类似:


(gdb) s
11 putchar(upper);
2: upper = 66 'B'
1: input = 98 'b'
接着s,又跳至while循环,putchar还是没有输出:


(gdb) s
8 while((input=getchar())!=EOF)
2: upper = 66 'B'
1: input = 98 'b'


我们再接再厉,s一下:
这时候很神奇的是:终于有输出了,是在输入当前行输出AB。
当我们直接输入EOF的时候,getchar马上接收到EOF信号退出函数。


我再来一个正常的例子来实验一下,输入为:ad'\n',结果发现
程序运行到最后一个字符回车符的时候,就已经可以输出了:
(gdb) s
11 putchar(upper);
2: upper = 10 '\n'
1: input = 10 '\n'


(gdb) s
AD 跳至while循环的时候已经有输出了
8 while((input=getchar())!=EOF)
2: upper = 10 '\n'
1: input = 10 '\n'


我把上面的情况总结一下:


(1)getchar只认得第一个输入的EOF,如果前面有多个字符后面接着一个EOF,则EOF只会让getchar函数结束,getchar不会接收EOF,只接收前面输入的字符;
(2)putchar不是马上输出字符,而是一个一个输出,如果遇到回车,则输出所有字符,但是如果是多个字符加一个EOF,情况就发生改变,运行完putchar并不马上输出,而是要运行至getchar函数后才输出。


这实在是一个难以解释的bug。


不过,我猜测是这样的:
getchar函数和putchar函数共同维护着同一个缓冲区,getchar函数在运行之前会将缓冲区内部的所有数据清空并输出,由于刚开始的时候缓冲区都是'\0',所以什么也没有显示,我只能看到getchar正准备接收字符。putchar函数则会再遇到'\n' 的时候将缓冲区数据输出,由于我们是多个字符后面接着EOF,且EOF没有被getchar读取,所以putchar再运行至最后一个字符时仍然不输出。直到再次运行getchar函数,它将缓冲区所有的字符输出,所以我们看到输出了。getchar只能认识第一次就输入的EOF,否则会让其退出。


这便是我的猜测了,如果能看到源代码,那问题就好办多了。













评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值