首先声明:这些内容主要是面向C语言的初学者,尤其是正在学习C语言的学生。
在《C语言字符型数据(一)》中,我们对文本文件中的内容进行了简单的加密处理。程序如下所示:
#include "stdio.h"
int main()
{
char ch;
freopen("original.txt","r",stdin) ; //输入输出被分别重定向到两个文件。
freopen("result.txt","w",stdout);
ch=getchar();
while(ch!=EOF) //EOF指文件的末尾
{
if(ch>='A'&&ch<='Z'||ch>='a'&&ch<='z')
{
ch=ch+3;
if(ch>'z'||(ch>'Z'&&ch<='Z'+3))
ch= ch-26;
}
printf("%c",ch);
ch = getchar();
}
return 0;
}
有一个学生在练习上述程序时,误将while(ch!=EOF) 写成了while(ch=!EOF),程序编译通过,运行后,result.txt文件中什么结果都没有。花了很长时间才最终发现了这个错误。其实很多初学者都曾犯过类似的错误。比如,在写if语句的条件时,也很容易将if(ch!=EOF)写成if(ch=!EOF),或者本来是判断是否相等if(ch==’a’),结果写成了if(ch=’a’)。对初学者而言,最大的困难是这类错误并没有语法问题,从而很难查找,这也是C语言灵活性的一个体现。下面我们先分析错误的原因,再给出解决方法。
错误分析:
while后面的括号中可以是任何表达式,表达式首先被求值,然后判断该值的真假,C语言判断真假的规则是:零是假,非零的值都为真。当写成ch=!EOF时,这实际上是一个赋值表达式,因为感叹号现在是逻辑运算符“非”,它的优先级高于赋值“=”,所以ch=!EOF等价于ch=(!EOF)。EOF是一个常量,等于-1(非零为真),所以!EOF的值为假,注意逻辑表达式求值后的真假是用1和0来表示的,因此!EOF的值为0,ch也被赋值为0,表达式ch=!EOF的值等于ch的值,也为0。因此,while后面的条件恒为假,循环体一次都没有执行。这就是为什么result.txt文件中什么结果都没有。
解决方法:
1.有牛人曾经给出过一个解决方案:在写while或者if后面的条件时,把常量写在左面,变量写在右边。这样写的好处是,当写错运算符时,编译会报错。比如上面的例子,建议写成while(EOF!= ch),这样写并不影响判断,但如果误写为while(EOF=!ch),编译时会报错,因为EOF是一个常量,常量是不能被赋值的。这样就很容易纠正错误。
2.适当加入断言来防止错误的发生。比如上面的程序中,如果while循环正常结束的话,ch应该等于EOF,因此我们可以断言循环结束后ch等于EOF。加了断言的程序如下所示:
#include "stdio.h"
#include"assert.h"
int main()
{
char ch;
freopen("original.txt","r",stdin);
freopen("result.txt","w",stdout);
ch=getchar();
while(ch!=EOF) //当这个表达式写成while(ch=!EOF),运行时后面的assert会检测到问题。
{
if(ch>='A'&&ch<='Z'||ch>='a'&&ch<='z')
{
ch=ch+3;
if(ch>'z'||(ch>'Z'&&ch<='Z'+3))
ch = ch-26;
}
printf("%c",ch);
ch = getchar();
}
assert(c==EOF); //正常情况下,循环结束时,c==EOF,断言是正确的。当while后面的表达式写错时,循环
//一次都没有执行,这时c!=EOF ,因此断言会报错。
return 0;
}
初学者可以自己上机去试试上面的程序,看断言报错的结果是怎样的。