初学者学习C语言中常见问题的探讨

2014-07-18 18:54霍卓群
电脑知识与技术 2014年13期
关键词:常见错误初学者C语言

霍卓群

摘要:C语言是编程领域中很有影响力的一种程序设计语言。它简洁、紧凑,使用方便、灵活,并且具备高级语言和低级语言的特征。由于C语言的编译系统对语法的检查不如其他语言那么严格,因此C语言对使用者的要求比较高.笔者结合实际教学中的经验,分类列举出C语言中常见的错误进行分析与探讨,以此对初学者给予借鉴。

关键词: C语言; 初学者; 常见错误; 解决方法

中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2014)13-3012-04

Study of Common Problems in C Language Beginners

HUO Zhuo-qun

(Lecturer of Electronic Information Department of Anhui Finance and Trade Vocational College,Hefei 230601, China)

Abstract: C language is a very influential programming language in the field . It is concise, compact, easy, flexible, and has both the characteristics of high and low level languages. Because the examine of grammer by the compiling system of C language is not as strict as others ,it has higher standard for its users . Combined with my teaching experience, I list common mistakes in C language,then analysis and discuss, hoping helpful for beginners.

Key words: C language; beginner; common errors; solution

在计算机教育方面,C语言是为数不多的国内外能同步的程序设计语言之一,可见,它在高校理工类专业基础课程中的重要地位。对于刚进入大学生活的学生们,开始学习C语言课程,会感觉C语言的知识点多、理论性强、具有严密的逻辑性,学习难度较大。授课教师在课堂教学和实训环节中,发现学生们常易出现许多同性的问题,而这些或多或少的问题给学生们带来了很大的麻烦,甚至产生较强的挫败感。该文结合了笔者多年C语言教学经验,将常见问题进行分析与探讨。如下将以“陷阱”分类阐述,重点让初学者能够通过具体示例,吸取他人所犯错误的经验教训。

1 词法“陷阱”

试想下,当我们阅读一个句子时,我们并不去考虑组成这个句子的单词中单个字母的含义,而是会把单词作为一个整体来理解。的确,字母本身并没有什么意义,而是我们将字母组成单词,然后给单词赋予一定的意义。对于C语言编写程序,也是一样的道理。

“符号”是程序的一个基本组成单位,作用相当于一个句子中的单词。如下将讨论符号以及符号间的一些常见问题。

1.1 “=”不同于“==”

在C语言中,符号“=”为赋值运算符,符号“==”为比较运算符。一般的,赋值运算相对于比较运算出现得更频繁,以此字符数较少的“=”被赋予了赋值运算的含义。学生在程序设计中,往往本意想用作比较运算,却可能无意中误写成赋值运算。如示例1:

main()

{ inta,b; scanf("%d%d",&a,&b);

if(a=b) printf("a,b相等\n");

else printf("a,b不相等\n");}

执行程序时会发现即使输入a,b的值不相同,输出结果仍然是“a,b相等”。此问题的原因就在于if中的条件误将赋值运算符“==”写成了比较运算符“=”。

1.2 “x

在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");}

执行程序时,输入“1 2 3”或“3 2 1”,没有输出结果。此问题在于对“a>b>c”关系运算符应用的误解。比如:输入“1 2 3”,分析“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.3 scanf()函数的几点注意问题

1.3.1输入数据的方式与要求不符

因 C语言规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符则在输入数据时应输入与这些字符相同的字符。

比如:scanf("%d,%d",&a,&b);

输入时,如果用空格作两个数据间的分隔符输入: 4 5,则不合法。

合法的输入形式应是: 4,5

再如:scanf("a=%d,b=%d",&a,&b);

正确的输入形式应是:a=3,b=4

1.3.2输入数据中的空格和回车

scanf()函数,在用“%c”格式声明输入字符时,有不同于其他格式类型需注意的地方。字符格式类型中,空格符、转义字符都作为有效字符;而对于数值型数据,空格符、回车键、Tab键或非法数值字符均认为数值字符输入的终止符。

如下示例3:

main()

{char c1,c2;int d1,d2;

scanf("%c%c",&c1,&c2);

scanf("%d%d",&d1,&d2);

printf("c1=%c,c2=%c\n",c1,c2);

printf("d1=%d,d2=%d\n",d1,d2);}

输入:

ab

123 45

输出:

c1=a,c2=b

d1=123,d2=45

输入:

a b

12345 78

输出:

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()

{ int x=2;

if(x>2)

printf("x>2");}

程序运行结果没有输出。但如果if(x>2)条件后面加上分号,则输出“x>2”。同样的道理,如下,正常输出结果为“543”;而如果while子句后加上分号,此程序变成了“死循环”。示例7:

main()

{ int x=5;

while(x>2)

{ printf("%d",x);

x—; }}

2.2 switch语句

C语言的switch语句控制流能够依次通过并执行各个case部分,这一点是C语言的不同之处。如下分析有无“break”语句的效果,示例8:

main()

{ int color=1;

switch(color)

{ case 1: printf("red");break;

case 2: printf("blue");break;

case 3: printf("yellow");break; }}

程序运行结果为:red。如果将程序中三个“break”全部删除,程序运行结果为:redblueyellow。原因是C语言中的switch语句在执行了控制流程中的第一个之后,会自然而然地顺序执行下去,直到最后一个case语句。

实际中,C语言中switch语句的这种特性,既是它的优点,又是它的一大弱点。说到弱点是因为初学者很容易遗漏各个case后面的break语句,造成难以理解的程序结果。说到优势是因为当程序员有意的略去一个break语句,则可以实现多个分支共同作用处理,重要的是看程序员如何应用。

