C语言研究性学习的路线
现行的多数C语言教材有太多的误区,不仅不能给读者提供有效的学习线索,还常常“误导”读者,于是,“死记硬背”便成了学习C语言的唯一选择。本文以拙作《新编C语言程序设计教程》(清华大学出版社出版,博客blog.csdn/stunt配套视频zeq126.56)为基础,探讨了C语言的研究性学习。
C语言的学习可分为四个阶段。
第一阶段 理解C语言与计算机的关系
理解计算机如何计算。
计算机由五大部件组成,C语言如何使用这五大组成部件?
计算机采用二进制,那么正负号,小数点、字符等如何在计算机中存储?采用不同的编码方式,计算机中的运算与数学上的运算有何异同?
在理解上述问题的基础上,能根据优先级、结合性、数据类型和序列点熟练地对C语言表达
式求值,也就是能模拟计算机进行计算。
第二阶段 掌握编程方法
计算机通过连续地对表达式求值(计算)解决实际问题。
掌握逻辑运算之后,理解计算机解决问题的方法,即有条件地重复。编程的关键在于到利用“重复”解决问题的方法。算一次加法,再算一次加法,又算一次加法,“重复加”,如1+2+3+…+100。大僧人数为1时满足条件吗?大僧人数为2时满足条件吗?大僧人数为3时满足条件吗?“重复试”,如百僧百馍问题。
遇到问题一定要先用自己的超级计算机(大脑)分析问题出重复,再用循环把重复表达出现,这就是编程。
第三阶段 用函数组织代码
当程序由多个函数组成时,可以借助对函数封闭性的研究来理解这一阶段遇到的问题。
递归函数是个奇迹,自然而优雅地模拟了递归算法。递归算法是更高级的重复。
第四阶段 掌握指针的用法
指针提供了使用内存中相关存储单元的另一种途径。
由于只需某类型的地址,就可使用相关存储单元,指针变量可以提高函数的执行效率。由于void型指针变量的特点,程序中的某些函数可以超越类型的限制而具有了某种通用性。
理解了指针的作用,掌握指针也并非什么难事。
C语言的知识点有:
1. C语言与计算机的关系
2. 表达式的求值
3. 逻辑运算及选择结构
4. 算法及循环结构
5. 数组的作用及准确理解
6. 函数的作用及准确理解
7. 指针的作用及准确理解
8. 其它
这几部分相辅相成,构成了一个有机的整体。分析如下:
一、C语言与计算机的关系
(-)C语言如何驱动计算机
人们利用C语言与计算机沟通,借助编译系统,计算机能够理解并执行C语言程序。C语言是使用计算机的接口。
使用计算机需了解计算机的三大基本原理,特别是二进制编码和五大组成部分。C语言与计算机的五大组成部分关系如图所示。
图1-6 C语言与计算机的联系(1-6为书中编号)
在此需特别注意“变量”这一概念。C语言中用变量标识计算机中的存储单元,使用变量就是使用计算机中的存储器。如果存储单元不能存入某类存储数据如5.5,则相关变量就不能赋值为5.5。这是变量与数学中未知数如x的本质区别。以后在学习指针变量、数组变量时,主要通过分析它们标识存储单元的特点来探求其本质特征。
C语言用函数控制计算机的输入设备和输出设备,那么C语言中的函数与数学中的函数有何
异同?本质上没有区别!都是从输入到输出的映射。
不仅应注意函数在C语言中的表现形式,更重要的是明白函数与程序的关系。main函数就是程序,程序的执行过程就是main函数的执行过程。没有main函数,函数再多也不能称为程序。程序中的其它函数只有通过main函数的调用(直接或间接)才有执行的机会。借助编译系统的调试功能,不仅可以直观地观察一个“复杂”程序的执行过程,而且应明白一个道理,程序是一条语句一条语句执行的,并不神秘。
通过学习printf函数的简单使用,既能动手尝试编程,又能体会到编写程序时需要的严谨,尤其是程序的输出与代码的“绝对一致”。
重点:
1. 计算机的五大组成部分与C语言的关系。
2. 变量的概念。(存储单元的标识;分类型;先定义后使用;赋值)
3. 函数在C语言中的表现形式及重要作用。(写程序就是定义main函数)
4. 程序中#include <stdio.h>的作用。
5. 函数调用及printf函数的使用。
6. 编译系统的作用及VC6.0的使用。(程序的编译及调试执行)
(二)二进制对计算机的制约
计算机采用二进制意味着数据在计算机中只能以01数字串的形式存在,即数据必须以二进制形式编码。
虽然数值型数据可以方便地转化成二进制形式,但正负号和小数点位置的编码问题需要解决。同类型的数据在计算机中为何编码长度一致?固定的编码长度又会带来什么样的问题?
整数编码的难点在于为何采用补码形式,补码的符号位为何可以参与运算,且补码加法的运算规律。
小数编码的难点在于精度,如何估算float型和double型的精度呢?
理解了数值型数据的编码规律,则C语言(或其它编程语言)中的基本数据类型也就懂了。稍有疑问就是C语言中的整型为何有许多种类呢?
整型的知识点多而散。
1. 字面量前缀表进制(0八进制0x十六进制),后缀表示类型(l长整型u无符号整型)。
2. 输入输出时的格式字符,既有长短(l和h),又有进制(o和x)。
特别注意:
1. 用负数给无符号整型变量赋值,如unsigned long ui = -1;,或输入格式字符为无符号型时,用户却输入了一个负数,如scanf(“%uh”, &i); -1↙。
2. printf函数输出时以格式字符所指明的类型解码变量所标识存储单元的存储内容,而不考虑相关变量的实际类型,即“我的地盘我作主”。
c语言如何去学浮点型的知识点。
1. 字面量有小数形式和指数形式两种。
2. 格式字符f,e,E用在printf函数中(输出数据时)没有区别,但是当利用scanf函数获得用户输入的double型数据时,格式字符前面必须加附加格式说明符l。
难点:
1. 精度是指小数形式还是指数形式?
2. 计算机中0.1+0.1并不等于0.2,但是0.25+0.25却等于0.5!什么情况下有误差,什么情况没有误差?
字符型数据编码的原则就是统一标准,每个字符对应一个编号,计算机中存储这个编号(整数),因此C语言中字符型数据可以与整型数据通用。(当用整型解码时就输出编号printf("%d",ch);当用字符型解码时就输出字符如printf("%c",ch)。)
字符型的知识点。
1. ASCII码的一些特点,如小写字母和大写字母谁的编号大?
2. 回车键对应于C语言中哪个字符?
3. 字符型变量多样的初始化方法。
4. 语句short i = 9; 与char ch = ‘9';的区别及联系。
5. putchar函数和getchar函数的使用。
思考与探究
1. 按下5回车后,用户输入的是整数5、小数5还是字符5?
2. 用户输入多个数据时,如zzj↙,下面的代码段如何执行?
{
char ca, cb;
ca = getchar();
cb = getchar();
printf(“%c%c”, ca, cb);
}
{
char ca, cb;
ca = getchar();
cb = getchar();
printf(“%c%c”, ca, cb);
}
3. 用不同的数据类型作为求阶乘函数的返回值类型,阶乘函数最大能正确输出哪个整数的阶乘?
二、表达式的求值
(一)基础知识
在后面加一个分号(;),C语言表达式就变成了C语句,此时计算机执行C语句的过程就是对表达式求值的过程,因此表达式求值实际上模拟了计算机的计算。
C语言表达式的重要特征是每个表达式都有一个确定的值及类型。求值时需根据操作符的优先级和结合性来确定运算顺序。能否顺利地求出表达式的值可作为判断C语言表达式是否合法的依据。
从表3-1可知,C语言操作符的种类繁多,但是数学上常见的一些运算在C语言中却并没有相关的操作符,如求平方根、求绝对值、幂运算等,不过C语言提供了相应功能的库函数,如sqrt函数、fabs函数、pow函数等。与printf函数类似,使用这些库函数需要在程序中包含math.h头文件(#include <math.h>)。
重点:
在后面加一个分号(;),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型,此
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 编码长度不同,如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。
先讨论整型间的算术运算。
表达式求值通常在运算器中进行,而运算器中专用存储单元的长度是固定的,因此,整型间运算时,当操作数的编码长度“不够”长时会被自动扩充成相应的长度。需注意两点:
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型后,参与运算的“新操作数”就变成了有符号数。
特别提醒:
不能用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。
强制类型转换操作的重点在于理解操作过程。
自增自减操作符的重点在于:
强制类型转换操作的重点在于理解操作过程。
自增自减操作符的重点在于:
3. 作用。(本质为i=i+1的最简洁形式,故3++是非法的)
4. 前置与后置的区别在于表达式的值不同。(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如何求一元二次方程的根。
关键在于体会程序的执行过程,即程序执行时,其状态(每个变量的值)如何变化,每条语句是如何影响程序状态的。
三、逻辑运算及选择结构
(一) 逻辑运算简介
逻辑结算是计算机支持的一种运算,计算机中运算器的重要组成部分除了支持运算的专用存储单元,就是进行算术运算和逻辑运算的算术逻辑单元。逻辑运算使得编写功能强大的程序成为可能。
逻辑运算是指对结论进行判断并得出一个或为真或为假的值的过程。它的最大特点是运算结果只有两个值:真(对)或假(错)。
最常见的逻辑运算就是“比较”操作,如2<2,2<=2,2==2,2>2等,C语言中称之为“关系”运算。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论