也许这也不能算是一个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,否则会让其退出。
这便是我的猜测了,如果能看到源代码,那问题就好办多了。
#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,否则会让其退出。
这便是我的猜测了,如果能看到源代码,那问题就好办多了。