在c语言中关系运算符号与数学中接触到的比较符号,从使用方式和能够都很相似,所以误将两者完全等价。
如示例2:
main()
{inta,b,c;scanf("%d%d%d",&a,&b,&c);
if(a>b>c)printf("a最大\n");
if(b>a>c)printf("b最大\n");
if(c>b>a)printf("c最大\n");}
执行程序时,输入“123”或“321”,没有输出结果。
此问题在于对“a>b>c”关系运算符应用的误解。
比如:
输入“123”,分析“if(c>b>a)”的结果,由于关系符“>”的结合性自左向右,即“3>2”比较的结果为“真”。
在C语言关系运算中,以“1”代表“真”,以“0”代表“假”,所以最终比较的是“1>1”,自然运算结果为“假”,也就不会出现预期的输出“c最大”。
因此,正确代码应将三段条件结构依次修改为:
if(a>b&&a>c)、if(b>a&&b>c)、if
(c>b&&c>a)。
1.3scanf()函数的几点注意问题
1.3.1输入数据的方式与要求不符
因C语言规定:
如果在“格式控制”字符串中除了格式说明以外还有其它字符则在输入数据时应输入与这些字符相同的字符。
比如:
scanf("%d,%d",&a,&b);
输入时,如果用空格作两个数据间的分隔符输入:
45,
则不合法。
合法的输入形式应是:
4,5
再如:
scanf("a=%d,b=%d",&a,&b);正确的输入形式应是:
a=3,b=4
1.3.2输入数据中的空格和回车
scanf()函数,在用“%c”格式声明输入字符时,有不同于其他格式类型需注意的地方。
字符格式类型中,空格符、转义字符都作为有效字符;而对于数值型数据,空格符、回车键、Tab键或非法数值字符均认为数值字符输入的终止符。
如下示例3:
main()
{charc1,c2;intd1,d2;
scanf("%c%c",&c1,&c2);
scanf("%d%d",&d1,&d2);
printf("c仁%。
,c2=%c\n",cl,c2);
printf("d^l=%d,d2=%d\n",di,d2);}
输入:
ab
12345
输出:
c1=a,c2=b
d1=123,d2=45
输入:
ab
1234578
输出:
c1=a,c2=
d1=12345,d2=78
如下示例4:
main()
{chargender,ms;
printf("请输入性别(f/m)\n");
scanf("%c",&gender);
printf("请输入婚姻状况(y/n)\n");scanf("%c",&ms);
printf("性别是:
%c,婚姻状况:
%c\n",gender,ms);}
执行时输入“f”,结果没有等输入(y/n)就显示结果如下图1。
不难发现字符变量ms得到的字符为“回车”,即输入“f”后用户敲入的“回车键”。
如何避免此种现象的产生,可以采取以下几种方法。
方法一:
规避编译器误将回车键作为字符存储至字符变
量ms中,可以使用scanf("\n%c",&ms);
方法二:
在两次输入语句中间加入清除缓冲流的语句,fflush(stdin);
改进后,正确执行结果如图2。
图1示例4运行错误结果图图2示例4-2运行正确结果图
1.4字符与字符串
C语言中,单引号和双引号含义迥异,在某些情况下如果把两者弄混,编译器并不会检测报错,从而在运行时会产生难以预料的结果。
用单引号引起来的一个字符实际上代表一个整数,整数值对应该字符在编译器采用字符集中的序列集。
用双引号引起来的字符串,代表着一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个空字符’、0'初始化。
具体不同,如下示例5:
main()
{printf("\n");printf("'\n'");printf("'\101'");}
运行结果如下图3,如果语句换成printf('\n');虽编译时没有异常,但运行时应用程序错误,具体问题如图4。
图3示例5运行结果图图4示例5错误截图
2语法“陷阱”
要理解一个C程序,仅仅理解组成该程序的符号是不够的。
初学者还必须理解这些符号是如何组成表达式、语句和程序的,有时候这些定义和直觉相悖,容易引起混淆。
下面就讨论一些容易产生问题的语法结构。
2.1语句结束符:
分号的使用
在C程序中如果不小心多写了一个分号可能不会造成不良后果,因为这实际上产生一个空语句。
但是也有重要的例
外,在if或者while子句之后需要紧跟一条语句时,如果此时多加了一个分号,就会产生出人意料的结果。
示例6:
main()
{intx=2;
if(x>2)
printf("x>2");}
程序运行结果没有输出。
但如果if(x>2)条件后面加上
分号,则输出“x>2”同样的道理,如下,正常输出结果为
“543”;而如果while子句后加上分号,此程序变成了“死循环”。
示例7:
main()
{intx=5;
while(x>2)
{printf("%d",x);
x―;}}
2.2switch语句
C语言的switch语句控制流能够依次通过并执行各个
case部分,这一点是C语言的不同之处。
如下分析有无
“break”语句的效果,示例8:
main()
{intcolor=1;
switch(color)
{case1:
printf("red");break;case2:
printf("blue");break;case3:
printf("yellow");break;}}程序运行结果为:
red。
如果将程序中三个“break”全部删除,程序运行结果为:
redblueyellow。
原因是C语言中的switch语句在执行了控制流程中的第一个之后,会自然而然地顺序执行下去,直到最后一个case语句。
实际
中,C语言中switch语句的这种特性,既是它的优点,又是它的一大弱点。
说到弱点是因为初学者很容易遗漏各个case后面的break语句,造成难以理解的程序结果。
说到优势是因为当程序员有意的略去一个break语句,则可以实现多个分支共同作用处理,重要的是看程序员如何应用。
2.3else“悬挂”引发的问题
此问题是大多数初学者很容易弄错,而且并非C语言独有,其他语言也会让程序员们常常失误。
在应用多支路的程序设计中,往往会用到if的嵌套结构,常常容易出现错误。
如实现如下的分段函数。
程序代码示例9:
intmain()
{intx,y;
scanf("%d",&x);
y=0;
if(x>=0)
if(x>0)
y=5;
else
y=-5;
printf("y=%d\n",y);}
如上代码在执行测试时会发现实际输出结果与编程者的愿望相去甚远。
原因在于C语言中有这样的规则,else总是与同一对括号内最近的缺少对于else部分的if结合匹配。
也就是说,并不单单是x<0时y=-5,还包括x=0时y=-5。
故该问题用添加括号的办法,改为正确程序部分代码如下:
if(x>=0)
{if(x>0)y=5;}
elsey=-5;
此时,else并没有同离它更近的第二个if匹配,而是与第一个if结合,因为第二个if已经被括号“封闭”起来了,构成了if的嵌套结构。
3语义“陷阱”
3.1整数溢出
C语言提供的数据类型有许多,其中整型数据提供了三种不同长度的类别:
shortint、int和longint。
不管是哪种数据类型都有一个固定的长度,它能存储的最大值是一个固定的整数,当尝试去存储一个大于这个固定最大值时,将会导致整数溢出.
例如:
求满足条件1+2+3+,+n<32767的最大整数n,如下实例10:
main()
{intn=1,sum=0;
while(sum<=32767){sum+=n;n++;}
printf(“n=%d\n”,n-1);}
乍看该程序时无错误,但事实上,上列程序中的while循环是一个无限循环,原因在于int型数的表示范围为-32768到+32767,当累加和sum超过32767时,便向高位进位,而对int型数而言,最高位表示符号,故sum超过32767后便得到一个负数,while条件当然满足,从而形成无限循环。
解决此类问题是将数据容量升高,sum定义为longint型。
3.2求值顺序可能说起求值顺序,许多人能联想到运算符的优先级问题,但其实这不是一回事。
运算符优先级是诸如:
a+b*c等同于a+(b*c);而求值顺序是诸如:
if(n!
=0&&x/n>y),此时即使n=0时,也不会出现“0作为除数”的错误。
原因就是因为当n=0时,n!
=0的表达式为0,不会再运算到后面的表达式。
C语言中某些运算符总是以一种已知的、规定的顺序来
对其操作数进行求值,而另外一些则不是这样的。
再如:
a
于b,则表达式结果肯定为假,也无须对后面的继续求值。
这也就是常说的“运算符短路原则”。
C语言中只有四个运算符(&&、||、?
:
、,)存在规定的求值顺序。
逻辑运算符“&&”和“||”首先对左操作数求值,只在需要时才对右侧操作数求值。
运算符“?
:
”是三元运算符,在a?
b:
c中,首先对操作数a进行判断,再确定求b还是c。
如:
1>2?
3:
4,由于1>2结果为假,则最终表达式值为4。
而逗号运算符,首先对左侧操作数求值,然后该值被“丢弃”,再对右侧操作数求值。
如:
a=1,2,3;最终a的值为3。
4总结本文笔者通过多年的教学经验总结出一些应用性实例,针对初学者在学习C语言时易犯的错误,分类讨论后给予错误分析,并提出相应的解决方案。
如想熟练掌握C语言,需要学生们不断地总结分析,大量的上机实践,这样才能积累更多的程序设计经验。
参考文献:
[1]张悦.一种基于程序结构的程序主变元分析与确定方法研究[D].北京:
北京化工大学,2008.
[2]常鑫.C语言程序设计的输入输出[J].内蒙古科技与
经济,2012
(1).
[3]雷萌.C语言疑惑经验谈[J].软件导刊,2011(3).
[4]朱一峰.C语言常见错误分析及解决方法[J].辽宁师专学报,2009,11(4).
[5]张海燕.C语言学习中的难点浅析[J].科技资讯,2008(29).