2.3 else“悬挂”引发的问题

此问题是大多数初学者很容易弄错,而且并非C语言独有,其他语言也会让程序员们常常失误。在应用多支路的程序设计中,往往会用到if的嵌套结构,常常容易出现错误。

如实现如下的分段函数。程序代码示例9:

int main()

{ int x,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;}

else y=-5;

此时,else并没有同离它更近的第二个if匹配,而是与第一个if结合,因为第二个if已经被括号“封闭”起来了,构成了if的嵌套结构。

3 语义“陷阱”

3.1整数溢出

C语言提供的数据类型有许多,其中整型数据提供了三种不同长度的类别:short int、int和long int。不管是哪种数据类型都有一个固定的长度,它能存储的最大值是一个固定的整数,当尝试去存储一个大于这个固定最大值时,将会导致整数溢出.

例如:求满足条件1+2+3+…+n≤32767的最大整数n,如下实例10:

main()

{int n=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定义为long int型。

3.2求值顺序

可能说起求值顺序,许多人能联想到运算符的优先级问题,但其实这不是一回事。运算符优先级是诸如:a+b*c等同于a+(b*c);而求值顺序是诸如:if(n!=0&&x/n>y),此时即使n=0时,也不会出现“0作为除数”的错误。原因就是因为当n=0时,n!=0的表达式为0,不会再运算到后面的表达式。

C语言中某些运算符总是以一种已知的、规定的顺序来对其操作数进行求值,而另外一些则不是这样的。再如:a

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).

实际中,C语言中switch语句的这种特性,既是它的优点,又是它的一大弱点。说到弱点是因为初学者很容易遗漏各个case后面的break语句,造成难以理解的程序结果。说到优势是因为当程序员有意的略去一个break语句,则可以实现多个分支共同作用处理,重要的是看程序员如何应用。

2.3 else“悬挂”引发的问题

此问题是大多数初学者很容易弄错,而且并非C语言独有,其他语言也会让程序员们常常失误。在应用多支路的程序设计中,往往会用到if的嵌套结构,常常容易出现错误。

如实现如下的分段函数。程序代码示例9:

int main()

{ int x,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;}

else y=-5;

此时,else并没有同离它更近的第二个if匹配,而是与第一个if结合,因为第二个if已经被括号“封闭”起来了,构成了if的嵌套结构。

3 语义“陷阱”

3.1整数溢出

C语言提供的数据类型有许多,其中整型数据提供了三种不同长度的类别:short int、int和long int。不管是哪种数据类型都有一个固定的长度,它能存储的最大值是一个固定的整数,当尝试去存储一个大于这个固定最大值时,将会导致整数溢出.

例如:求满足条件1+2+3+…+n≤32767的最大整数n,如下实例10:

main()

{int n=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定义为long int型。

3.2求值顺序

可能说起求值顺序,许多人能联想到运算符的优先级问题,但其实这不是一回事。运算符优先级是诸如:a+b*c等同于a+(b*c);而求值顺序是诸如:if(n!=0&&x/n>y),此时即使n=0时,也不会出现“0作为除数”的错误。原因就是因为当n=0时,n!=0的表达式为0,不会再运算到后面的表达式。

C语言中某些运算符总是以一种已知的、规定的顺序来对其操作数进行求值,而另外一些则不是这样的。再如:a

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).

实际中,C语言中switch语句的这种特性,既是它的优点,又是它的一大弱点。说到弱点是因为初学者很容易遗漏各个case后面的break语句,造成难以理解的程序结果。说到优势是因为当程序员有意的略去一个break语句,则可以实现多个分支共同作用处理,重要的是看程序员如何应用。

2.3 else“悬挂”引发的问题

此问题是大多数初学者很容易弄错,而且并非C语言独有,其他语言也会让程序员们常常失误。在应用多支路的程序设计中,往往会用到if的嵌套结构,常常容易出现错误。

如实现如下的分段函数。程序代码示例9:

int main()

{ int x,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;}

else y=-5;

此时,else并没有同离它更近的第二个if匹配,而是与第一个if结合,因为第二个if已经被括号“封闭”起来了,构成了if的嵌套结构。

3 语义“陷阱”

3.1整数溢出

C语言提供的数据类型有许多,其中整型数据提供了三种不同长度的类别:short int、int和long int。不管是哪种数据类型都有一个固定的长度,它能存储的最大值是一个固定的整数,当尝试去存储一个大于这个固定最大值时,将会导致整数溢出.

例如:求满足条件1+2+3+…+n≤32767的最大整数n,如下实例10:

main()

{int n=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定义为long int型。

3.2求值顺序

可能说起求值顺序,许多人能联想到运算符的优先级问题,但其实这不是一回事。运算符优先级是诸如:a+b*c等同于a+(b*c);而求值顺序是诸如:if(n!=0&&x/n>y),此时即使n=0时,也不会出现“0作为除数”的错误。原因就是因为当n=0时,n!=0的表达式为0,不会再运算到后面的表达式。

C语言中某些运算符总是以一种已知的、规定的顺序来对其操作数进行求值,而另外一些则不是这样的。再如:a

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).

猜你喜欢
常见错误初学者C语言
初学者,赶紧看过来
基于Visual Studio Code的C语言程序设计实践教学探索
基于C语言的计算机软件编程
浅谈如何提高初学者的钢琴演奏能力
初学者如何临写《九成宫醴泉路》
关于初中数学最简二次根式的探究
初中生解一元一次方程常见错误及成因分析
高职高专院校C语言程序设计教学改革探索
浅析大学英语写作常见错误及相应技巧
论子函数在C语言数据格式输出中的应用