谭浩强《C语言程序设计》考研考点讲义_第1页
谭浩强《C语言程序设计》考研考点讲义_第2页
谭浩强《C语言程序设计》考研考点讲义_第3页
谭浩强《C语言程序设计》考研考点讲义_第4页
谭浩强《C语言程序设计》考研考点讲义_第5页
已阅读5页,还剩174页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

考情分析及复习介绍 (1)第一章C语言概述 (14)第二章算法 (19)第三章数据类型、运算符与表达式 (33)第四章单的C程序设计 (49)第五章择结构程序设计 (60)第六章循环控制 (69)第七章数组 (80)第八章函数 (99)第九章预处理命令 (130)第十章指针 (139)第十一章结构体与共同体 (172)第十二章位运算 (201)第十三章文件 (209)11考情分析及章节介绍◆课程特点算法的概念及其表示;数据类型、运算符与表达式;程序设计结构;数组;指针;预处理命令;文件。给定以下几组定义,说明其含义:intpintpintpdef识点综合运用,难于理解22 {}运行情况输出:◆出题思路:按内容分布、按题型分布探讨按内容分布按知识点的基础内容出题D.由所用机器的机器字长所决定A.C语言程序总是从第一个定义的函数开始执行DCmain序的开始部分写出下面程序的输出结果(A) {}33 {if(x++<0)y=-1;ifxy}盘上输入-1,则程序的输出是(A)。C { {}}}44出题思路填空题:n7整除”的C语言逻辑表达式()。 以下程序的运行结果是(11)。 {}按题型角度:知识点的基本内容及综合下列标识符中,(B)是合法的用户定义的标识符。以下程序的运行结果是(B) {if((++i)||(++j>0))k++;}C.枚举元素的值可以在类型定义时指定D.枚举元素可以作为常量使用55 程序补充:个数,求其平均值。++} {*(p1+i)!=‘\0’i}*(p1+i)=\‘0’程序阅读:写出程序的输出结果。+=程序阅读:*(p1+i)=\‘0’66写出程序的输出结果。 { {}程序改错:下面程序用以计算整数阶乘。 {程序改错: {77◆按题型角度:算法及知识点的综合编程输出10至100之间的所有素数。编程输出100至999之间的所有水仙花数。 设任意含有10个元素的一维数组A和指针变量p,请用指针与数组关系编程输出该数组元素内容。◆程序设计:综合性强假设一个文本文件中保存着100个整数,请将这100个整数从大到小的顺序写到另一个文件中去,并且在新文件中每10个整数占一行。源文件名和目标文件名通过命令行参数获取。 {nfout {} 88◆复习思路从教材的角度(按章节顺序,较全面)(第一阶段)从考题的角度(抓重点,找遗漏)(第二阶段)从教材的角度(按章节顺序,较全面)(第一阶段)认真复习每个知识点,掌握C语言规则。例如:针对字符数组和字符指针变量字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。而对字符指针变量,可以采用下面方法赋值:char**a;对于容易混淆的知识点,善于归纳和总结有关整型的数据类型定义含义定义整型变量pintpp为指向整型数据的指针变量定义指针数组p,它由n个指向整型数据的指针元素组成intpn;p为指向含n个元素的一维数组的指针变量intp();p为带回整型函数值的函数int*p();p为带回一个指针的函数,该指针指向整型数据int(*p)();p为指向函数的指针,该函数返回一个整型值int**p;p是一个指针变量,它指向一个指向整型数据的指针变量66培养认真分析的习惯,建立地址分配模型。 {}运行情况输出:从考题的角度(抓重点,有遗漏)(第二阶段)主要是出题几率小的知识点。例如:下面符合要求的位段定义是(B)。 { { { {●按章节介绍知识点分布重点内容必考;主要内容●算法用自然语言表示算法用流程图表示算法:基本程序设计结构用伪代码表示算法●数据类型、运算符与表达式各类数值型数据间的混合运算算术运算符和算术表达式赋值运算符和赋值表达式逗号运算符和逗号表达式●C语言程序设计赋值语句数据输入输出字符数据输入输出格式输入与输出●选择结构关系运算符和关系表达式逻辑运算符和逻辑表达式●循环控制用for语句实现循环循环的嵌套●数组字符串和字符串结束标志字符数组的输入输出字符串处理函数●函数函数定义的一般形式、函数参数和函数的返回值数组作为函数参数局部变量和全局变量变量的存储类别:动态存储方式与静态存储方式●预处理命令参数的宏定义、带参数的宏和函数的区别”处理条件编译●指针地址和指针变量的指针和指向变量的指针变量数组与指针字符串与指针指向函数的指针、返回指针值的函数指针数组和指向指针的指针●结构体与共同体结构体数组指向结构体类型数据的指针用指针处理链表共用体枚举类型●位运算位运算符和位运算位段●文件文件类型指针文件的打开与关闭文件的读写文件的定位出错的检测文件输入输出复习思路知识点较多、知识点之间可以综合应用,复习难度大;按章节顺序、全面掌握知识点,培养综合分析考题的能力;从考题的角度、有重点地针对性地复习。第一章本章考点考点1C语言出现的历史背景C语言概述C语言是国际上广泛流行的高级语言,C语言是在B语言的基础上发展起来的。B(BCPL)语言是1970年由美国贝尔实验室设计的,并用于编写了第一个UNIX操作系统,优点:CB原来用汇编编写的UNIX,但仅在贝尔实验室使用。相辅相成。1983年,美国国家标准化协会(ANSI)根据C语言各种版本对C的发展和扩充,制定了新的标准C1987年,ANSI公布新标准—87ANSIC。目前,流行的C语言编译系统大多是以ANSIC为基础进行开发的。说明:不同版本的C编译系统所实现的语言功能和语法规则略有差别,因此了解所用的C语言编译系统的特点。考点2C语言的特点 (2)运算符丰富。34种运算符。 (3)数据类型丰富,具有现代语言的各种数据结构。 (4)具有结构化的控制语句,是完全模块化和结构化的语言。 (5)语法限制不太严格,程序设计自由度大。 (6)允许直接访问物理地址,能进行位操作,能实现汇编语言的大部分功能,可直接对硬件进行操作。 (7)目标代码质量高,程序执行效率高。只比汇编程序生成的目标代码效率低10%-20%。 (8)程序可移植性好。基本上不做修改就能用于各种型号的计算机和各种操作系统。考点3简单的C语言程序介绍/*文件包含*//*主函数*/ /*输出语句*/}每个C程序必须有一个主函数main {}是函数开始和结束的标志,不可省每个C语句以分号结束voidmain()/*主函数*/ {} {}/*对被调用函数max的声明*//*输入变量a和b的值*//*输出c的值*/ {/*以下3行为C语句*/}/*求两数之和*//*声明,定义变量为整型*/说明:/*……*/表示注释。注释对编译和运行不起作用,可以用汉字或英文字符表示,可以出现在一行中的最右侧,也可以单独成为一行。 (1)C程序是由函数构成的,使程序容易实现模块化。 (2)一个函数由两部分组成:函数体:花括号内的部分。若一个函数有多个花括号,则最外层的一对花括号为函数体的范围。函数体包括两部分:注意:函数的声明部分和执行部分都可缺省,例如: {}这是一个空函数,什么也不做,但是合法的函数。总结: Cmain执行的,与main函数的位置无关。 (2)C程序书写格式自由,一行内可以写几个语句,一个语句可以分写在多行上,C程序没有行号。 (3)每个语句和数据声明的最后必须有一个分号。 (4)C语言本身没有输入输出语句。输入和输出的操作是由库函数scanf和printf等函数来完成的。C对输入输出实行“函数化”。考点4运行C程序的步骤 (1)上机输入与编辑源程序 (2)对源程序进行编译 (3)与库函数连接 (4)运行可执行目标程序 (1)目前使用的大多数C编译系统都是集成环境(IDE)的。可用不同的编译系统对C程序进行操作。 标操作。 檪殏 檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏檪殏 檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏主要知识点:C语言的特点:运算符丰富、模块化和结构化、允许直接访问物理地址、进行位操作、可移植性好等第二章算法本章考点考点1算法的概念一个程序应包括两个方面的内容:著名计算机科学家沃思提出一个公式:完整的程序设计应该是:数据结构+算法+程序设计方法+语言工具算法”。对同一个问题,可有不同的解题方法和步骤。例如求:∑n为了有效地进行解题,不仅需要保证算法正确,还要考虑算法的质量,选择合适的算法。希望方计算机算法可分为两大类别:非数值运算:包括的面十分广泛,最常见的是用于事务管理领域,例如图书检索、人事管理、行车调度管理等。例1求1×2×3×4×5220可以设两个变量:一个变量代表被乘数,一个变量代表乘数。不另设变量存放乘积结果,而直接将每一步骤的乘积放在被乘数变量中。设p为被乘数,i为乘数。用循环算法来求结果,算法可改写:活性。S3到S5组成一个循环,在实现算法时,要反复多次S3步骤为止。此时算法结束,变量p的值就是所求结果。例题例2有50个学生,要求将成绩在80分以上者打印出来。设n表示学号,n1代表第一个学生学号,ni代表第i个学生学号。用g代表学生成绩,gi代表第i个学生成绩,算法表示如下:->变量i作为下标,用来控制序号(第几个学生,第几个成绩)。当i超过50时,表示已对50个学生的成绩处理完毕,算法结束。出。年;(2)能被100整除,又能被400整除的年份是闰年。如1600,2000年是闰年。不符合这两个条件的年份不是闰年。设y为被检测的年份,算法可表示如下:->221算法如下:分数。sum最后的值就是多项式的值。例5对一个大于或等于3的正整数,判断它是不是一个素数。概念:所谓素数,是指除了1和该数本身之外,不能被其它任何整数整除的数。例如,13是素数。分析:判断一个数n(n≥3)是否素数的方法:将n作为被除数,将2到(n-1)各个整数轮流作为算法如下:除即可。考点2算法的特性一个算法应该具有以下特点:有穷性:包含有限的操作步骤。确定性:算法中的每一个步骤都应当是确定的。有零个或多个输入:输入是指在执行算法时需要从外界取得必要的信息。有效性:算法中的每一个步骤都应当能有效地执行,并得到确定的结果。考点3算法的表示222可以用不同的方法表示算法,常用的有:传统流程图结构化流程图伪代码自然语言就是人们日常使用的语言,可以是汉语或英语或其它语言。用自然语言表示通俗易懂,含义,描述包含分支和循环的算法时也不很方便。因此,除了那些很简单的问题外,一般不用自然语言描述算法。美国国家标准化协会ANSI规定了一些常用的流程图符号:例7将例2的算法用流程图表示。打印50名学生中成绩在80分以上者的学号和成绩。例6图223例8将例3判定闰年的算法用流程图表示224例9将例4的算法用流程图表示例10将例5判断素数的算法用流程图表示 (图见视频)用流程图表示算法总结:流程图是表示算法的较好的工具。一个流程图包括以下几部分: (1)表示相应操作的框; (2)带箭头的流程线; (3)框内外必要的文字说明。优点:用流程图表示算法要比用文字描述算法逻辑清晰、易于理解。225三、三种基本结构和改进的流程图传统流程图用流程线指出各框的执行顺序,对流程线的使用没有严格限制。因此,使用者可以毫不受限制地使流程随意地转向,使流程图变得毫无规律,阅读者要花很大精力去追踪流程,使人难以理解算法的逻辑。如图:解决办法:必须限制箭头的滥用,即不允许无规律地使流程随意转向,只能顺序地进行下去。用这三种基本结构作为表示一个良好算法的基本单元。循环结构2263.三种基本结构的共同特点: (1)只有一个入口; (2)只有一个出口;(请注意:一个菱形判断框有两个出口,而一个选择结构只有一个出口。不要将菱形框的出口和选择结构的出口混淆。) (3)结构内的每一部分都有机会被执行到; (4)结构内不存在“死循环”(无终止的循环)。不正确的流程表示:总结:由三种基本结构顺序组成的算法结构,可以解决任何复杂的问题。由基本结构所构成的算法属于“结构化”的算法,它不存在无规律的转向,只在本基本结构内才允许存在分支和向前或向后的跳转。只要具有上述四个特点的都可以作为基本结构。可以自己定义基本结构,并由这些基本结构组成结构化程序。下图符合基本结构的特点:227如下是一个多分支选择结构。虚线框内的结构是一个入口一个出口,并且有上述全部的四个特点。由此构成的算法结构也是结构化的算法。可以认为这是由三种基本结构所派生出来的。 (图见视频)程线。全部算法写在一个矩形框内,在该框内还可以包含其它的从属于它的框。这种流程图又称N--S结构化流程图。个简单的操作,也可以是三个基本结构之一。如下图:A框可以是一个选择结构,B框可以是一个循环结构。228例12将例2的算法用N-S图表示。(打印50名学生中成绩高于80分的学号和成绩)判定闰年的算法用N-S图表示229将例5判别素数的算法用N-S流程图表示流程图不符合基本结构特点!不能分解为三种基本结构,无法直接用N-S流程图的三种基本结构的符号来表示。因此,应当先作必要的变换。 (图见视频)总结:一个结构化的算法是由一些基本结构顺序组成的。在基本结构之间不存在向前或向后的跳转,流程的转移只存在于一个基本结构范围之内(如循环中流程的跳转);一个非结构化的算法可以用一个等价的结构化算法代替,其功能不变。如果一个算法不能分解为若干个基本结构,则它必然不是一个结构化的算法。优点:比文字描述直观、形象、易于理解;比传统流程图紧凑易画。尤其是它废除了流程线,整个算法结构是由各个基本结构按顺序组成的,N--S流程图中的上下顺序就是执行时的顺序。用N--S图表示的算法都是结构化的算法,因为它不可能出现流程无规律的跳转,而只能自上而下地顺序执行。概念:伪代码是用介于自然语言和计算机语言之间的文字和符号来描述算法。特点:如同一篇文章一样,自上而下地写下来。每一行(或几行)表示一个基本操作。不用图形符号,书写方便、格式紧凑,比较好懂,便于向计算机语言算法(即程序)过渡。用处:适用于设计过程中需要反复修改时的流程描述。330可以用汉字伪代码表示:若x为正打印x否则也可以中英文混用。开始置t的初值为1置i的初值为2使t=t×i {循环体到此结束}输出t的值结束例17输出50个学生中成绩高于80分者的学号和成绩。用伪代码表示算法:BEGIN{算法开始} END{算法结束}概念:用计算机实现算法。计算机是无法识别流程图和伪代码的。只有用计算机语言编写的程序才能被计算机执行。因此在用流程图或伪代码描述出一个算法后,还要将它转换成计算机语言331程序。特点:用计算机语言表示算法必须严格遵循所用的语言的语法规则,这是和伪代码不同的。用处:要完成一件工作,包括设计算法和实现算法两个部分。设计算法的目的是为了实现算法。例20将例16表示的算法(求5!)用C语言表示。 {t=t*I;}}应当强调说明:写出了C程序,仍然只是描述了算法,并未实现算法。只有运行程序才是实现算法。应该说,用计算机语言表示的算法是计算机能够执行的算法。考点4结构化程序设计方法一个结构化程序:就是用高级语言表示的结构化算法。用三种基本结构组成的程序必然是结构结构化程序设计强调程序设计风格和程序结构的规范化,提倡清晰的结构。结构化程序设计方法的基本思路是:把一个复杂问题的求解过程分阶段进行,每个阶段处理的问题都控制在人们容易理解和处理的范围内。采取以下方法来保证得到结构化的程序:逐步细化;模块化设计;结构化编码。两种不同的方法: (图见视频)332檪殏 檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏用这种方法逐步分解,直到作者认为可以直接将各小段表达为文字语句为止。这种方法就叫做“自顶向下檪殏 檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏考虑周全,结构清晰,层次分明,作者容易写,读者容易看。如果发现某一部分中有一段内容不落即可,与其它部分无关。模块设计的方法: (1)模块化设计的思想实际上是一种“分而治之”的思想,把一个大任务分为若干个子任务,每一个子任务就相对简单。 (2)在拿到一个程序模块以后,根据程序模块的功能将它划分为若干个子模块,如果这些子模块的规模还嫌大,还再可以划分为更小的模块。这个过程采用自顶向下方法来实现。 (3)子模块一般不超过50行。 (4)划分子模块时应注意模块的独立性,即:使一个模块完成一项功能,耦合性愈少愈好。檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏本章小结主要知识点:S程图等333第三章本章考点考点1C的数据类型C语言提供的数据类型:指针类型数据类型、运算符与表达式考点2常量与变量在程序运行过程中,其值不能被改变的量称为常量。常量区分为不同的类型:例1符号常量的使用 {880第七章数组本章考点考点1一维数组的定义和引用数组:是一组具有相同数据类型的数据的有序集合。类型说明符数组名[常量表达式];a此数组有10个元素。 (1)数组名定名规则和变量名相同,遵循标识符定名规则。 (2)在定义数组时,要指定数组中元素的个数,方括弧中的常量表达式用来表示元素的个数,即数组长度。 (3)常量表达式中可以包括常量和符号常量,但不能包含变量。即,C语言不允许对数组的大小作动态定义,即数组的大小不依赖于程序运行过程中变量的值。例如,下面这样定义数组是不行的:数组说明中其他常见的错误①floata[0];/*数组大小为0没有意义*/②intb(2)(3);/*不能使用圆括号*/intkak;/*不能用变量说明数组大小*/3.一维数组在内存中的存放881每个数据元素占用的字节数,就是基类型的字节数。一个元素占4个字节。数组名[下标]下标可以是整型常量或整型表达式。aa[5]+a[7]-a[2*3]注意:定义数组时用到的“数组名[常量表达式]”和引用数组元素时用到的“数组名[下标]”是有区别的。例如∶inta[10];/*定义数组长度为10*/t=a[6];/*引用a数组中序号为6的元素。此时6不代表数组长度*/2.一维数组元素引用的程序实例 {}运行结果如下:98765432101.对数组元素初始化的实现方法:882 (1)在定义数组时对数组元素赋以初值。例如:将数组元素的初值依次放在一对花括弧内。经过上面的定义和初始化之后: (2)可以只给一部分元素赋值。例如:定义a数组有10个元素,但花括弧内只提供5个初值,这表示只给前面5个元素赋初值,后5个 (3)如果想使一个数组中全部元素值为0,可以写成这是与FORTRAN语言不同的,不能给数组整体赋初值。 (4)在对全部数组元素赋初值时,由于数据的个数已经确定,因此可以不指定数组长度。例如: 在第二种写法中,花括弧中有5个数,系统就会据此自动定义a数组的长度为5。但若数组长度与提供初值的个数不相同,则数组长度不能省略。例如,想定义数组长度为10,就不能省略数组长度程序实例: {f[i]=f[i-2]+f[i-1];883 {if(i%5==0)printf(″\n″);}/*For循环结束*/}/*程序结束*/举例2:用起泡法对10个数排序(由小到大)。起泡法的思路是:将相邻两个数比较,将小的调到前头注意:经过第一趟(共5次比较与交换)后,最大的数9已“沉底”。然后进行对余下的前面5个数第二趟比较。注意:经过第二趟(共4次比较与交换)后,得到次大的数8。如果有n个数,则要进行n-1趟比较。在第1趟比较中要进行n-1次两两比较,在第j趟比较中要进行n-j次两两比较。程序流程图如下: {884++ {}++printf(″\n″);}程序运行结果如下:‘3考点2二维数组的定义和引用二维数组定义的一般形式为类型说明符数组名[常量表达式][常量表达式];例:定义a为3×4(3行4列)的数组,b为5×10(5行10列)的数组。如下:不能写成注意:可以把二维数组看作是一种特殊的一维数组:它的元素又是一个一维数组。885个元素的一维数组。二维数组在内存中的存放二维数组中的元素在内存中的排列顺序是:按行存放,即先顺序存放第一行的元素,再存放第二行的元素…下图表示对a[3][4]数组存放的顺序有了二维数组的基础,那么多维数组如何定义呢?定义三维数组:floata[2][3][4];多维数组元素在内存中的排列顺序:第一维的下标变化最慢,最右边的下标变化最快。二维数组元素的表示形式为:数组名[下标][下标]886例如:a[2][3]是整型表达式,如a[2-1][2*2-1],例如:b[1][2]=a[2][3]/2在使用数组元素时,应该注意下标值应在已定义的数组大小的范围内。常出现的错误有:inta[3][4];/*定义a为3×4的数组*/┆数据类型数组名[常量表达式1][常量表达式2]={初始化数据};可以用下面4种方法对二维数组初始化 (1)分行给二维数组赋初值。如: (2)可以将所有数据写在一个花括弧内,按数组排列的顺序对各元素赋初值。如: (3)可以对部分元素赋初值。如100050009000也可以对各行中的某一元素赋初值,如1000060000011也可以只对某几行元素赋初值。如100056000000 (4)如果对全部元素都赋初值,则定义数组时对第一维的长度可以不指定,但第二维的长度不能省。如:8787等价于:在定义时也可以只对部分元素赋初值而省略第一维的长度,但应分行赋初值。如:03000001000例4将一个二维数组行和列元素互换,存到另一个二维数组中。 { { ijb[j][i]=a[i][j];}printf(″\n″);} {printf(″\n″);}}/*程序结束*/运行结果如下:888536例5有一个3×4的矩阵,要求编程序求出其中值最大的那个元素的值,以及其所在的行号和列号。程序如下: { }889}/*程序结束*/考点3字符数组定义方法与前面介绍的类似。例如:用来存放字符数据的数组是字符数组。字符数组中的一个元素存放一个字符。对字符数组初始化,最容易理解的方式是逐个字符赋给数组中各元素。如:如果在定义字符数组时不进行初始化,则数组中各元素的值是不可预料的。如果花括弧中提供的初值个数(即字符个数)大于数组长度,则按语法错误处理。如果初值个数小于数组长度,则只将这些字符赋给数组中前面那些元素,其余的元素自动定为空字符(即′\0′)。例如: (图见视频)如果提供的初值个数与预定的数组长度相同,在定义时可以省略数组长度,系统会自动根据初值个数确定数组长度。例如:c也可以定义和初始化一个二维字符数组。例如:按函数在程序中出现的位置来分,可以有以下三种函数调用方式:把函数调用作为一个语句。如例1中的printstar(),这时不要求函数带回值,只要求函数完成一定的操作。函数出现在一个表达式中,这种表达式称为函数表达式。这时要求函数带回一个确定的值以参函数调用作为一个函数的实参。例如:函数调用作为函数的参数,实质上也是函数表达式形式调用的一种,因为函数的参数本来就要求是表达式形式。三、对被调用函数的声明和函数原型在一个函数中调用另一函数(即被调用函数)需要具备哪些条件呢? (1)首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。 (2)如果使用库函数,还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息“包含”到本文件中来。 (3)如果使用用户自己定义的函数,而该函数的位置在调用它的函数(即主调函数)的后面(在同一个文件中),应该在主调函数中对被调用的函数作声明。函数原型的一般形式为 (1)函数类型函数名(参数类型1,参数类型2……); (2)函数类型函数名(参数类型1参数名1,参数类型2参数名2,……);声明的作用是把函数名、函数参数的个数和参数类型等信息通知编译系统,以便在遇到函数调用时,编译系统能正确识别函数并检查调用是否合法。(例如函数名是否正确,实参与形参的类型和个数是否一致)。回事。函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型、函数体等,它是一个完整的、独立的函数单位。而函数的声明的作用则是把函数的名字、函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。例5对被调用的函数作声明 /*对被调用函数add的声明*/} floatz/*函数体*/}如果被调用函数的定义出现在主调函数之前,可以不必加以声明。因为编译系统已经先知道了已定义函数的有关情况,会根据函数首部提供的信息对函数的调用作正确性检查。改写例5/*函数首部*//*函数体*/ {}考点5函数的嵌套调用嵌套定义就是在定义一个函数时,其函数体内又包含另一个函数的完整定义。C语言不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中,又调用另一个函数。方法: xxfxfxxx个根。如果f 保证(x1,x2)区间内只有一个根。 (2)连接(x1,f(x1))和(x2,f(x2))两点,此线(即弦)交x轴于x。 (4)重复步骤(2)和(3),直到|f(x)|<ε为止,ε为一个很小的数,例如10-6.此时认为f (图见视频)分别用几个函数来实现各部分功能: {}xpointx*/ {y=(x1*f(x2)-x2*f(x1))/ (f(x2)-f(x1));}root,求近似根*/ {if(y*y1>0) }voidmain()/**主函数**/ {ff}考点6函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。C语言的特点之一就在于允许函数的递归调用。例如: {}例7有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后10岁。请问第5个人多大。可以用数学公式表述如下: 可以用一个函数来描述上述递归过程:intage(intn)/**求年龄的递归函数**/ {intc;/*c用作存放函数的返回值的变量*/}age5人的年龄。 {}例8用递归方法求n!可用下面的递归公式表示:例9Hanoi(汉诺)塔问题。这是一个古典的数学问题,是一个用递归方法解题的典型例子。问在下,小的在上。现在想把这64个盘子从A座移到C座,但每次只允许移动一个盘,且在移动过程中在3个座上都始终保持大盘在下,小盘在上。在移动过程中可以利用B座,要求编程序打印出移动的步骤。为便于理解,先分析将A座上3个盘子移到C座上的过程: (1)将A座上2个盘子移到B座上(借助C); (2)将A座上1个盘子移到C座上; (3)将B座上2个盘子移到C座上(借助A)。其中第(2)步可以直接实现。第1步又可用递归方法分解为:由上面的分析可知:将n个盘子从A座移到C座可以分解为以下3个步骤: (1)将A上n-1个盘借助C座先移到B座上。 (2)把A座上剩下的一个盘移到C座上。 (3)将n-1个盘从B座借助于A座移到C座上。程序如下: {/*对hanoi函数的声明*/} { }}e {}运行情况如下:考点7数组作为函数参数由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素当然可以作为函数例10有两个数组a和b,各有10个元素,将它们对应地逐个相比(即a[0]与b[0]比,a[1]与b [1]比等)。如果a数组中的元素大于b数组中的相应元素的数目多于b数组中元素大于a数组中相应元素的数目(例如a[i]>b[i]6次,b[i]>a[i]3次,其中i每次为不同的值),则认为a数组大于b printf(″\n″);printf(″\n″);++ }} }运行情况如下:1357986420‘5389-1-35604‘a[i]>b[i]4timesaibi1timesa[i]<b[i]5times可以用数组名作函数参数,此时形参应当用数组名或用指针变量。 /*函数声明*/printf(″\n″);} }运行情况如下:例12形参数组不定义长度 } }运行结果如下:例13用选择法对数组中10个整数按由小到大排序。所谓选择法就是先将10个数中最小的数与a[0]对换;再将a[1]到a[9]中最小的数与a[1]对换……每比较一轮,找出一个未经排序的数中最小的一个。共比较9轮。a[0]a[1]a[2]a[3]a[4]316613994未排序时的情况4将5个数中最小的数1与a[0]对换13694将余下的4个数中最小的数3与a[1]对换13496将余下的3个数中最小的数4与a[2]对换13469将余下的2个数中最小的数6与a[3]对换,至此完成排序程序实例 ++++printf(″\n″);} ++ ++}} } }运行结果如下:考点8局部变量和全局变量在一个函数内部定义的变量是内部变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量的。这称为“局部变量”。 } } }说明:/**函数f1**//**函数f2*//**主函数**/ (1)主函数中定义的变量(m,n)也只在主函数中有效,而不因为在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中定义的变量。 (2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。例如,上面在f1函 (3)形式参数也是局部变量。例如上面f1函数中的形参a,也只在f1函数中有效。其他函数可fa (4)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语 }}在函数内定义的变量是局部变量,而在函数之外定义的变量称为外部变量,外部变量是全局变量 (称全程变量)。全局变量可以为本文件中其他函数所共用。它的有效范围为从定义变量的位置开始到本源文件结束。 }/*外部变量*//*定义函数f1*//*外部变量**//*定义函数f2*/ } }/**主函数**/ ++}/*定义函数,形参为数组*/ ++ }}建议不在必要时不要使用全局变量,原因如下:①全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元。②使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量。③它使函数的通用性降低,因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把C程序中的函数做成一个封闭体,除了可以通过“实参—形参”的渠道与外界发生联系外,没有其他渠道。例16外部变量与局部变量同名 } }运行结果为8考点9变量的存储类别/*a,b为局部变量*/一、动态存储方式与静态存储方式前面已介绍了从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。那么从变量值存在的时间(即生存期)角度来分,又可以分为静态存储方式和动态存储方式。所谓静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。而动态存储方式则是在程序运行期间根据需要进行动态的分配存储空间的方式。这个存储空间可以分为三部分:程序区静态存储区动态存储区在C语言中每一个变量和函数有两个属性:数据类型和数据的存储类别。对数据类型,读者已熟悉(如整型、字符型等)。存储类别指的是数据在内存中存储的方式。存储方式分为两大类:静态存储根据变量的存储类别,可以知道变量的作用域和生存期。如不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。因此这类局部变量称为自动变量。自动变量用关键字auto作存储类别的声明。例如:intf(inta)/*定义f函数,a为形参*/ }有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。这时就应该指定该局部变量为“静态局部变量”,用关键字static进行声明。通过下面简单的例子可以了解它的特点。例17考察静态局部变量的值。 } }对静态局部变量的说明: (1)静态局部变量属于静态存储类别,在静态存储区内分配存储单元。在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,占动态存储区空间而不占静态存储区空间,函数调用结束后即释放。 (2)对静态局部变量是在编译时赋初值的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值而只是保留上次函数调用结束时的值。而对自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。 (3)如在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。这是由于每次函数调用结束后存储单元已释放,下次调用时又重新另分配存储单元,而所分配的单元中的值是不确定的。 (4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。例18输出1到5的阶乘值。 } f=f*n;}一般情况下,变量(包括静态存储方式和动态存储方式)的值是存放在内存中的。当程序中用到哪一个变量的值时,由控制器发出指令将内存中该变量的值送到运算器中。经过运算器进行运算,如果需要存数,再从运算器将数据送到内存存放。如果有一些变量使用频繁(例如在一个函数中执行10000次循环,每次循环中都要引用某局部变量),则为存取变量的值要花费不少时间。为提高执行效率,C语言允许将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取。由于对寄存器的存取速度远高于对内存的存取速度,因此这样做可以提高执行效率。这种变量叫做寄存器变量,用关键字例19使用寄存器变量 } f=f*i;}外部变量是在函数的外部定义的全局变量,它的作用域是从变量的定义处开始,到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。编译时将外部变量分配在静态存储区。有时需要用extern来声明外部变量,以扩展外部变量的作用城。个文件内声明外部变量例20用extern声明外部变量,扩展它在程序文件中的作用域。 } }2.在多文件的程序中声明外部变量例21用extern将外部变量的作用域扩展到其他文件。本程序的作用是给定b的值,输入a和 /*定义外部变量*//*函数声明*/}/*声明A为一个已定义的外部变量*/ {y*=A;}有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。例如: {} {…对变量而言,声明与定义的关系稍微复杂一些。在声明部分出现的变量有两种情况:一种是需要 一般为了叙述方便,把建立存储空间的声明称定义,而把不需要建立存储空间的声明称为声明。显然这里指的声明是狭义的,即非定义性声明。例如: }/*是声明不是定义。声明A是一个已定义的外部变量*/存储类别小结 (1)从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:局部变量全局变量 (离开函数,值就消失)|静态局部变量(离开函数,值仍保留)|寄存器变量(离开函数,值就消失)|(形式参数可以定义为自动变量或寄存器变量)|静态外部变量(只限本文件引用) (即非静态的外部变量,允许其他文件引用) (2)从变量存在的时间(生存期)来区分,有动态存储和静态存储两种类型。静态存储是程序整个运行时间都存在,而动态存储则是在调用函数时临时分配单元。动态存储静态存储|自动变量(本函数内有效)|寄存器变量(本函数内有效)|形式参数(本函数内有效)|静态局部变量(函数内有效)|静态外部变量(本文件内有效)|外部变量(其他文件可引用) (3)从变量值存放的位置来区分,可分为:内存中静态存储区|静态局部变量|静态外部变量(函数外部静态变量)|外部变量(可为其他文件引用)内存中动态存储区:自动变量和形式参数 (4)关于作用域和生存期的概念。从前面叙述可以知道,对一个变量的性质可以从两个方面分析,一是变量的作用域,一是变量值存在时间的长短,即生存期。前者是从空间的角度,后者是从时间的角度。二者有联系但不是同一回事。 (5)static对局部变量和全局变量的作用不同。对局部变量来说,它使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化(局部于本文件),但仍为静态存储方式。从作用域角度看,凡有static声明的,其作用域都是局限的,或者是局限于本函数内(静态局部变量),或者局限于本文件内(静态外部变量)。考点10内部函数和外部函数函数本质上是全局的,因为一个函数要被另外的函数调用,但是,也可以指定函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数区分为内部函数和外部函数。如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名static类型标识符函数名(形参表) (1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数是外部函数,可供其他文件调用。如函数首部可以写为externintfun(inta,intb)Cextern部函数。 (2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数例22有一个字符串,内有若干个字符,今输入一个字符,要求程序将字符串中该字符删去。用外部函数实现 义的3个函数*/} } } {}运行情况如下: (输入str) (输入要删去的字符) (输出已删去指定字符的字符串)檪殏 檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏檪殏 檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪檪殏本章小结主要知识点:函数的概念函数的定义与调用函数的递归调用变量的作用域函数的作用域第九章预处理命令本章考点ANSIC标准规定可以在C源程序中加入一些“预处理命令”,以改进程序设计环境,提高编程效率。这些预处理命令是由ANSIC统一规定的,但是它不是C语言本身的组成部分,不能直接对它们进行编译(因为编译程序不能识别它们)。必须在对程序进行通常的编译之前,先对程序中这些特殊的命令进行“预处理”经过预处理后程序可由编译程序对预处理后的源程序进行通常的编译处理,得到可供执行的目标代码。C语言与其他高级语言的一个重要区别是可以使用预处理命令和具有预处理的功能。C提供的预处理功能主要有以下三种:这些功能分别用宏定义命令、文件包含命令、条件编译命令来实现。为了与一般C语句相区别,考点1宏定义宏定义的作用是在本程序文件中用指定的标识符PI来代替“3.1415926”这个字符串,在编译预处理时,将程序中在该命令以后出现的所有的PI都用“3.1415926”代替。这种方法使用户能以一个简单的名字代替一个长的字符串.这个标识符(名字)称为“宏名”例1使用不带参数的宏定义 l=2.0*PI*r;s=PI*r*r;v=4.0/3*PI*r*r*r;}运行情况如下:说明: (1)宏名一般习惯用大写字母表示,以便与变量名相区别。但这并非规定,也可用小写字母。 (2)使用宏名代替一个字符串,可以减少程序中重复书写某些字符串的工作量。 (3)宏定义是用宏名代替一个字符串,只作简单置换,不作正确性检查。只有在编译已被宏展开后的源程序时才会发现语法错误并报错。 (4)宏定义不是C语句,不必在行末加分号。如果加了分号则会连分号一起进行置换。 (5)#define命令出现在程序中函数的外面,宏名的有效范围为定义命令之后到本源文件结束。 (6)可以用#undef命令终止宏定义的作用域。nt }-----↓----f1() {} (7)在进行宏定义时,可以引用已定义的宏名,可以层层置换。例9.2在宏定义中引用已定义的宏名R {}运行情况如下:printfLprintf函数调用语句展开为: (8)对程序中用双撇号括起来的字符串内的字符,即使与宏名相同,也不进行置换。 (9)宏定义是专门用于预处理命令的一个专用名词,它与定义变量的含义不同,只作字符替换,不分配内存空间。二、带参数的宏定义作用:不是进行简单的字符串替换,还要进行参数替换。带参数的宏定义一般形式为:define(参数表)字符串字符串中包含在括弧中所指定的参数:数a和b,用3*2代替S(3,2)。因此赋值语句展开对带参的宏定义是这样展开置换的:对带实参的宏(如S(3,2),则按#define命令行中指定的字符串从左到右进行置换。若串中包含宏中的形参(如a、b),则将程序中相应的实参(可以是常量、变量或表达式)代替形参。如果宏定义中的字符串中的字符不是参数字符(如a*b中的*号),则保留。这样就形成了置换的字符串。例3使用带参的宏 }运行情况如下:说明: (1)对带参数的宏展开只是将语句中的宏名后面括号内的实参字符串代替#define命令行中的形参。 (2)在宏定义时,在宏名与带参数的括弧之间不应加空格,否则将空格以后的字符都作为替代字符串的一部分。带参数的宏和函数的区别: (1)函数调用时,先求出实参表达式的值,然后代入形参。而使用带参的宏只是进行简单的字符替换。 (2)函数调用是在程序运行时处理的,为形参分配临时的内存单元。而宏展开则是在编译前进行 (3)对函数中的实参和形参类型要求一致。而宏名无类型,它的参数也无类型,只是一个符号代表,展开时代入指定的字符串即可。宏定义时,字符串可以是任何类型的数据。 (4)调用函数只可得到一个返回值,而用宏可以设法得到几个结果。例4通过宏展开得到若干个结果 } }运行情况如下: (5)使用宏次数多时,宏展开后源程序长,因为每展开一次都使程序增长,而函数调用不会使源程序变长。 (6)宏替换不占运行时间,只占编译时间。而函数调用则占运行时间(分配单元、保留现场、值传如果善于利用宏定义,可以实现程序的简化,如事先将程序中的“输出格式”定义好,以减少在输出语句中每次都要写出具体的输出格式的麻烦。例5通过宏展开得到若干个结果 }运行时输出结果:12334所谓“文件包含”处理是指一个源文件可以将另外一个源文件的全部内容包含进来。C语言提供其一般形式为:可以不定义字符数组,而定义一个字符指针。用字符指针指向字符串中的字符。例16定义字符指针 a}对字符串中字符的存取,可以用下标方法,也可以用指针方法例17将字符串a复制为字符串b *(b+i)=′\0′;printf(″\n″);}也可以设指针变量,用它的值的改变来指向字符串中的不同的字符。例18用指针变量来处理例17问题。 *p2=′\0′;printf(″\n″);}例19用函数调用实现字符串的复制 (1)用字符数组作参数 } while(from[i]!=′\0′) to[i]=′\0′;}程序运行结果如下: (2)形参用字符指针变量 acher} *to=**from;*to=′\0′;} {while((*to=*from)!=′\0′) } {while((*to++=*from++)!=′\0′);} {while(*from!=′\0′)tofrom+;*to=′\0′;}它与下面语句等价:while((*to++=*from++)!=′\0′);将*from赋给*to,如果赋值后的*to值等于′\0′,则循环终止(′\0′已赋给*to)或 while((*p2++=*p1++)!=′\0′);}三、对使用字符指针变量和字符数组的讨论虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点: (1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。 (2)赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。而对字符指针变量,可以采用下面方法赋值:char**a;但注意赋给a的不是字符,而是字符串第一个元素的地址。 (3)对字符指针变量赋初值:char**a;而对数组的初始化:不能等价于 (4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。而常有人用下面的方法,目的是想输入一个字符串,虽然一般也能运行,但这种方法是危险的:char**a;应当这样: (5)指针变量的值是可以改变的,如:例20改变指针变量的值 charaIloveChina!″;}需要说明,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。 chara″IloveChina!″;}考点5指向函数的指针变量调用函数可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。例22求a和b中的大者。先列出按一般方法的程序。 } }将main函数改写为 int(*p)();}二、用指向函数的指针作函数参数函数指针变量常用的用途之一是把指针作为参数传递到其他函数。前面介绍过,函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。现在介绍指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。它的原理可以简述如下:有一个函数(假设函数名为sub),它有两个形参(x1和x2),定义x1和x2为指向函数的指针变量。在调用函数sub时,实参为两个函数名f1和f2,给形参传递的是函数f1和f2的地址。这样在函数sub

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论