C语言研究性学习的路线
现行的多数C语言教材有太多的误区,不仅不能给读者提供有效的学习线索,还常常“误导”读者,于是,“死记硬背”便成了学习C语言的唯一选择。本文以拙作《新编C语言程序设计教程》(清华大学出版社出版,配套视频zeq126.56.com)为基础,探讨了C语言的研究性学习。
C语言的知识点有:
1. C语言与计算机的关系
2. 表达式的求值
3. 逻辑运算及选择结构
4. 算法及循环结构
5. 数组的作用及准确理解
6. 函数的作用及准确理解
7. 指针的作用及准确理解
8. 自定义数据类型及文件
这几部分相辅相成,构成了一个有机的整体。分析如下:
二、 表达式的求值
(一)基础知识
在后面加一个分号(;),C语言表达式就变成了C语句,此时计算机执行C语句的过程就是对表达式求值的过程,因此表达式求值实际上模拟了计算机的计算。
C语言表达式的重要特征是每个表达式都有一个确定的值及类型。求值时需根据操作符的优先级和结合性来确定运算顺序。能否顺利地求出表达式的值可作为判断C语言表达式是否合法的依据。
从表3-1可知,C语言操作符的种类繁多,但是数学上常见的一些运算在C语言中却并没有相关的操作符,如求平方根、求绝对值、幂运算等,不过C语言提供了相应功能的库函数,如sqrt函数、fabs函数、pow函数等。与printf函数类似,使用这些库函数需要在程序中包含math.h头文件(#include <math.h>)。
重点:
1. 每个表达式都有一个确定的值。
2. 利用数学库函数写出常见的数学式子。
3. 查表根据优先级和结合性利用加括号的方式确定复杂的表达式的求值顺序,如3+5*2为(3+(5*2));i=j=k=23为(i=(j=(k=23)))。
(二)赋值表达式
赋值操作符的优先级倒数第二,右结合。
重点:
1. 赋值操作符“=”读作“赋值为”。
2. i=i+1;i=j;的执行过程。
3. 如何判断表达式i=j=k=23的合法性。
4. 理解复合赋值操作符的本质,如i*=a+b;。
难点:
类型不匹配时的赋值操作。
1. 整型之间相互赋值
1.1 编码长度相同,但有无符号数和有符号数的区别,如unsigned short型和short型,此时只是简单地把被赋值变量的状态设置成赋值变量的存储状态。赋值后两者的值通常不同,如有unsigned uh; short h=-1; ,则uh=h;后uh的值为65535。
1.2 编码长度不同,如long型与short型或unsigned short型。以a=b为例,又分两种情况。当b的编码长度小于a时,赋值原则为赋值后两者的值相同。如有long l; unsigned short uh=65535; short h=-1;则l=uh后,l的值为65535;而l=h后,l的值为-1。由练习2.6可知,编码长度增加而值不变时编码的变化规律。当b的编码长度大于a时,赋值操作只能使a的状态与b的部分字节状态一致,舍弃了b中高位的状态,赋值后两者的值通常不同。
2. 整型与浮点型之间的相互赋值
整数可以看作是小数部分为0的浮点数,而浮点型变量向整型变量赋值时会舍弃小数部分。
注意:
1. 编程时尽量使用安全的赋值操作。(赋值后两者的值相等)
2. 应理解类型不匹配时的赋值原则。
(三)算术表达式
C语言中算术操作符的优先级和结合性虽然和数学上的一致。但由于计算机中不同类型数据的编码格式不同,当类型不同的操作数混合运算时,得出与计算机一致的结果也并非易事。
先讨论整型间的算术运算。
表达式求值通常在运算器中进行,而运算器中专用存储单元的长度是固定的,因此,整型间运算时,当操作数的编码长度“不够”长时会被自动扩充成相应的长度。需注意两点:
1. 短变长是安全的“赋值操作”。
2. C语言中这个长度是“逻辑的”,即编译系统中int型的长度,而非计算机的实际长度。int型在TC中是2个字节,在VC6.0中是4个字节。这也就意味着同样的代码可能可能在TC中不需扩充而直接计算,但在VC6.0中则需要扩充后才能计算。当然,字符型进行算术运算时被看作只有1个字节的整型,无论如何都会被扩充的。
整型间计算的难点是有符号数和无符号数混合运算时,结果也是无符号数,因此结果不会小于0。正如例3-4所示,辨析是否真正为混合运算却有难度。如有unsigned short ui=23;int j=-32;,则ui+j的值大于0吗?
在TC中,ui+j可直接计算,两者为不同类型混合运算,结果为无符号数,ui+j又不可能等于0,故它们的值大于0。
在VC中,ui只有2个字节需要扩充为4个字节的int型,扩充后值虽然没变,但参与运算的“新操作数”却变成了int型,为有符号数了,两个int型相加结果为-9小于0。
注意:
当编码长度扩充为int型后,参与运算的“新操作数”就变成了有符号数。
特别提醒:
不能用printf函数输出结果的方式查看ui+j的结果是否大于0!现阶段查看ui+j的结果是正是负是个难题。〈可以借助算术操作符%来查看结果为正还是为负〉
有浮点型参与的算术运算
由于编码的原因,浮点型运算要比整型的复杂得多。为提高运算精度,操作数有浮点型时,操作数如为整型或float型则自动转化为double型,结果也为double型。注意,正如例3-5所示,表达式的最终结果为double型,并非开始求值时就将所有操作数的类型转化为double型。(5/2+0.3)
算术表达式求值涉及的类型转换与不同类型变量相互赋值类似,原变量的值与类型并不会改变。上述的类型转换均是“自动”进行的,除此之外,类型不匹配时就会出错,如有float f=2.3;,则表达式f%3就是非法的,因为%操作符只能用于整型,求值时浮点型操作数不会自动转换成整型!只有强制改变操作数的类型为整型,相关表达式才能合法,即(int)f%3。
强制类型转换操作的重点在于理解操作过程。
自增自减操作符的重点在于:
1. 作用。(本质为i=i+1的最简洁形式,故3++是非法的)
2. 前置与后置的区别在于表达式的值不同。(int i=2;则表达式i++的值为2,表达式++i的值为3。当然两者都可使i自增1变为3)
(四)其它
1. 逗号操作符的作用为将多条C语句连接成一条C语句,因此,它的优先级最低且通常不关心表达式的值与类型。(此处有伏笔。逗号表达式i=3,++i按优先级应先计算自增操作,但实际上并非如此。)
2. C语言并没有严格规定表达式求值的方方面面,因此,特殊情况下某些表达式的值可能与编译系统相关,如(i++)+(i++)+(i++)。这不是C语言学习的重点,只需了解并编程时不使用此类表达式即可。由于可读性的重要性,过于复杂的表达式都不提倡在程序中出现,更别说此类表达式了。
3. 学习表达式有两方面的要求:一方面能根据优先级、结合性、类型转换等求值原则求出复杂表达式的值,像计算机那样“计算”;另一方面在编码时尽量选用简洁易懂无歧义的表达式,以提高程序的可读性。
4. 学习3.6典型例题需注意:
4.1 体会表达式的作用及目的,如学习例3-10时,不仅会对每条表达式求值,而且要体会程序最终的目的。
4.2 会用表达式编程解决问题。如例3-11如何输出用户输入的三位正整数的数字和;例3-12怎样交换两个字符型变量的值;例3-13如何求一元二次方程的根。
关键在于体会程序的执行过程,即程序执行时,其状态(每个变量的值)如何变化,每条语句是如何影响程序状态的。