全国计算机等级考试(二级)——C_语言辅导教...

时间:2024.5.9

全国计算机等级考试(二级)——C 语言辅导教材(清华大学出版)

1 内 容 简 介

本书共16章,其中第1章为学前自测与指导,从2~14章均包括“知识点讲析”、“补充与扩展”、“典型例题”和“本章练习”4部分,内容涉及计算机等级考试二级C语言的各个方面。第15章提供了3套笔试模拟试卷,

第16章是上机指导。书后提供了各章练习题的参考答案。

本书针对性强,内容深入浅出,实例丰富,设计新颖实用,适用于报考计算机等级考试二级C语言的读者;同时,也可以作为专本科教材辅导书,或者相当水平的培训教材。

2 丛 书 序

实践证明,通过证书考试引导考生学习某方面的专业技术,在全世界范围内都收到了很好的效果。19xx年推出的全国计算机等级考试,已成为中国除升学考试以外最大的考试之一。通过计算机等级考试,引导学生掌握必要的计算机基础知识,已成为中国的一大特色。现在,很多企事业单位在接收毕业生时,对就业人员的计算机等级考试证书都有一定的要求。

计算机等级考试推出9年来,考试大纲已经过了两次修订,最新版考纲于20xx年下半年在全国推行。

“考网——全国计算机等级考试专业网站”经过几年的苦心经营,已成为计算机等级考试考友的好朋友。考网的全体员工利用专业网站的便利,经过两年的准备,编写了这套精心设计的计算机等级考试辅导丛书。这是一套编写时间最长、准备最充分、推出最晚的辅导书。站在别人的肩膀上,当然会看得更远。我们通过对已出版图书的潜心研究,汲取其精华,剔除其糟粕,再加上通过各种途径获得的有利于考生学习知识与通过考试的资料,使得本套辅导书的读者将获得很大的收益。

本套丛书的特色如下。

以学习理论为指导,确实提高学习效率

书中的内容主要有4个模块:

? “知识点讲析”栏目,对每个知识点精化抽取最重要的信息,并根据对考纲

和历届试题的分析结果,指出了重点和难点。

? “补充和扩展”栏目,本丛书按考纲组织,形成了有机的系统,这个系统让

知识点像颗粒(或者说积木元素)一样存在着,便于读者根据自身的学习风

格“建构”自己的知识框架,可以灵活地“堆砌”这些积木(知识点),形成

自身的系统。

? “典型例题”列举了有代表性的题目,在“解题方法”中,要么揣摩出题人

的意图,要么给出同类试题适当的解题方法和思路,都是为了使有限的几个

“典型例题”能够举一反三,搭建从知识到考试的桥梁,尽可能地提高复习

备考的效率,而“知识点分析”往往是对该题所涉及的知识点的重现,使读

者能通过这些根据考试重点组织的典型例题加深理解相应的知识。

? “本章练习”为读者提供了一次回忆知识点的机会。这种重重反复而又方式

各异的栏目设计方案源于生理、心理学关于记忆的理论,这种理论(较早的

代表是艾宾豪斯的遗忘曲线)认为,知识的习得,即长时记忆的成功存储需

要多次的短时间内的“重现”。

纵观这四个栏目,可以发现,对于每一章,读者是这样建构知识系统的:用“知识点讲析”搭建系统框架,“补充和扩展”使系统枝繁叶茂,有血有肉,“典型例题”重现重点

3难点,并且完成从外观到内涵、从理论到应用的转变,“本章练习”又重现知识点,使读者停留在重点难点的眼光不至于遗漏了其他知识而造成考试中的盲点。

以学习者为中心,精心设计,方便易用

本丛书中每本书的第1章首先进行学习需求分析和起始能力的测试,根据每一章涉及的重点知识点组织的学前自测题,是读者把握自己起始能力的好工具。了解初始能力后,可以在学习计划表中填写考试复习时间表,建议分章填写,即填写一章的计划后,等到这一章复习结束,再填写另外一章。在每章最后,还有简单的“本章小结表”,用于及时总结自己的学习成果,记录学习中遇到的问题,并呼应“学习计划表”对自学过程进行自我监控。

介绍考试经验,提高考试通过率

本丛书还介绍了复习备考的成功经验,提供了历年考题(笔试和上机)的分析,帮助考生了解出题方向,既能了解自己的起始水平(通过学前自测题),又能知道历年试题的情况,在此基础上得到复习备考的建议,进而设计自己的学习计划。这样相当于在知己知彼的情况下,明确了目标,并制定了行动方案,不仅对该项考试有很大的好处,读者甚至可以借此学会应付各种考试的有效“套路”,那样,收获就不止于一次考试的成功了。

附赠超值光盘

本书附赠的超值光盘包含五大部分内容:

? 超大题库,真实考试环境的模拟和补充扩展。有笔试练习系统,笔试模拟考

试环境以及上机模拟环境。针对20xx年的最新考题,提供了超大题库组成的

考试模拟题,读者可以在学习过程中进行训练和复习,另外,还有评分和分

析系统,可查看正确答案以及经典的试题分析。

? 等级考试信息电子教程,便于以多种方式学习和复习,读者可利用电子书签

标识自己的学习进度。

? 丰富的考试资讯:等级考试的政策,报考和考试方式及注意事项,最新考纲

及大纲解读;阅卷老师的体会;考生常问问题,如考试时间和成绩查询等等。

? 实用的工具。提供了Acrobat Reader以及TC等编程平台环境。

与网络结合,轻松获得帮助

建议读者在埋头苦读之余,利用网络进行交流。读者在学习的过程中,如遇到问题,可登录考网(网址www.Kaowang.com),从其“在线答疑”中获得帮助。

最后,感谢诸位专家教授的指导和帮助:北京航空航天大学的郑晓齐教授,北京现代培训学校的邸雪峰先生,以及本丛书的副主编考网的魏笔凡、郭笑坤和李学胜先生;感谢我的父母和爱人李春玉对我的理解和大力支持;感谢本套丛书的作者,他们都认真负责,兢兢业业,几易其稿,有的甚至修改原稿多达4~6遍,使本套书成为同类书籍中的精品。

再次向所有对本丛书有贡献的人表示衷心的感谢!

考网网站站长 何雄

20xx年9月于北京航空航天大学高教所

4 前 言

本书是针对报考全国计算机等级考试二级(C语言)的考生编写的一本辅导参考书。书中按照计算机等级考试大纲的要求分章节进行讲述。同时,作为一本较好的C语言参考资料,对学习C语言的读者也有很好的指导作用。对于本书的读者,建议先阅读第1章学前测试。在作题时,可以进行自我测试,然后根据文章中给出的答案

和复习建议制定出适合自己的学习或者复习计划。

对于初级读者,建议从第1章开始看起。虽然读者对计算机和C语言的基本知识有所了解,但并没有达到等级考试所要求的程序,所以从最基本的知识学起是比较好的。

对于中初读者,建议从第4章或第5章看起。因为对于这部分读者来说,已经基本掌握了计算机的基础知识,所以没有必要再在这些知识上浪费很多的时间。第4章或第5章是C语言中基础的部分,但相对于前面的章节又是较为高级的,从这里开始学起是适合中级读者的。

对于高级读者,建议重点学习C语言中较为难学的函数、指针、结构体、共用体、以及文件这些章节。在学习这些知识的同时还可以学习更为高级的关于C语言的知识,比如使用C语言进行绘图等,甚至还可以学习面向对象的编程语言C++语言。

第1章针对计算机等级考试的考试热点和难点对读者进行学前测试,主要目的是为读者提供自我检查的机会。同时本章还根据测试结果,按照读者的不同程度分级别给出复习建议。

第2章讲述了计算机基础知识,包括计算机系统、硬件和软件的基本知识,计算机的安全问题以及计算机网络和多媒体的基本知识。

第3~14章重点讲述了程序设计语言——C语言。

第3章是整体介绍C语言的结构。这一章概括讲述C语言的风格特点,C语言源程序的格式、构成以及main函数和其他函数的构成,等等。

第4章介绍C语言的数据类型及其运算。数据类型在任何一种编程语言中都是非常重要的内容,这一章既讲述C语言最基本的数据类型,同时也讲述了C语言的运算符和表达式类型。

第5章介绍了C语言的基本语句。这一章介绍了为编写简单的C源程序所必需的一些内容。重点讲述了表达式语句、空语句和复合语句。

第6章介绍了C语言中的选择结构程序设计方法。在C语言中可以有两种方法实现选择结构,一种是用if语句实现,另外一种就是用switch语句来实现。同时C语言的选择结构是允许嵌套的。

第7章讲述了C语言中的循环结构程序设计方法。在C语言中可以有两种方法实现循环结构,一种是用for语句实现,另外一种就是用while语句或者do while语句来实现。同样C语言的循环结构也是允许嵌套的。同时本章还讲述了continue语句和break语句。

第8章讲述C语言中数组的定义和引用方法。包括一维数组和多维数组的定义、初始

5化和引用以及字符串与字符数组等知识点。

第9章详细介绍了C语言中非常重要的知识点——函数。本章详尽地介绍了函数的定义方法和引用方法;函数的类型和返回值;函数的正确调用,嵌套调用,递归调用;函数的形式参数与实在参数;局部变量和全局变量;变量的存储类别,变量的作用域和生存期;内部函数与外部函数以及库函数的正确调用。

第10章介绍了编译预处理的概念和使用方法。包括不带参数的宏定义,带参数的宏定义以及“文件包含”处理。

第11章介绍了C语言中的指针概念。指针是极其重要的一个概念,而且是难点之一。本章详细介绍了指针和指针变量的概念。包括变量、数组、字符串、函数、结构的指针以及指向变量、数组、字符串、函数、结构体的指针变量;用指针作函数参数;返回指针值的指针函数;指针数组,指向指针的指针,main函数的命令行参数。

第12章讲述了结构体和共用体的定义方法和使用方法。本章的另外一个重要概念是关于用指针和结构体构成的链表。重点讲述了单向链表的建立、输出、删除与插入等操作。

第13章是讲述C语言位运算的。包括位运算符的含义及使用以及简单的位运算。

第14章是关于文件操作的。文件的概念和使用在C语言中是极为重要的,因为在编制大型程序时,文件是经常用到的。本章即讲述了缓冲文件系统(即高级磁盘I/O系统)。包括文件类型指针;文件的打开与关闭;文件的读写以及文件的定位。

第 15章是计算机等级考试上机指导。主要讲述了计算机等级考试上机考试的有关事项。比如大纲要求的计算机基本操作;按给定要求编写和运行程序以及调试程序。同时将DOS、Windows操作系统的基本操作作为上机指导的一部分在此进行讲述,因为这一部分也具有很强的实践性。在本章的最后给出了计算机等级考试的真实上机环境。

第2~12章中用“【】”括起来的内容均属大纲考点,其后若有图标,不同的图标表示不同的内容: 表示该考点为重点 表示该考点为难点。

本书由考网的郭笑鲲主笔,潇湘工作室的陈河南先生负责图书的总体策划和最终统稿,另外,贺军、贺民、龚亚萍、李志云、戴军、肖迎、王学农、陈安南、李晓春、谢高联、李志伟、王巧红、王朝阳、王雷、周里文、吴少波、杨颖等人在预读、查错、测试等工作中付出了很多努力,在此一并表示感谢!

感谢读者选择本书。如果读者在阅读本书的过程中遇到问题,或有其他意见和建议,请发电子邮件至:

编者

20xx年9月5日

Xiaoxiang-007@sohu.com

6 第 1 章 学前自测与指导

内容提要

1.学前自测试题

2.试题答案与讲析

3.历届笔试试题分析

4.历届上机试题分析

5.应试学习指导

6.C语言学习指导

7.应试技巧指导

8.学习计划表

【学前自测试题】

基础知识

1.通常所说的计算机主机主要包括( )。

A)CPU B)CPU和内存

C)CPU、内存与外存 D)CPU、内存与硬盘

2.下面存储器中,存取速度最快的是( )。

A)软盘 B)硬盘

C)光盘 D)内存

3.系统软件中最重要的是( )。

A)操作系统 B)语言处理程序

C)工具软件 D)数据库管理系统

4.一张存储容量是1.44MB的软磁盘,可以存储大约140万个(

A)ASCII字符 B)中文字符

。 )

C)盘中文件 D)子目录

5.800个24?24点阵汉字字型库所需要的存储容量是( )。

A)7.04KB B)56.24KB

C)7200B D)450KB

6.某张软盘上已染有病毒,为防止该病毒传染计算机系统,正确的措施是( )。

A)删除该软盘上所有程序 B)给该软盘加上写保护

1 C)将该软盘放一段时间后再用 D)将该软盘重新格式化

7.磁盘处于写保护状态,那么磁盘中的数据( )。

A)不能读出,不能删改,也不能写入新数据

B)可以读出,不能删改,也不能写入新数据

C)可以读出,可以删改,但不能写入新数据

D)可以读出,不能删改,但可以写入新数据

8.计算机网络能传送的信息是( )。

A)所有的多媒体信息 B)只有文本信息

C)除声音外的所有信息 D)文本和图像信息

9.将二进制数10010001转换为十制数应该是( )。

A)100 B)145 C)20 D)126

DOS的基本操作

1.DOS系统启动后,下列文件中常驻内存的是( )。

A)DOS.COM B)COMMAND.COM

C)DISKCOPY.COM D)SYS.COM

2.若当前盘为C盘,在A盘目录\data中只有文本文件test.DAT,A 盘当前目录为根目录,则查看该文件的内容可使用的命令是( )。

A)TYPE data\test.DAT B)TYPE A:\data\*.*

C)TYPE \data\test.DAT D)TYPE A:\data\test.DAT

3.为了将所有扩展名为.PAS的文件改成扩展名为.P,应使用命令( )。

A)REN *.PAS *.?AS B)REN PAS P

C)REN *.PAS *.p D)REN *.PAS *.P??

4.假设DOS系统文件与所有外部命令文件都在C盘根目录中,下列DOS命令中能正确执行的是( )。

A)DEL IBMBIO.COM B)DISKCOPY C: A:

C)RD C:\ D)COPY COMMAND.COM A:

5.在下列各组的两个命令中,根据给定的条件,执行效果互相等价的是( )。

A)DEL \DOS 与RD \DOS (当前盘上有目录\DOS)

B)FORMAT A:与DEL A: *.* (A盘为启动盘)

C)DISKCOPY A: B: 与COPY A: *.* B: (A盘为启动盘)

D)COPY F1 F2 与TYPE F1>F2 (当前盘当前目录下有文件F1)

6.设当前盘为A盘,要将B盘当前目录下的两个文件A.TXT与X.TXT连接后以文件名Y.TXT存放到A盘的当前目录中,可用的命令为( )。

A)COPY A.TXT+X.TXT A:Y.TXT B)COPY B:A.TXT+X.TXT A:Y.TXT

C)COPY B:A.TXT+B:X.TXT >Y.TXT D)COPY B:A.TXT+B:X.TXT Y.TXT

7.下列4组DOS命令中,互相等价的一组是( )。

A)COPY A: *.* B: 与 DISKCOPY A: B:

B)COPY ABC.TXT+XYZ.TXT 与 TYPE XYZ.TXT >> ABC.TXT

2 C)COPY ABC.TXT+XYZ.TXT XYZ.TXT 与 COPY XYZ.TXT+ABC.TXT

D)TYPE *.FOR>CON 与 COPY *.FOR CON

8.设当前盘中某一目录路径为\A\B\C\D\XU,当前目录为\A。要将根目录下扩展名为.C的所有文件复制到当前目录下,并将扩展名改为.FOR,应使用命令( )。

A)COPY \*.C *.FOR B)COPY *.C \A\*.FOR

C)COPY *.FOR \*.C D)COPY \A\*.C *.FOR

Windows的基本操作

1.在Windows中,启动应用程序的正确方法是( )。

A)用鼠标双击该应用程序图标 B)将该应用程序窗口最小化成图标

C)将该应用程序窗口还原 D)将鼠标指向该应用程序图标

2.在Windows中,终止应用程序执行的正确方法是( )。

A)用鼠标双击应用程序窗口左上角的控制菜单框

B)将应用程序窗口最小化成图标

C)用鼠标双击应用程序窗口右上角的还原按钮

D)用鼠标双击应用程序窗口中的标题栏

3.在Windows中,将一个应用程序窗口最小化之后,该应用程序( )。

A)仍在后台运行 B)暂时停止运行

C)完全停止之后 D)出错

4.Windows应用环境中鼠标的拖动操作不能完成的是( )。

A)当窗口不是最大时,可以移动窗口的位置

B)当窗口最大时,可以将窗口缩小成图标

C)当窗口有滚动条时可以实现窗口内容的滚动

D)可以将一个文件移动(或复制)到另一个目录中去

5.要在Windows标准窗口的下拉菜单中选择命令,下列操作错误的是( )。

A)用鼠标单击该命令

B)用键盘上的上下方向键将高亮度条移至该命令后再按回车键

C)同时按下Alt键与该命令后括号中带有下划线的字母键

D)直接按该命令后面括号中带有下划线的字母键

C语言的结构

1.在C语言中,程序的基本单位是______,一个C源程序至少包含一个______,当然也可以包含一个______和许多个其他的______。

2.C语言具有层次清晰的特点,它用函数作为程序模块以实现程序的______,从而使得程序易于调试和维护,符合现代编程的风格,所以C语言是一种______语言。

阅读下面的程序,回答3、4题。

程序1-1:P1-1.c

#include<math.h>

#include<stdio.h>

main()

3 {

double s;

printf("Input a number:\n");

scanf("%f",&s);

s=sqrt(s);

printf("%lf\n",s);

}

3.在上面的程序中,include称为______,而类似math.h的以.h为扩展名的文件称为______。

4.在程序1-1中,属于数据说明的语句是______,整个函数体包含在______之中。

5.与一般的编程语言相比较,C语言具有限制小,灵活性______,语法限制______,程序设计自由度大,

可移植性______,并且能够______访问内存。

6.以下说法中正确的是( )。

A)C语言程序总是从第一个函数开始执行

B)在C语言程序中,要调用的函数必须在main()函数中定义

C)C语言程序总是从main()函数开始执行

D)C语言程序中的main()函数必须放在程序的开始部分

数据类型及其运算

1.C语言的数据类型可以分为基本类型、______类型、______类型和空类型。其中基本类型又可以分为整型、______、______和枚举类型。构造类型可以分为______类型和______类型。

2.在C语言中,合法的字符常量是( )。

A)'\084' B)'\x43'

C)'ab' D)"\0"

3.在C语言中不但规定了运算符的优先级,并且规定了运算符的______,这也是其他高级语言所没有的,同时也增加了C语言的复杂性。例如,算术运算符的结合性是______,自增运算符“++”的结合性是______。

4.C语言中,运作对象必须是整型数据的运算符是( )。

A)% B)\

C)%和\ D)* *

5.为表示关系x?y?z,应使用C语言表达式( )。

A)(x>=y)&&(y>=z) B)(x>=y)AND(y>=z)

C)(x>=y>=z) D)(x>=y)&(y>=z)

6.如果假设

a=2,b=3,x=3.5,y=2.5

那么下面的算术表达式的值是( )。

(float)(a+b)/2+(int)x%(int)y

A)2 B)3

4 C)3.5 D)2.5

7.在C语言中,合法的长整型常数是( )。

A)OL B)4962710

C)324562& D)216D

8.以下程序的输出结果是( )。

程序1-2:P1-2.c

main()

{

int x=10,y=10;

printf("%d %d\n",x--,--y);

}

A)10 10 B)9 9

C)9 10 D)10 9

基本语句

1.C语言中一共有5类语句,它们是控制语句、______语句、______语句、空语句和______。其中______语句在程序中可以用作空循环体。

2.以下合法的赋值语句是( )。

A)x=y=100; B)d--;

C)x+y; D)c=int(a+b);

3.x、y、z被定义为int型变量,若从键盘给x、y、z输入数据,正确的输入语句是( )。

A)INPUT x、y、z; B)scanf("%d%d%d",&x,&y,&z);

C)scanf("%d%d%d",x,y,z); D)read("%d%d%d",&x,&y,&z);

4.以下的语句中,错误的是( )。

A)putchar('a'); B)printf("%d",n);

C)getchar(); D)scanf("%7.2f ",&a);

5.以下叙述中正确的是( )。

A)输入项可以是一个实型常量,如:

scanf("%f",3.5);

B)只有格式控制,没有输入项,也能正确输入数据到内存,例如:

scanf("a=%d ,b=%d")'

C)当输入一个实型数据时,格式控制部分可以规定小数点后的位数,例如:

scanf("%4.2f",&d);

D)当输入数据时,必须指明变量地址,例如:

scanf("%f",&f);

6.在下面的语句中除了使用控制语句以外,还使用的其他语句类型是( )。

5while(getchar()!='\n');

A)复合语句和空语句 B)表达式语句和空语句

C)空语句和函数调用语句 D)复合语句和函数调用语句选择结构程序设计

1.将下面的程序运行两遍,若分别从键盘上输入6和4,则输出结果分别是( )。程序1-3:P1-3.c

main( )

{

int x;

scanf("%d",&x);

if(x++>5)

printf("%d",x);

else

printf("%d\n",x--);

}

A)7和5 B)6和3

C)7和4 D)6和4

2.阅读以下程序:

程序1-4:P1-4.c

main()

{

int x;

scanf("%d",&x);

if(x--<5)

printf("%d",x);

else

printf("%d",x++);

}

程序运行后,如果从键盘上输入5,则输出结果是( )。

A)3 B)4

C)5 D)6

3.假定w、x、y、z、m均为int型变量,有如下程序段:

w=1;

x=2;

y=3;

z=4;

m=(w<x)?w:x;

m=(m<y)?m:y;

m=(m<z)?m:z;

则该程序运行后,m的值是( )。

A)4 B)3

C)1 D)2

64.设a=3,b=4,c=5,则下面逻辑表达式的值是:(

!(a+b)+c-1&&b+c/2

A)1 B)10.5

C)0 D)?1

5.阅读以下程序:

程序1-5:P1-5.c

main()

{

int s;

scanf("%d",&s);

switch(s==!s)

{ 。 )

case 0:printf("The number is not 0");break;

case 1:printf("The number is 0"); break;

}

}

程序运行后,如果从键盘上输入?9,则输出结果是( )。

A)The number is not 0 B)The number is 0

C)程序出错 D)0

循环结构程序设计

1.若变量i和j已经定义为int类型,则以下程序段中内循环体的总的执行次数是( )。for (i=5;i;i--)

for(j=0;j<4;j++)

{...}

A)20 B)25

C)24 D)30

2.设I,j,k均为int型变量,则执行完下面的for循环后,k的值为( )。for(i=0,j=10;i<=j;i++,j--)

k=i+j;

A)11 B)9

C)20 D)10

3.有以下程序:

程序1-6:P1-6.c

main()

{

int i,j;

for(j=10;j<11;j++)

{

for(i=9;i<j;i++)

if(!(j%i))

7 break;

if(i=j-1)

printf("%d",j);

}

}

输出结果是( )。

A)11 B)10

C)9 D)12

4.以下程序的输出结果是( )。

程序1-7:P1-7.c

main()

{

int a,b;

for(a=1,b=1;a<=100;a++)

{

if(b>=10)

break;

if(b%3==1)

{

b+=3;

continue;

}

}

printf("%d\n",a);

}

A)101 B)6

C)5 D)4

5.假定a和b为int型变量,则执行以下语句后b的值为(

a=1;

b=10;

do

{

b-=a;

a++;

}

while (b--<0);

A)9 B)-2

C)-1 D)8

6.在C语言中,下面的说法正确的是( )。

A)不能使用do-while语句构成的循环 。 )

B)do-while语句构成的循环必须用break语句才能退出

C)do-while语句构成的循环,当while语句中的表达式值为非0时结束循环

D)do-while语句构成的循环,当while语句中的表达式值为0时结束循环

8数组的定义和引用

1.在下面的数组定义中,合法的是( )。

A)int a[]="string"; B)int a[5]={0,1,2,3,4,5};

C)vhst s="string"; D)char a[]={0,1,2,3,4,5};

2.若有定义和语句:

char s[10];

s="abcd";

printf("%s\n",s);

则结果是(以下k代表空格)( )。

A)输出abcd B)输出a

C)输出abcdkkkkk D)编译不通过

3.给出以下定义:

char x[]="abcdefg";

char y[]={'a','b','c','d','e','f','g'};

则正确的叙述为( )。

A)数组x和数组y等价 B)数组x和数组y的长度相同

C)数组x的长度大于数组y的长度 D)数组x的长度小于数组y的长度4.若有以下定义:

double w[10];

则w数组元素下标的上限为______,下限为______。

5.下面各语句行中,正确的关于字符串操作的语句行是( )。

A)char st[4][5]={"ABCDE"};

B)char s[5]={'A','B','C','D','E'};

C)char *s; s="ABCDE";

D)char *s; scanf("%s",s);

6.下述对C语言字符数组的描述中错误的是( )。

A)字符数组可以存放字符串

B)字符数组中的字符串可以整体输入、输出

C)可以在赋值语句中通过赋值运算符“=”对字符数组整体赋值

D)不可以用关系运算符对字符数组中的字符串进行比较

7.定义如下变量和数组:

int i;

int x[3][3]={1,2,3,4,5,6,7,8,9};

则下面语句的输出结果是( )。

for(i=0;i<3;i++)

printf("%d",x[i][2-1]);

A)1,5,9 B)1,4,7 C)3,5,7 D)3,6,9

函数

91.以下函数调用语句中含有( )个实参。

func((exp1,exp2),(exp3,exp4,exp5));

A)1 B)2 C)4 D)5

2.sizeof(double)是( )。

A)一种函数调用 B)一个双精度型表达式

C)一个整型表达式 D)一个不合法的表达式

3.设有以下语句:

char str1[]="string",str2[8],*str3,*str4="string";

则( )不是对库函数strcpy的正确调用。

A)strcpy(str1,"HELLO1"); B)strcpy(str2,"HELLO2");

C)strcpy(str3,"HELLO3"); D)strcpy(str4,"HELLO4");

4.设有如下的函数:

g(x)

float x;

{

printf("\n%d",x*x);

}

则函数的类型

A)与参数x的类型相同 B)是void

C)是int D)无法确定

5.C语言规定,程序中各函数之间( )。

A)既允许直接递归调用又允许间接递归调用

B)不允许直接递归调用又不允许间接递归调用

C)允许直接递归调用不允许间接递归调用

D)不允许直接递归调用允许间接递归调用

6.以下对C语言函数的有关描述中,正确的是( )。

A)在C中,调用函数时,只能把实参的值传送给形参,形参的值不能传送给实参

B)C函数既可以嵌套定义又可以递归调用

C)函数必须有返回值,否则不能使用函数

D)C程序中有调用关系的所有函数必须放在同一个源程序文件中

7.以下叙述中不正确的是( )。

A)在C中,函数中的自动变量可以赋初值,每调用一次,赋一次初值

B)在C中,在调用函数时,实在参数和对应形参在类型上只需赋值兼容

C)在C中,外部变量的隐含类别是自动存储类别

D)在C中,函数形参可以说明为register变量

编译预处理

1.C提供3种预处理功能:______、______和条件编译。

2.指出下面这句话的问题:C语言编译预处理是在编译之前完成的。

103.设有如下宏定义

#define MYSWAP(z,x,y) {z=x; x=y; y=z;}

以下程序段通过宏调用实现变量a、b内容交换,请填空。

float a=5,b=16,c;

______;

4.下列程序的运行结果是( )。

程序1-8:P1-8.c

#include <stdio.h>

#define M 3

#define N M+1

#define NN N*N/2

main()

{

printf("%d,",NN);

printf("%d\n",5*NN);

}

A)3,17 B)4,18 C)6,18 D)8,40

5.以下程序的输出结果是( )。

程序1-9:P1-9.c

#include <stdio.h>

#define FUDGE(y) 2.84+y

#define PR(a) printf("%d",(int)(a))

#define PRINT(a) PR(a);putchar('\n')

main()

{

int x=2;

PRINT(FUDGE(5)*x);

}

A)11 B)13 C)15 D)16

指针

1.若x是整型变量,pb是基类型为整型的指针变量,则正确的赋值表达式是(

A)pb=&x B)pb=x; C)*pb=&x; D)*pb=*x 。 )

2.下面函数的功能是( )。

sss(s,t)

char *s,*t;

{

while((*s)&&(*t)&&(*t++==*s++));

return(*s-*t);

}

A)求字符串的长度 B)比较两个字符串的大小

11 C)将字符串s复制到字符串t中 D)将字符串s接续到字符串t中

3.设有说明

int(*ptr)[m];

其中的标志符ptr是( )。

A)m个指向整型变量的指针

B)指向m个整型变量的函数指针

C)一个指向具有m个整型元素的一维数组的指针

D)具有m个指针元素的一维指针数组,每个元素都只能指向整型量

4.若要用下面的程序片段使指针变量P指向一个存储型变量的动态存储单元:

int *p;

p=______malloc(sizeof(int));

则应填入( )。

A)int B)int *

C)(*int) D)(int *)

5.下面函数的功能是( )。

int funl(char *x)

{

char *y=x;

while(*y++);

return(y-x-1);

}

A)求字符串的长度 B)比较两个字符串的大小

C)将字符串x复制到字符串y D)将字符串x连接到字符串y后面

6.设有以下定义:

int a[4][3]={1,2,3,4,5,6,7,8,9,10,11,12};

int (*prt)[3]=a,*p=a[0];

则下列能够正确表示数组元素a[1][2]的表达式是( )。

A)* ((*prt+1)[2]) B)*(*(p+5))

C)(*prt+1)+2 D)*(*(a+1)+2)

7.函数NewPrint用于打印sin(x)、cos(x)和ln(x)等函数的曲线模拟,其参数就是上述函数,则NewPrint的原型为( )。

A)double NewPrint(double x)

B)void NewPrint(double(*f)(double x))

C)void (*NewPrint)(double x)

D)void (*NewPrint)(double f(double x))

结构体与共用体

1.结构体又称为“______”,是由具有不同数据类型的多个变量组合而成的数据存储形式。定义一个结构体类型的一般形式为:

12struct 结构体名{成员列表};

其中的成员又可以称为“______”,成员表列可以称为“______”。

2.若有以下的说明,则对初值中字符 'a' 的引用方式为( )。

static struct

{

char ch;

double x;

char a[];

}c[2][2]={

{ {'a',3.5,'bc'},

{'c',4.5,'de'},

{'m',8.6,'abc'}

}

};

A)c.ch B)c[0][0].ch

C)c[1][1].ch D)a[0]

3.设有以下说明语句:

struct ex

{

int x;

float y;

char z;

}example;

则下面的叙述中不正确的是( )。

A)struct结构体类型的关键字 B)example是结构体类型名

C)x,y,z都是结构体成员名 D)struct ex是结构体类型

4.以下选项中,能定义s为合法的结构体变量的是( )。

A)typedef struct abc

{

double a;

char b[10];

}s;

B)struct

{

double a;

char b[10];

}s;

C)struct abc

{

double a;

char b[10];

}

abc s;

D)typedef struct abc

{

13 double a;

char b[10];

}

abc s;

5.变量a所占内存字节数是( )。

union U

{

char st[4];

int i;

long l;

};

struct A

{

int c;

union U u;

}a;

A)4 B)5 C)6 D)8

位运算

1.在位运算符中除了取反运算符“~”以外,其余均为______运算符,即要求运算符的两侧各有一个运算量,并且运算量只能是______或者______数据。

2.位段就是以位为单位定义长度的______类型中的成员,就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的______。

3.整型变量x和y的值相等,且为非0值,则以下选项中,结果为0的表达式是( )。

A)x||y B)x|y C)x&y D)x^y

4.设有以下语句:

char a=3,b=6,c;

c=a^b<<2;

则c的二进制值是( )。

A)00011011 B)00010100

C)00011100 D)00011000

5.字符(char)型数据在微机内存中的存储形式是( )。

A)反码 B)补码

C)EBCDIC码 D)ASCII码

文件操作

1.标准函数fgets(s,n,f)的功能是( )。

A)从文件f中读取长度为n的字符串存入指针s所指的内存

B)从文件f中读取长度不超过n-1的字符串存入指针s所指的内存

C)从文件f中读取n个字符串存入指针s所指的内存

D)从文件f中读取长度为n-1的字符串存入指针s所指的内存

2.对于用“r”方式打开文件,下列说法正确的是( )。

14 A)用“r”方式打开的文件只能输入,不能输出

B)用“r”方式打开的文件只能输出,不能输入

C)用“r”方式可以打开任何文件

D)以上均不对

3.下面程序把从终端读入的文本(用@作为文本结束标志)输出到一个名为bi.dat的新文件中,请填空。

#include "stdio.h"

FILE *fp;

{

char ch;

if((fp=fopen(______))==NULL)

exit(0);

while((ch=getchar())!='@')

fputc(ch,fp);

fclose(fp);

}

4.下面的程序执行后,文件test.t中的内容是:

程序1-10:P1-10.C

#include <stdio.h>

void fun(char *fname,char *st)

{

FILE *myf;

int i;

myf=fopen(fname,"w");

for(i=0;i<strlen(st);i++)

fputc(st[i],myf);

fclose(myf);

}

main()

{

fun("test","new world");

fun("test","hello,");

}

A)hello, B)new worldhello,

C)new world D)hello, rld

5.下面的程序用来统计文件中字符的个数,请填空。

程序1-11:P1-11.C

#include

main()

{

FILE *fp;

long num=0;

if((fp=fopen("fname.dat","r"))==NULL)

{

15 printf("Can't open file!\n");exit(0);

}

while ______

{

fgetc(fp);

num++;

}

printf("num=%d\n",num);

fclose(fp);

}

6.设有如下程序:

程序1-12:P1-12.C

#include <stdio.h>

main(argc,argv)

int argc;

char *argv[];

{

FILE *fp;

void fc();

int i=1;

while(--argc>0)

if((fp=fopen(argv[i++],"r"))==NULL)

{

printf("Cannot open file!\n");

exit(1);

}

else

{

fc(fp);

fclose(fp);

}

}

void fc(ifp)

FILE *ifp;

{

char c;

while((c=getc(ifp))!='#')

putchar(c-32);

}

上述程序经编译、连接后生成可执行文件名为cpy.exe。假定磁盘上有3个文本文件,其文件名分别为a,b,c;内容分别为aaaa#,bbbb#,cccc#。

如果在DOS下输入cpy a b c<CR>,则程序输出的结果是:______。

【试题答案与讲析】

基础知识

16 1.B。计算机主机不能缺少的是CPU和内存,这也是主机主要包括的部件。

2.D。存取速度的比较顺序为内存>硬盘>软盘>光盘。

3.A。操作系统可以说是计算机的灵魂,没有了操作系统,任何软件比如工具软件、数据库管理系统或者语言处理程序都不能运行。

4.A。存储容量是以字节为单位的,ASCII字符占2个字节,中文字符占4个字节,盘中文件占的字节不确

定,子目录占的字节不确定。1.44MB合140万个ASCII字符。

5.B。一个24?24点阵汉字字型信息占用72个字节,800个所需要的存储容量是800?72=57600,合56.24KB。

6.D。对染有病毒的软盘最有效的杀毒方法就是重新格式化,其他诸如删除该软盘上的所有程序并不能完全确定将病毒删除。

7.B。对处于写保护状态的磁盘所能进行的惟一操作就是读出数据,不能对其删改或者写入新的数据,这也是写保护的目的所在。

8.A。想象一下网络所带给你的东西吧,文本?图像?声音?视频?所有的媒体信息都可以通过网络传输,这就是网络令人着迷的地方。

9.B。很简单的数制转换。将二进制数的各个位乘上相应的位权,然后将所有结果相加即可得到十进制数。

DOS的基本操作

1.B。DOS系统启动后常驻内存的是COMMAND.COM。

2.D。由于要查看的文件不在当前盘中,需要指定文件的路径。

3.C。通配符的使用方法。字符?表示可以是任意一个字符,字符*表示可以是任意数目的任何字符。

4.D。因为系统文件常驻内存,所以是不能用del命令删除,也不能用其他命令修改。DISKCOPY命令所需要的源盘的驱动器和目标盘的驱动器类型必须相同,容量也要相当。RD命令不能删除根目录。

5.D。TYPE F1>F2利用了输出重定向技术,将文件F1的内容输出到文件F2中去,这就相当于COPY命令。

6.D。COPY命令后所需要连接的两个文件均需要给出文件的路径。

7.B。这两组命令均是将两个文件连接在一起。

8.A。COPY命令的使用方法。参见第16章。

Windows的基本操作

1.A。启动应用程序的方法之一就是用鼠标双击该应用程序图标。

2.A。终止应用程序执行的方法之一就是用鼠标双击应用程序窗口左上角的控制菜单框。

3.A。最小化不过是将窗口变成一个图标,并没有终止应用程序的执行。

4.B。当窗口最大化时,鼠标不能拖动窗口,也不能将其缩小成图标。

5.C。这样并不能选择命令。

C语言的结构

17 1.函数,主(main)函数,main函数,函数。

函数是C语言中最重要的概念之一。

2.模块化,结构化。

这是程序设计两大思想之一,另外一个想法就是面向对象的程序设计。

3.文件包含命令,头文件。

文件包含属于编译预处理方面的内容。

4.double s; ,{}。

考生需要清楚C语言的源程序的基本格式是什么。

5.大,不太严格,好,直接。

这些就是C语言的特点,也是C语言相对于其他语言的特别之处。

6.C。main函数在C语言源程序中的地位是非常特别的。

数据类型及其运算

1.构造,指针,字符型、实型,结构体,共用体。

C语言提供了丰富的数据类型,这也是C语言比较灵活的原因之一。

2.B。字符常量的书写形式要求是单引号而非双引号,所以D不对;ab不是字符而是字符串,所以C不对;八进制084超出了字符范围,也是错误的;只有B是合法的字符常量。

3.结合性,自左至右,自右至左。

运算符的优先级和结合性是比较重要的知识点,一些常用运算符的优先级和结合性必须熟悉。

4.A。%前后要求是整型数据,其他均无此要求。

5.A。在C语言中,逻辑与运算是通过运算符&&来实现的。

6.C。这里涉及到数据类型的转换问题。

7.A。C语言规定,在一个数据之后加一个L就表示该数据是长整型数据。

8.D。自减运算符--的结合性是自右至左。X--表示在使用x之后,使x减1;--x表示在使用x之前先使x减1。

基本语句

1.函数调用,表达式,复合,空。

C语言的语句共有5类,如题所示。

2.B。C语句有特定的格式,从这一点来考查就可以看出A和C是错误的,对于赋值语句,需要有赋值方和被赋值方,只有选项B适合。

3.B。C语言的输入语句需要用到特定的输入函数scanf,在使用该函数时,需要注意函数的两个方面:格式控制和地址表列。

4.D。注意,使用输入函数scanf输入数据时不能规定精度,也就是说,如同选项D这种形式的写法是错误的。

5.D。输入函数scanf的输入项不能是任何形式的常量,而必须是地址,并且地址项是必须存在的,而不能是可有可无的,即输入数据时必须指定变量地址。

18 6.C。这是一个空语句的典型例子。例子中还使用了函数调用语句调用标准函数getchar()。

选择结构程序设计

1.A。选择结构给出了两种可选情况。

2.B。对于不同的x给出了两种情况,在这两种情况下分别进行不同的运算。

3.C。条件运算符要求有3个操作对象,所以称其为三目运算符,这是C语言中惟一的一个三目运算符。条件表达式(m<z)?m:z的执行顺序是:如果(m<z)条件为真,则条件表达式取值m,否则取值z。

4.A。对于本题,首先要清楚运算符的优先级。非(!)运算符最高,然后是算术运算符、关系运算符和逻辑与和逻辑或、赋值运算符等。

5.A。switch语句是多分支选择语句。

循环结构程序设计

1.A。对于一个循环,需要注意的是循环变量赋初值、循环条件和循环变量增值这三方面。循环也可以嵌套,在这种情况下循环次数的计算要将两个循环体的次数相乘。

2.D。首先要确定循环变量赋初值、循环条件和循环变量增值这3个起始条件,然后可以判断循环结束后i和j分别处于什么状态。

3.B。同样要确定循环变量赋初值、循环条件和循环变量增值这三方面。

4.D。本题包含一个循环语句和选择语句。对于循环语句,需要清楚循环条件;对于选择语句,需要明确选择的各个分支。

5.D。本题包含do-while循环结构。考生同样需要清楚循环变量赋初值、循环条件和循环变量增值这三方面。

6.D。对于do-while语句构成的循环,考生要分清它和for循环等语句的区别,特别是结束循环的条件之间的区别。

数组的定义和引用

1.D。在数组初始化时,如果花括弧中提供的初值个数大于数组长度,则做语法错误处理,如果小于数组长度,则其余的元素自动定为空字符。

2.D。printf函数不能直接输出字符串。

3.C。数组x的长度定义中包含一个空字符 '\0',而数组y中则不包含该空字符。

4.9,0

数组的下标使用方法。

5.C。对数组初始化或者赋值是有一定的规则的,选项A和B均是错误的赋值语句,而选项D的错误在于输入函数scanf不能这样使用。

6.C。对字符数组不能使用赋值语句对其整体赋值。

7.C。关于数组元素的引用方法。

函数

1.B。函数的实参是指用逗号分开的几个实体,而并不包括各个实体中的具体内容。以本题为例,由逗号分开的实体有两个,而在这两个实体中的内容则不必理会。

19 2.C。分清函数调用和表达式之间的区别。sizeof 所构成的仅仅是一个表达式而已,并不是函数调用。同时要清楚,sizeof得到的值是double类型的字节数,所以是一个整型数据,而非双精度数据。

3.C。本题需要清楚库函数strcpy的调用方法。具体可以参见库函数手册。

4.C。函数的类型即函数返回值的类型。该函数的返回值是执行函数 printf正确与否,正确则返回1,错误则返回0,所以该函数的类型是int型。

5.A。函数既可以直接递归调用也可以间接递归调用。

6.A。调用函数就是将实参的值传给形参,但形参的值是不能返回给实参的。函数可以递归调用,但不能嵌套定义。函数不必一定有返回值,可以是void类型的。有调用关系的函数不必在一个源文件中,只要在主函数中说明即可。

7.C。在C语言中,外部变量的隐含类别是静态存储类别。

编译预处理

1.宏定义,文件包含。

考生应清楚C语言提供的预处理功能,并且清楚其概念。

2.答案略。

编译预处理是C语言特有的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分做处理,处理完毕自动进入对源程序的编译。编译预处理是在进行编译的第一遍扫描(词法扫描和语法分析)之前所做的工作。

3.MYSWAP(c,a,b)

根据宏定义的展开规则,本题不难作答。

4.D。本题同样需要清楚宏定义的展开规则。

5.C。宏定义的展开是编译预处理一章的考查重点,考生务必要对其熟悉。

指针

1.A。将一个整型变量赋给一个指针类型的变量,只能是将整型变量的地址赋给指针变量。利用取地址运算符&得到变量x的地址,从而将其赋给指针变量pb。

2.B。比较字符串s和t的大小。

3.C。说明int(*ptr)[m];的含义就是定义一个数组指针,该数组指针指向一个具有m个整型元素的一维数组。

4.D。该题涉及到类型转换问题。如果要将某一类型的数据转换成指针类型的数据,要用诸如(类型说明符 *)这种形式来实现。

5.A。显然是求字符串x的长度。

6.D。对数组元素的引用,需要清楚的是数组的指针就是数组的首地址,也就是数组第一个元素的地址,然后可以在此基础上进行运算找到数组中某一指定的元素。

7.C。在本题中定义了指针类型的函数,即函数的返回值为一个指针。同时题目中使用了类型转换使得函数无返回值。

结构体与共用体

1.构造,分量,域表。

20 结构体的定义。

2.B。结构体变量的引用需要用到成员(分量)运算符“.”。一般引用形式是:结构体变量名.成员名。在本题中字符 'a' 属于结构体数组中某一成员的一个分量,对它的引用如同选项B所示的方式。

3.B。结构体变量的定义方法有3种,题目中给出了其中的一种。在这个定义中,struct是结构体类型的关键字;x,y,z都是结构体成员名;struct ex是结构体类型;example是结构体变量名而不是结构体类型名。

4.B。结构体变量的定义方法有3种,选项B是其中的一种。选项A因为有了typedef关键字,所以它是类型定义,而不是结构体变量的定义;选项D同样是类型定义;选项C的错误在于语法错误,结构体类型定义中缺少分号“;”。

5.C。共用体是使用覆盖技术,让几个变量互相覆盖,也就是使得几个不同的变量共占同一段内存。对于共用体来说,系统并不是为每一个共用体成员分配一个内存空间,而是所有成员公用同一段内存空间,这样一来,共用体变量所占的内存长度等于最长的成员的长度。

位运算

1.二目,整型,字符型。

位运算符的形式。

2.结构体,位数。

位段的含义。

3.D。逻辑运算符“||”和位运算符“|”在逻辑关系上是相同的,即参与运算的两个操作数只要其中一个为1,则结果就是1。但这两个运算符在用法上却是不同的。位运算符“|”要求其两边必须是以“位”为单位的数据,可以是整型或者字符型的数据,但不能是其他普通类型的数据。并且在处理这些数据时是以“位”为单位进行的。

4.A。“左移”运算符的性质是将一个数的各二进位全部左移若干位。左移的位数是由“<<”右边的数指定的。左移后溢出的高位丢弃,不足的低位补 0。本题需要注意位运算符的优先级。

5.D。字符型数据和整型数据是相同的,所以字符型数据的存储形式就是整型数据的存储形式。

文件操作

1.B。标准函数fgets(s,n,f)的功能是从f所指向的文件读出一个长度为(n?1)的字符串,存入起始地址为s的内存空间。

2.A。用“r(read)”方式打开文件只能用来向计算机内存读入数据而不能用来向该文件输出数据。并且该文件必须是已经存在的文件,即不能打开一个并不存在的文件,否则程序会显示出错信息。

3.bi.dat,"W"

在使用文件之前必须先将文件打开。程序中if语句的作用就是将文件打开并将文件信息赋给文件指针fp。while语句的作用是将从终端输入的文本写入文件,这就需要指定文件要以只写的方式打开。

21 4.A。在main()函数中,有两次调用子函数 fun。解答本题的关键就是要清楚这两次调用之间的关系。在fun子函数中规定了打开文件的方式是“只写(w)”。以这种方式打开文件时,如果原来不存在该文件,在打开时就自动新建立一个以指定名字命名的文件;如果原来已经存在一个以该文件命名的文件,打开时就将其自动删除而重新建立一个新文件。因为两次调用的文件名是相同的,所以当第二次调用fun函数时,就自动将第一次建立的文件删除而创建了一个新的空文本文件,然后就将数据“hello,”写入文件。

5.(!feof(fp))

在while语句里,逐个读取字符,每读取一个字符就使得变量num自加一,这样就可以得到整个文件的字符个数。判断while语句停止的方法是,当读取的字符为文件结束标志时即跳出循环。

6.aaaabbbbcccc

main函数是可以带有它自己的参数的,当它跟文件指针结合起来后就可以在命令行对文件进行操作。

【历届笔试试题分析】

计算机等级考试是教育部考试中心推出的一种客观、公正、科学的专门测试计算机应用人员的计算机知识与技能的全国范围的等级考试。而笔试则是其中的一项,其目的就是考查考生对计算机理论知识的掌握程度。二级笔试试卷的考查范围包括计算机基础知识与DOS和Windows操作系统的基本操作,程序设计(自己选择一种高级编程语言或一种数据库语言)。

从历年的笔试试卷来看,读者不难发现:试卷整体结构变得越来越规范,总体难度趋于稳定,知识点分布越来越广泛且趋于合理。下面以20xx年上半年的考试试卷为例,对等级考试的试卷命题情况做一下分析。

在试卷的设计中,试卷所考查的内容比例、题型比例均符合计算机等级考试大纲的规定和要求。试卷的考查

内容有相当大的覆盖面,所考查的知识点非常广泛,知识点的分布也非常广泛,这也是等级考试出题的一个方向。整张试卷的内容分量适中,中等程度的考生基本能在考试规定的时间内做完整张试卷。试题的难度合理并沿袭以往的风格,这就使得等级考试有了较高的可信度,并且使得以往试题对将要参加考试的考生来说有较高的参考价值。

下面从题型和内容上对等级考试做如下分析。

整张试卷的题型分为选择题和填空题两种。选择题共有50道题,分值不一,一般情况下是1~40题每题1分,41~50题每题2分,共60分。按照考试大纲的要求,选择题的第一部分是关于计算机基础知识的有关问题,内容均来自考试大纲。这一部分比较简单,考生只要平时对计算机有所了解,并且考前做过较为详细的复习,答对这一部分并不困难。这一部分大约有5道题。

第二部分是关于计算机基本操作的题目。这一部分一般有10道题。内容就是DOS和Windows操作系统的使用方法,其中重点是DOS的使用。由于一般的读者对DOS的使用

22比较陌生,所以会感觉这一部分难度较大。其实DOS的使用并不是无规律可循的,只要读者按照考试大纲的要求并结合本书所讲的DOS的使用命令仔细研究,是完全可以熟练掌握DOS的使用的。因为DOS的命令是有限的,其中常用的命令则更少,从这些常用命令出发就可以将DOS系统掌握。需要提醒考生的一点是,由于DOS是一种操作系统,所以在掌握它的使用方法时,惟一有效的方法就是上机练习。经常上机练习DOS的各种命令,不但能有效地复习笔试部分的相关知识点,还能高效地复习上机部分的相关题目。由于Windows的普及,想必考生对这一部分的掌握不会有太大问题。

第三部分也是最重要的一部分,就是C语言程序设计。这一部分是整张试卷的重点,也是占有分值最多的一部分。对这一部分掌握的好坏决定了考生的命运。因此这一部分成了考生复习的重点,这也是本书以绝对的篇幅来讲述C语言程序设计的原因。鉴于此,本书专门开辟了关于C语言的学习指导栏目,详见后面的论述。

【历届上机试题分析】

计算机等级考试对考生的考查分为笔试和上机两部分,成绩分别计算。只有两部分均通过者才能拿到等级证书,有一门没通过的可以参加下一次考试,通过的一门的成绩将保留至下一次考试。

上机试题包括三部分内容:完成指定的计算机基本操作(包括机器启动和操作命令的使用);按给定要求编写和运行程序;调试程序,包括对给出的不完善的程序进行修改和补充,使之能得到正确的结果。

完成指定的计算机基本操作主要就是考查DOS命令的使用。需要掌握的DOS命令在考试大纲中均已经指定,考生只要熟悉这些命令的使用方法就可以答好这一部分试题。考试以DOS 5.0为基准,内容包括所有的内部和外部命令。考生必须了解所有命令参数的使用。如果在考试过程中发现文件或目录不存在,但只要命令输入正确同样给分。如果DOS命令中需要使用软盘操作,考生只要输入命令正确同样也给分,考生无需磁盘(无盘工作站也是如此)。

按给定要求编写和运行程序主要考查考生阅读和编写程序的能力,这一部分在试题中占有较大的比重,考生需要谨慎对待。考生在自己所指定语言环境中,按给定的题目要求编制程序,经过调试、运行,最后得到结果。考生所编写的程序存放到指定的文件中,程序运行结果同样输出到指定的文件中(每行输出一个结果),一般来说,程序中的输入输出过程或函数或语句已给出,考生不必自行编写和修改。

调试程序主要考查考生阅读程序和调试程序的能力,这一部分在试题中同样占有较大比重,亦需谨慎对待。考生在自己所指定语言环境中,按给定的题目要求修改、调试相应的程序。在修改调试过程中,考生一般不允许增加或删除行数(包括空行),一行只能修改或填写一个或几个地方,还有值得注意的是在注释行中有***found***或***FOUND***的行考生不要删除,因为这是程序修改调试的标志行,删除或移动位置也将影响考生这部分的成绩。本部分要修改调试的程序已经给出,考生只要把文件拿来使用即可。

23【应试学习指导】

从考试的角度来看,考试强调的是应用性和实践性。因而实际考试的内容,并不能完全在教材中找到现成的答案。考生应通过复习和上机,积累运用计算机的技巧。通过读书很难一下获得很多技巧;动手上机,主动地提出实验任务,并付诸实现,方能丰收。不可以书本为中心,也不能丢开书本一味盲目上机,中心任务是理论体系及知识点与上机运用的结合。

要根据自己的情况,选用适当的资料。资料有教程、指导书和习题集3类。教程是系统地讲授一门课程,指导书是提纲挈领地讲述一门课,习题集则是知识点的一些具体形式。例如对二级C语言考试,如果已经系统学习过C语言,就可选用指导书,这样便于较快地复习知识体系,掌握知识重点,提高复习的效率。如果是想从头学习C语言,则要用教程之类的书。至于到底选用哪个版本的书,当然最好是国家教委考试中心指定的教材。做习题集是在掌握了知识体系的前提下很好的一种复习方法。如果没有形成知识的大框架,做习题集总是有点以偏盖全之弊。

在实际学习中要注意,上机操作的能力,对微机软硬件资源的熟练调用、程序的调试能力是非常重要的。上机考试是用一种软件来考的,这就是说还有一个考试软件的掌握和使用问题。实践表明,能事先熟悉一下考试软件的功能和特点,对于轻松自信地应考是非常有益的。全仿真考试模拟软件,是解决这些问题的有力帮手。在考前练习模拟软件,并进行模拟评分,检测自己的掌握程度,然后针对不足部分重点进行复习,应是最后准备的一个阶段。

平时做题时要对题目进行分类。现在关于等级考试的习题和例题很多,搞题海战术不是最有效的。考生可以根据考试大纲,对所做题型进行适当分类整理。比如在二级C语言笔试中,一般有3大类。一是计算机基础知识,包括计算机硬件和软件的常识,一些基本概念,各种外部设备的常识,主机的常识等;二是DOS、Windows操作系统的基本操作知识,如各种内外部命令的用法,格式及参数,DOS的3个核心文件command.com,io.sys,dos.sys,DOS的自动批处理文件Autoexec.bat和配置文件Config.sys的知识,目录、路径,当前文件等知识;三是C语言的基本知识。

上机题的编程题分数较多,对上机成绩有举足轻重的作用。如果只是纸上谈兵,没有实际的编程经验,是很难应付的。经验表示,对二级考生,为准备上机编程,应较熟练地掌握30~50个左右的程序例子。上机题一般都有输出结果,而要产生输出结果,必须运行程序。运用程序调试方法,在运行中调试程序,是一项重要的技能。

为了检验自己的考试准备情况,最好的方法是用全真模拟考试软件进行自测,现在的模拟考试软件大多是历届考试题的汇集,包括上机和笔试,基本上涵盖了考试的要求和题型,在完成时间上应较快,不论笔试模拟和上机模拟,多做几套,基本上能得90分左右,应考应不成问题。

下面将按照读者对计算机基础知识和C语言的掌握程度来分级给出指导建议。

初级程度的考生。如果考生在做了上面的学前测试题后得分没有达到及格线,那么考生应该扎扎实实地进行复习。对于基础知识的复习,考生最好在经常上机的基础上对这一

24部分知识进行复习。基础知识有其自身的逻辑关系,考生只要掌握了这种逻辑关系就可以很快学好这些基础知识。

中级程度的考生。如果考生在做了上面的学前测试题后得分达到了及格线,但并没有达到优秀或者自己满意的程度,那么考生可以有选择地进行复习。对这一部分考生来说,计算机基础知识并不成问题,所以对这一部分知识,只要读者在考试前拿出一点时间浏览一下就可以了。对于C语言,这部分考生也有较为系统的掌握,在复习时可以有针对性地进行。比如有的考生对指针一部分感到陌生,那就可以对这一部分进行深入分析。

高级程度的考生。对这一部分考生,不但基础知识,甚至C语言也不成问题。这部分考生在复习时可以深入地学习一些扩展的知识,比如使用C语言进行绘图,还可以接触一下面向对象的编程语言C++。

【C语言学习指导】

学习C语言的一个好方法,就是在计算机上进行大量的编程实践。

C语言的功能强大,使用方便灵活,从而得到了广泛的应用。但是真正地学好C、用好C并不是一件容易的事。灵活固然是好事,但也使人难以掌握,尤其对初学者来说更是如此,往往出了错还不知道是怎么回事。这就需要编程者自己来保证程序的可读性和正确性。调试一个C源程序要比调试一个Pascal或Fortran程序困难得多,在这个调试的过程中是最能显示编程者的水平的。

要想提高自己的编程水平,就必须提高自己的调试程序的能力。而这种能力的培养是必须要经过大量的编程实践才能得来的。不进行上机练习而只是阅读书本或许可能学会 C语言的语法知识,但这种知识是不能经过实践考验的,而且不能清楚地知道这些知识的用法如何,只有经过使用才能知道一些细节上的问题。

【应试技巧指导】

在实际考试时是有技巧的。因为考试都有一定的考试规则,计算机等级考试也不例外,利用这些考试规则可以有效地进行答题,保证最大限度地通过考试。

对于笔试,建议考生答题时按照题目的顺序进行,因为前面的题目一般会比后面的题目容易。

对于上机试题,考生可以按照自己的喜好来选择答题顺序,特别是对于后面两道程序修改题和编程题,考生一定要选择好答题顺序。先答完自己熟悉的题目,然后再分析较难的题目。对于DOS基本操作这一部分内容,如果考生忘记了某一命令的使用方法,可以使用DOS帮助系统,即在该命令后加上“/?”来获得帮助信息。

如果考生抽到的试题较难,而且考生确信自己不能获得及格以上的成绩,考生可以放弃这次考试,但不能保存自己的答题内容。这样考生还会获得一次答题的机会,在两次考试的间隙考生可以迅速浏览一下上一场考试中的难点问题,从而增加获胜的机会。

25【读者学习计划表】

表1-1给出考生学习计划表的样本,考生可根据自己的情况制订。

表1-1 学习计划表

进度 计划复习时间(天) 实际起止时间 备注项目

第1章 月 日 ? 月 日

第2章 月 日 ? 月 日

第3章 月 日 ? 月 日

第4章 月 日 ? 月 日

第5章 月 日 ? 月 日

第6章 月 日 ? 月 日

第7章 月 日 ? 月 日

第8章 月 日 ? 月 日

第9章 月 日 ? 月 日

第10章 月 日 ? 月 日

第11章 月 日 ? 月 日

第12章 月 日 ? 月 日

第13章 月 日 ? 月 日

第14章 月 日 ? 月 日

第16章 月 日 ? 月 日

总复习 月 日 ? 月 日

26 第 2 章 基 础 知 识

考纲要求

1.计算机系统的主要技术指标与系统配置。

2.计算机系统、硬件、软件及其相互关系。

3.计算机硬件系统的基本组成,包括中央处理器(运算器与控制器),内存

储器(RAM与ROM),外存储器(硬盘、软盘与光盘),输入设备(键盘

与鼠标)和输出设备(显示器与打印机)。

4.软件系统的组成,系统软件与应用软件;软件的基本概念,文档;程序设

计语言与语言处理程序(汇编程序、编译程序、解释程序)。

5.计算机的常用数制(二进制、十六进制及其与十进制之间的转换);数据

基本单位(位、字节、字)。

6.计算机的安全操作;计算机病毒的防治。

7.计算机网络的一般知识。

8.多媒体技术的一般知识。

知识点讲析

【计算机系统的基本组成】

计算机系统

一个完整的计算机系统由硬件系统和软件系统两大部分组成。其构成如图2-1所示。

中央处理器(CPU) 控制器(CU)

主机 运算器(ALU)

内存储器(主存)——RAM、ROM

硬件系统 存储器(辅存)——软盘、硬盘、光盘

外部设备 输入设备(Input)——键盘、鼠标等

计算机系统 输出设备(Output)——显示器、打印机等

系统软件——操作系统、语言处理程序、数据库管理系统等

软件系统

应用软件——通用应用软件、专用应用软件

27 图2-1 计算机系统的组成

半个多世纪以来,计算机已逐渐发展成为由巨型机、大型机、中型机、小型机、微型机组成的一个庞大的计算机家族。它们尽管在规模、性能、结构、应用等方面存在着很大差别,但基本组成结构都是相同的。

计算机硬件系统

计算机硬件是指构成计算机系统的物理实体或物理装置,如主板、机箱、键盘、鼠标、显示器、打印机,等等。计算机硬件是各种设备的总和,是支持计算机工作的物质基础。

计算机软件系统

计算机软件是指为运行、维护、管理和应用计算机所编制的各种程序的集合,如操作系统Windows 98、文字处理软件Word和WPS、程序设计开发软件Visual Basic,等等。计算机软件为人们提供了使用计算机的方法和手段。

软硬件系统之间的关系

计算机的硬件系统和软件系统是密切相关、相辅相成的关系。计算机硬件是软件的基础,提供机器指令、低级编程接口和运算控制能力,任何软件的使用都建立在硬件的基础上,离不开硬件设备的支持。计算机软件是对硬件功能的扩展和完善,没有软件的计算机称为裸机,其功能极为有限,甚至不能有效启动或进行最起码的数据处理工作。要使计算机能够解决各种实际问题,必须有软件的支持。需要指出,现代计算机的软、硬件之间的分界线并不十分明显,软硬件在逻辑上有着某种等价性意义。在计算机系统中,软硬件之间的功能分配是一个关键性问题,通常需要综合考虑价格、速度、存储容量、灵活性、适应性及可靠性等多方面因素。

【计算机系统的主要技术指标】

衡量计算机系统的技术指标很多,主要包括以下几点:

字长

字长以二进制位为单位,大小是CPU能够同时处理的二进制的位数;它标志了CPU进行运算和数据处理的最基本、最有效的信息位长度。微型计算机的字长为8位、16位、32位或64位。字长越大,可用来表示数的有效位越多,计算机处理数据的精度越高。因此,字长是用来衡量计算机精度的主要指标。按微机的字长可分为 8位机(如早期的苹果机)、16位机(如286微机)、32位机(如386、486、奔腾机)和 64位机(大型机)等。

内存容量

计算机中的存储器的容量一般是用字节作为基本单位。内存容量指的是计算机内存储器能存储信息的字节数。内存用于存储正在运行或随时要使用的程序和数据。内存的大小直接影响程序的运行。内存容量越大,所能存储的数据和运行的程序越多,程序运行速度越快,微型计算机处理信息的能力越强。内存一般以MB为单位,目前市场上的常见配置

28中内存应该在 128MB以上,因为目前 Microsoft 推出操作系统都至少需要或者推荐在128MB以上的内存。

存取周期

存取周期指的是对内存储器完成一次完整的读操作或写操作所需的时间,也就是存储器连续进行存取操作的最短时间间隔。通常用微秒(μs)或毫秒(ms)。存取周期是直接影响计算机速度的一个技术指标。

运算速度

运算速度通常指的是计算机每秒钟所能执行的指令条数,单位使用百万次/秒(MI/S)。显然,它是用于衡量计算机运算速度快慢的指标。

时钟频率(主频)

微型计算机时钟频率是指CPU提供有规则的电脉冲速度,即CPU在单位时间(秒)内发出的脉冲数。它在很大程度上决定了计算机的运算速度。单位一般用MHz(兆赫兹)。主频越高,计算机的运算速度越快。

【计算机系统配置】

计算机系统的配置可分为两个方面:硬件配置和软件配置。

硬件配置

1.CPU(又称中央处理器),目前流行的是32位微处理器。如Intel公司的Pentium4/1.6GHz(是指Intel公司生产的奔腾4的CPU芯片,其主频为1.6GHz);AMD公司的速龙1700+等,最新的CPU其主频已经超过2GHz。

2.主机板(简称主板),主板上主要有微处理器(CPU)和内存储器。

3.内存储器(简称内存,即RAM),目前市场上有SDRAM和DDR内存之分,DDR的带宽比SDRAM的大,通常用在中高档机型,现在流行的Pentium 4处理器一般都配以DDR内存,使两者的优点更充分地发挥,而且目前流行的配置都在128MB以上。

4.硬盘存储器(简称硬盘),较老的微机一般配置在10GB以下,现在的主流配置一般可达20GB~100GB。

5.软盘驱动器(简称软驱),一般微机均配有软盘驱动器(简称软驱),但随着计算机技术的发展,容量为

1.44MB的主流软盘已经不能适应需要。

6.显示器,目前15英寸以上大屏幕球面、纯平,乃至液晶显示器已经成为主流。

上面介绍的都是最基本的部件,另外还包括:键盘、鼠标、主机箱、显示卡、声卡、音箱、光盘驱动器、移动硬盘、打印机、Modem、网卡,还有诸多如扫描仪、数码照相机、摄像头等计算机的外部设备,使得计算机的应用更加丰富多彩。

软件配置

1.操作系统配置:一般都配有Windows 95/98/ME/NT/2000/XP,也可配备UNIX、Linux等。

29 2.语言处理程序配置:语言处理程序的配置要根据用户需要来选择。使用汇编语言编程时,就要配置汇编程序。使用高级语言编程,就要配置相应语言的编译解释程序。

3.工具软件配置:它包括PCTOOLS和NORTON等工具软件。

4.应用软件配置:应用软件根据用户具体的任务需要进行配置。例如字处理软件WPS、Word等;字表处理软件Excel等;进行网络浏览时,需配置 Internet Explorer。【微型机硬件系统】

微型机硬件由四大部分组成:中央处理器(又称 CPU,它包括运算器和控制器)、存储器(又分为内存储器和外存储器)、输入设备和输出设备。微型机硬件系统结构如图2-2所示。

输入设置 存储器 输出设置

运算器 控制器

CPU

控制信号 地址流 数据流

图2-2 微型机硬件系统组成图

中央处理器

中央处理器(简称CPU——Central Processing Unit)是微型计算机的核心部分,它又被称为微处理器,主要由运算器和控制器两部分组成。

1.运算器:又称算术逻辑单元(ALU),是计算机对数据进行加工处理的部件,包括算术运算(加、减、乘、除等)和逻辑运算(与、或、非、异或、比较等)。运算器中的数据取自内存,运算的结果又送回内存。运算器对内存的读写操作是在控制器的控制之下进行的。

2.控制器:负责从存储器中取出指令,并对指令进行译码。控制器会根据指令的要求,按时间的先后顺序,向其他各部件发出控制信号,以保证各部件协调一致地工作,一步一步地完成各种操作。

由于计算机的运算处理功能主要由CPU来完成,同时,CPU还实施对计算机其他部件的控制,所以CPU品质的高低直接决定了计算机系统的档次。反映CPU品质的最主要指标是主频和字长。主频表示CPU的工作速度,主频越高,CPU的运算速度就越快。目前,高性能的CPU主频已经超过了2GHz。字长是指CPU可以同时处理的二进制数据的位数,早期的IBM PC/XT、IBM PC/AT和286微机都是16位机,386以后的微机基本上都是32位机。

30 存储器

存储器是计算机记忆或暂存数据的部件。计算机中的全部信息,包括输入的原始数据、经初步加工的中间数据以及最后处理完成的有用数据都存放在存储器中。存储器还指挥计算机运行的各种程序,规定对输入数据如何进行加工处理的一系列指令也都存放在存储器中。存储器分为内存储器(内存)和外存储器(外存)两种。

1.内存储器

内存储器:简称内存,又称主存,由半导体器件构成。它可由CPU直接访问,存取速度快,但容量小,一般用来存放当前运行的程序和数据。整个内存被划分成许多个存储单元,每一个存储单元编排一个惟一对应的编号,称为存储单元地址,地址与存储单元一一对应。存储器所具有的存储空间大小,即所包含的存储单元总数称为存储容量,通常使用TB、GB、MB、KB、B等单位来描述。它们之间的大小关系是:

1KB = 1024B 1MB = 1024KB 1GB = 1024MB 1TB = 1024GB

按其工作方式的不同,内存储器又可以分为随机存取存储器和只读存储器。

随机存取存储器(RAM):简称随机存储器,其特点是可以读出,也可以写入。读出时并不损坏原有的存储内容,只有写入时才修改原来的存储内容。断电后,存储内容立即消失,即具有易失性。

只读存储器(ROM):只能读出原有的内容,用户不能写入新内容。原有的存储内容是由厂家采用掩膜技术一次性写入,它一般用来存放专用的、固定的程序和数据。需要强调的是,在ROM上面存储的信息都具有永久保存的优点,不会因断电而丢失。

高速缓冲存储器(Cache):简称缓存,是指可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速度极快,所以又被称为高速缓存。由于CPU处理指令和数据的速度比从常规主存中读取指令速度快,因此主存速度是系统的“瓶颈”,解决办法就是在主存和CPU之间增加一个高速缓冲存储器(Cache),使得等效的存取速度是接近于Cache的。更大容量的缓存将大大提高处理器的性能。与处理器相关的缓存一般分为两种,一级缓存(L1缓存),也称片内缓存;二级缓存(L2缓存)。早期的处理器把L1缓存集成在CPU内部,L2缓存则放到主板上在与CPU外频相同的频率下工作。新的处理器则将二级缓存也集成到CPU上。

内存储器最突出的特点是存取速度快,但是容量小、价格贵;而另一类存储器——外存储器的特点是容量大、价格低,但是存取速度慢。内存储器用于存放那些立即要用的程序和数据;外存储器一般用于存放暂时不用的程序和数据。

2.外存储器

外存储器:主要有磁盘存储器、磁带存储器和光盘存储器。

磁盘是最常用的外存储器,通常它分为硬盘和软盘两类。

(1)硬盘

硬盘驱动器(HardDisk Driver)主要由盘片组、读/写磁头、定位机构和传动系统等部分组成。磁盘片是用铝合金制成的圆盘,在其两面镀上镍钴合金并涂布上磁性材料形成记录信息的磁层。硬盘驱动器的设计大都采用温彻斯特技术。温彻斯特技术避免了因读/写磁

31头与盘片的硬接触而使盘片受损,又因气垫间隙非常小,从而提高了读/写的磁道密度和记录密度。

硬盘使用中应注意:避免频繁开关机器电源,应处于正常的温度和湿度、灰尘少、无振动、电源稳定的环境。硬盘驱动器采用了密封型空气循环方式和空气过滤装置,不得擅自拆卸。

(2)软盘

软盘(Floppy Disk)的信息记录在涂有一层磁性材料的载体表面,盘片在软盘驱动器的带动下做高速转动,通过磁头进行读/写操作。目前最常用的软磁盘是3.5英寸的1.44MB盘片。3.5英寸软盘置于硬塑胶盒中,没有裸露部分,盘片可以更好地得到保护。软盘的左上角有一个写保护口,口中有一个小拨块,当把小拨块移到下面,即左右两个小方孔都可以透亮时软盘就处于写保护状态。软盘写保护时,只允许进行读操作,禁止写入。

软盘片沿半径方向被划分成许多的同心圆,称为磁道,信息就记录在各个磁道上。每一磁道又划分为多个扇区,扇区的数目取决于软盘的类型。以常用的 3.5英寸双面软盘为例,每面磁道数是80,每道扇区数是18,每扇区的字节数是512,则3.5英寸双面软盘的容量为:

容量 = 面数?磁道数?每道扇区数?扇区字节数 = 2 ?80?18?512 ≈ 1.44MB

(3)光盘

光盘是近些年来迅速发展的一种辅助存储器,它对多媒体技术的广泛应用起了重要的推动作用。光盘片最早被用于存放数字音频信号,后来又被用于存储数字视频信号,近年来又成为多媒体计算机系统存放软件、文档和数据库信息的载体。从读/写类型来区分,可以把光盘分为以下三大类。

只读型光盘(CD-ROM):一张CD-ROM光盘可以存放650MB数据,主要用于电子出版物、素材库和大型软件的载体。CD-ROM驱动器的速度已从最初的单倍速(150Kb/s)发展到目前的52倍速以上。

CD-ROM、DVD小知识

CD-ROM(只读光盘存储器)作为典型的光学存储设备,是个人电脑系统中

必不可少的标准配件之一。DVD是数字视盘(Digital Video Disc)和数字万用盘

(Digital Versatile Disc)的缩写,相对于传统的CD-ROM来说,它是一种容量

更大,技术更先进的产品。由于增加了发射光头的精确度,同时提高对盘面利用

率、减少纠错码位数、修改信号调制方式以及减少每个扇区字节数等措施,DVD

光盘的容量大大增加,一般都达到了4.7GB左右的大容量,是传统CD-ROM光

盘的7倍左右。而随着半导体激光的短波化、格式效率的提高和双层盘技术的采

用,DVD容量将进一步增加到8.5GB以上,逐渐成为未来个人电脑中的主流部件。

一次写入型光盘(WORM):这种光盘给用户一次写入信息的机会,一旦写入就不能再擦除,但读出数据的次数可以是任意多次。WORM通常用于保存不允许随意修改的重要档案和历史文献。

可擦重写型光盘(E-R/W):可擦重写型光盘是光存储技术的突破性进展,它可以像硬

32盘一样任意读写数据,但读写速度要比硬盘慢。这类光盘主要用于开发系统和大型的信息系统中。

输入设备

输入设备是指给计算机输入信息的设备,是重要的人机接口,负责将输入的信息(数据和指令)转换成计算机能识别的二进制代码,送入存储器保存。微型计算机常用的输入设备是键盘、鼠标,另外还有图形扫描仪、数字化仪、条形码输入器、光笔、触摸屏、麦克风等设备。

1.键盘

键盘是实现人机交互的一种简便的输入设备,几乎是每一台微机必备的。使用者可以通过按键来直接向计算机传输数据或控制命令。常见的普通家用键盘有101/102键盘。

标准键盘可以分为主键盘区、功能键盘区、小键盘区(数字键区)和编辑键区。

主键盘区:位于键盘中间,包含字母键、数字键及一些常用的标点符号键。各种数据、字符和操作命令都是通过主键盘区输入到计算机。

小键盘区:位于键盘的右侧,又称为数字键区。这个区中的多数键具有双重功能:一是代表数字,二是代表某种编辑功能。它为专门进行数据录入的用户提供了很大方便。

功能键盘区:位于键盘的最上面一排,由 12 个功能键(F1~F12)组成。每个功能键的功能由软件系统定义。

编辑键区:这个区中的所有键主要用于文档的编辑修改。

常用的控制键有:

Tab 制表定位键,每按一次该键,光标右移8个字符。

Enter 回车键,主键盘区和副键盘区各有一个Enter键,用以确认输入的信息。

Space 空格键,是键盘上最长的一个键,每按动一次输入一个空格。

Shift 上档键,按住此键可输入双符号键的上行符号或实现字母键的大小写转换。

BackSpace 退格键,每按一次该键,可删除当前光标位置的前一个字符。

Home、End 光标快移键,分别用于将光标移到行首或行尾。

Ins或Insert 插入状态和改写状态转换键。

Del或Delete 删除键,每按一次该键,删除光标所在位置上的一个字符。

Print Screen 打印屏幕控制键,在Windows 95/98环境下,按下此键可把当前屏幕内容复制到剪贴板中。

Caps Lock 大小写字母锁定键,每按一次此键,字母键大小写转换一次。键盘上的Caps Lock灯亮时,表示处在大写字母输入状态。

PageUp、PageDown 翻页键,每按一次分别将屏幕上的内容向前或向后翻一屏。

↑,↓,→,← 光标移动键,分别用于向上、向下、向右、向左移动光标。

键盘上的控制键大多数情况下要配合使用,其功能由软件定义。例如按住Ctrl键或Alt键,再配合另外的按键,可以实现不同的操作功能。

2.鼠标

鼠标是一种手持式的坐标定位部件,用来控制屏幕上光标移动位置,并向主机输入用户所选中的某个图形位置点。鼠标按照结构可分为三种类型:机械式鼠标、光学鼠标和光

33电式鼠标。

鼠标的按键位于顶端,通常使用它的左键和右键。一旦屏幕鼠标指针定位到了所需的位置,通过按动左键或右键通知主机已选中此位置,就可以执行相应的命令或操作。鼠标的操作主要有移动、按击(单击、双击)和拖曳。

移动:在移动鼠标时,屏幕上的指针光标将做同方向的移动,并且鼠标在工作台面上的移动距离与指针光标在屏幕上的移动距离成一定的比例。

按击:按击鼠标器按钮主要用于选取指针光标所指的内容,命令计算机去做一件相应的事情。具体操作是:首先通过移动鼠标器将屏幕上的指针光标移动到所要选取的对象,如一个菜单名称、一个软件名称或某个特定的符号,然后单击或双击鼠标按钮即可选中对象。

拖曳:拖曳是按住鼠标的按钮不放开而移动鼠标,此时,被选中的对象就会随着鼠标的移动在屏幕上移动,

当移到目的地后再放开按钮。

输出设备

输出设备是输出计算机处理结果的设备。在大多数情况下,输出设备负责将计算机的处理结果转换成便于人们识别的形式。微型计算机的主要输出设备包括显示器、打印机、绘图仪,等等。

1.显示器

又称监视器,是计算机系统中最基本的输出设备。通过它用户可以很方便地查看输入计算机的程序、数据、图形等信息,以及经计算机处理后的中间结果、最后结果,它是人机交互的主要工具。

显示器根据显示器件的不同可分为阴极射线管显示器(CRT)和液晶显示材料制成的LCD显示器。其中常用的是CRT显示器,单片机和便携机一般用LCD显示器。

显示器的分辨率一般用整个显示器屏幕上光栅的列数与行数的乘积来表示。该乘积越大,分辨率就越高。目前常用的分辨率有640?480、800?600、1024?768、1280?1024,等等。

显示器必须配置正确的适配器(显卡)才能构成完整的显示系统。显卡较早的标准有CGA标准(320?200,彩色)和EGA标准(640?350,彩色),目前常用的是VGA标准。VGA适用于高分辨率的彩色显示器,其图形分辨率在640?480以上,其显示图形的效果比前两种要理想。在VGA之后,又不断出现SVGA,TVGA等显卡,分辨率提高到了800?600、1024?768甚至更高。

2.打印机

也是计算机的一个重要的输出设备,它用于将文字或图形在纸上输出,以供阅读和保存。

按其工作方式,打印机可分为击打式打印机和非击打式打印机两类。其中,点阵式打印机(针式打印机)属于击打式打印机,而目前广泛应用的喷墨打印机和激光打印机属于非击打式打印机。

喷墨打印机的价格相对激光打印机低廉,但同时也存在着打印质量和打印速度上的差距。目前家庭用微型机多配备彩色喷墨打印机。

激光打印机以其速度快、分辨率高、无击打噪声等特点,颇受用户欢迎。激光打印机

34通常由两部分组成:激光机和打印控制器。激光机技术来源于复印机,它由激光光源、旋转反射镜、聚焦透镜、感光鼓等组成。由于激光光束能聚焦成很细的光点,因此激光打印机的分辨率很高,打印质量很好。

35【微型机软件系统】

“没有软件的计算机只不过是一堆废铁”。可见,软件是组成计算机系统的重要部分。

微型计算机系统的软件分为系统软件和应用软件两大类。

1.系统软件

是指管理、监控和维护计算机资源(包括硬件和软件)的软件。最常用的系统软件有操作系统、计算机语言处理软件、数据库管理软件和各种工具软件,等等。

操作系统(Operating System):是最基本、最重要的系统软件。操作系统负责管理和协调计算机系统的各种硬件资源(包括 CPU、内存空间、磁盘空间、外部设备,等等),并负责解释用户对计算机的管理命令,使之转换为计算机的实际操作。按照所管理的用户数目,操作系统可以分为单用户操作系统和多用户操作系统。DOS(Disk Operating System)是微型计算机最常用的一种单用户单任务磁盘操作系统,它具有较完善的磁盘文件管理功能,并具有很好的开放性。Windows是美国微软公司(Microsoft)研制的具有视窗功能的单用户多任务操作系统,具有较强的图形化处理功能,可以在微机上同时运行多个任务。目前常用的Windows版本有Windows

95、Windows 98、Windows NT、Windows 2000、Windows XP等。UNIX是世界上应用最广泛的一种多用户多任务操作系统。

计算机语言处理软件:计算机语言分为机器语言、汇编语言和高级语言。机器语言是指机器能直接认识的语言,它是由“1”和“0”组成的一组代码指令。由于机器语言难记,所以基本上不能用来编写程序。汇编语言实际是由一组与机器语言指令一一对应的符号指令和简单语法组成。汇编语言软件要由一种“翻译”程序来将汇编语言翻译成机器语言程序,这种翻译程序称为汇编程序。汇编语言适于编写直接控制机器操作的底层程序,它与机器密切相关,一般人也很难使用。

高级语言是为一般程序员使用设计的、比较接近日常用语的、对机器依赖性低的、适用于各种计算机的计算机语言。常见的高级语言已有数十种,如BASIC语言、Visual BASIC语言、FORTRAN语言、C语言、Java语言。将高级语言所写的程序翻译为机器语言程序,有两种翻译方式,一种叫“编译程序”,另一种叫“解释程序”。编译程序把高级语言所写的程序作为一个整体进行处理,编译后与子程序库连接,形成一个完整的可执行程序。这种方法的缺点是编译、连接较费时,但编译后的可执行程序运行速度很快,FORTRAN 语言、C语言等都采用这种编译的方法。解释程序则对高级语言程序逐句解释执行,采用这种方法的程序设计的灵活性大,但程序运行效率较低。BASIC语言本来属于解释型语言,但现在也已发展为可以编译成高效的可执行程序,兼有两种方法的优点。Java语言则先编译为Java字节码,在网络上传送到任何一种计算机上之后,再用该机所配置的Java解释器对Java字节码进行解释执行。

2.应用软件

是指除了系统软件以外的所有软件,它是使用者利用计算机及其提供的系统软件,为解决各种实际问题而编制的计算机程序。

应用软件主要是为用户提供在各个具体领域中的辅助功能,它也是绝大多数用户学习、

36使用计算机时最感兴趣的内容。在办公自动化中,文字处理是一项重要内容,所以文字处理程序是很基本的应用软件。WPS是中国自主开发的中文文字处理程序,除了能解决中文的文字录入外,还具有较强的排版功能。Word是用于Windows下的文字处理软件,除了文字录入功能,它的“所见即所得”排版功能及较强的图文混排功能也很受用户的欢迎。

计算机软件是脑力劳动的产物。一个实用软件一般需要众多软件专业人员以及计算机应用工作者经过长期的

劳动完成。为促进计算机软件产业的发展,鼓励计算机软件的开发与流通,国务院于19xx年颁布的《计算机软件保护条例》明确规定:未经软件著作权人的同意,复制其软件的行为是侵权行为,侵权者要承担相应的民事责任。

【计算机的常用数制】

数制是人们利用符号来计数的科学方法。数制分为非进位计数制和进位计数制两种。进位计数制指的是按进位的方式计数的数制,简称进位制。其特点是:数码的数值大小与它在数中的位置有关。例如十进制数111,最右边的“1”处在个位上,它代表1;中间的“1”处在十位上,它代表10;最左边的“1”处在百位上,代表100。这三个“1”由于所处的位置不同而代表不同的数值,这就是进位计数制最大的特点。

进位计数制中,逢十进一的是十进制,逢二进一的是二进制。在计算机内部,一切信息(包括数值、字符、指令等)的存储、处理与传送均采用二进制的形式。相关的常用数制还有八进制、十六进制等。

无论是哪种进位制,都涉及两个最基本的概念:基数和权。某种进位制的基数是指在这种进位制中允许使用的基本数码的个数,也即每个数位上能使用的数码个数。例如最常用的十进制的基数是 10,因为在十进制中每一个数位上允许选用 0、l、2、3、4、5、6、7、8、9这十个数中的一个,每位计满以后就向高位进1。在前面的例子中,十进制数111,从右往左,第一个“1”在个位上,代表的数值是1?10=1;第二个“1”在十位上;代表0

的数值是1?10=10;第三个“1”在百位上,代表的数值是1?10=100。由此可见,每1 2

个数码所代表的数值等于该数码乘以一个与数码所处数位有关的常数,这个常数就叫做“位权”简称“权”。权的计算方法如下:以该进位制的基数为底,以数码所在数位的序号为指数,所得的整数次幂即为该进位制在该数位上的权。例如十进制中,从右向左数第一位是个位,权是10;第二位是十位,权是10;第三位是百位,权是10??0 1 2

【基本数据单位】

数据是一组可以识别的记号或符号,它通过各种组合来表达客观世界中的各种信息。数据是信息的载体,是信息的具体表现形式。数据可以是数字、字符、文字、声音、图像等,可以存储在物理介质上,用于传输和处理。

计算机中的数据是二进制数,常用的单位有:位、字节和字三种。

位(bit):1位二进制数,是计算机系统中数据的最小单位。

字节(Byte):8位二进制数组成一个字节,它是衡量信息数量或存储设备容量的基本单位。容量一般用KB、MB、GB、TB来表示。

37 字(Word):每一个存储单元所存放的内容称为一个字,常用来表示数据或信息的长度。

【计算机安全】

随着计算机的日益普及及其应用的不断深入,计算机的安全问题也逐渐获得了广泛的关注。计算机安全问题

涉及到计算机硬件和软件等各个方面,只有正确、安全地使用计算机才能使计算机系统正常、有效地工作。这里我们主要介绍计算机对使用环境的要求及计算机的维护。

使用环境

计算机对环境的要求主要有以下几个方面。

环境温度:计算机一般在室温10℃~30℃之间可以正常工作。温度过低会影响软盘驱动器对软盘的读写。环境温度过高,主机中的电子器件无法正常散热,也会影响计算机的运行。

环境卫生:计算机要求有洁净的工作环境。如果周围环境灰尘过多,会影响硬盘的工作寿命,所以要经常保持清洁卫生。

环境湿度:计算机运行环境的相对湿度最高不得超过80%,否则易造成元器件短路损坏机器;相对湿度不应低于20%,否则易使计算机产生静电干扰,严重的会损坏元器件。

电源:计算机的供电需要较为恒定的电压,并且频繁地断电也会对计算机造成破坏。有条件的情况下,可以装配交流稳压电源或不间断供电电源UPS。

计算机的维护

计算机的安全使用除了上面的使用环境要求外,还涉及到维护。计算机的维护主要包括开关机、软盘及硬盘等。

1.开关机

由于在开机和关机时会有较大的冲击电流,因此开机时应先开显示器再开主机,而关机时应先关主机再关显示器;不要带电拔插。也就是说,在计算机带电时,不要随意移动各种设备(标明可以热插拔的如USB设备等除外),不要拔插各种接口卡和电缆;每次开机与关机之间的时间间隔至少要10秒。

2.软盘

不要带电拔插软盘,也就是说,在计算机带电时,不要随意移动各种设备(标明可以热插拔的如USB设备等除外),不要拔插各种接口卡和电缆;磁盘驱动器的指示灯亮时,切不可拔插盘片;否则,有可能丢失磁盘上的数据,甚至损坏磁头;在关闭计算机前,务必将软盘从软盘驱动器中取出来;因为盘面磨损,软盘的寿命是有限的,因此存有重要的数据软盘应有备份,以防万一;为了防止计算机病毒的侵入,尽量避免使用外来软盘和网络上的外来软件,自己的软盘应常使用“写保护”,不使用来历不明的程序,经常使用杀毒软件检毒和杀毒,实在无法消除的病毒应使用格式化的方法彻底清除。

3.硬盘

硬盘的容量要比软盘大得多,存取的速度也快;可以存取大型文件,但是相对较小的

38重要文件也应该用软盘进行备份;硬盘驱动器的机械结构比较复杂,因此用户不能随意地打开它;读取

硬盘内容时,硬盘是高速旋转的,并且硬盘中的磁头夹在盘面上下,所以硬盘驱动器最忌震动。

【计算机病毒】

计算机病毒的概念

计算机病毒是人为制造的能够侵入计算机系统并给计算机带来故障的程序或指令集合。它通过不同的途径“潜伏”或“寄生”在存储介质(如内存、磁盘)或程序里,当满足某种条件时,它会自我复制并传播,使信息资源受到不同程度的损坏;严重时会使计算机特别是计算机网络全部瘫痪甚至无法恢复。由于这种特殊程序的活动方式与微生物学中的病毒类似,故取名为计算机病毒。

计算机病毒的特性

1.灵活性

计算机病毒都是一些可以直接运行或间接运行的程序,它们小巧灵活,一般占有很少的字节,可以隐藏在可执行程序或数据文件中,不易被人发现。

2.传染性

计算机病毒传染性是指病毒程序在计算机系统中的传播和扩散。病毒程序进入计算机系统后等待时机修改别的程序并把自身复制进去。在计算机运行过程中不断自我复制,不断感染别的程序。被感染的程序在运行时又会继续传染其他程序,于是很快就传染到整个计算机系统。在计算机网络中,病毒程序的传染速度更快,受害面也更大。计算机病毒有很强大的传染性,通过自我复制,计算机病毒可以迅速地在程序之间、计算机之间、计算机网络之间传播。

3.隐蔽性

病毒程序一般都短小精悍,技巧性相当高,极具隐蔽性,很难被发现,它通常依附于一定的媒介,不单独存在,因此,在病毒发作之前不容易被发现,而一旦发现,计算机系统往往已经被感染或受到破坏。

4.潜伏性

计算机病毒可以长时间潜伏在文件中,在触发机制被满足之前,计算机病毒可能不会表现出任何症状,只有触发了特定的条件才会进行传染或对计算机系统进行破坏。触发条件可以是一个或多个,例如某个日期、某个时间、某个事件的出现,某个文件的使用次数以及某种特定的软硬件环境等。例如CIH病毒一般在4月26日才会发作。

5.破坏性

计算机病毒可能对计算机系统产生不同程度的损害,主要表现为占用系统资源、破坏文件和数据、干扰程序运行、打乱屏幕显示甚至摧毁系统等,其破坏方式取决于病毒程序的设计者。

39 计算机病毒造成的后果,有良性和恶性之分。良性病毒只占用系统资源或干扰系统工作,并不破坏系

统数据;恶性病毒一旦发作则会破坏系统数据、覆盖或删除文件甚至造成系统瘫痪,如黑色星期五病毒、磁盘杀手病毒等。

计算机病毒的防范

计算机病毒危害很大。使用计算机系统,尤其是微型计算机系统,必须采取有效措施,防止计算机病毒的感染和发作。

1.人工预防

人工预防也称标志免疫法。因为任何一种病毒均有一定标志,将此标志固定在某一位置,然后修复宿主程序,达到免疫的目的。

2.软件预防

目前主要是使用计算机病毒的疫苗程序,这种程序能够监督系统运行,并防止某些病毒入侵。国际上推出的疫苗产品如英国的Vaccin软件,它发现磁盘及内存有变化时,就立即通知用户,由用户采取措施处理。

3.硬件预防

硬件预防主要采取两种方法:一是改变计算机系统结构;二是插入附加固件。目前主要是采用后者,即将防病毒卡的固件(简称防病毒卡)插到主机板上,当系统启动后先自动执行,从而取得微处理器的控制权。

4.管理预防

这是目前最有效的一种预防病毒的措施。目前世界各国大都采用这种方法。一般通过以下三种途径。

法律制度:规定制造计算机病毒是违法行为,对罪犯进行法律制裁。

计算机系统管理制度:有系统使用权限的规定、系统支持资料的建立和健全的规定、文件使用的规定、定期清除病毒和更新磁盘的规定等。

教育:这是一种防止计算机病毒的重要策略。通过宣传、教育,使用户了解计算机病毒的常识和危害,尊重知识产权,不随意复制软件,养成定期检查和清除病毒的习惯,杜绝制造病毒的犯罪行为。

【计算机网络】

计算机网络是计算机技术与通信技术结合的产物,网络技术对信息技术与产业的发展有着重要的影响。计算机网络最重要特点和优势就是资源共享。

计算机网络概述

1.计算机网络的组成

组成部分:主机(HOST)、节点(NODE)、通信线路、调制解调器等。主机是一个主要用于科学计算与数据处理的计算机系统;节点是在通信线路和主机之间设置的通信线路控制处理机,主要分担数据通信、数据处理的控制处理功能;通信线路主要包括连接各个

40节点的高速通信线路、电缆、双绞线或通信卫星等;在网络通信中,传送距离过大时,为防止信号的畸变,一般要将数字信号转换成便于在通信线路中传输的交流信号进行传输,在这个过程中需要完成调制与解调的功能,兼有这两项功能的装置称为调制解调器。

2.计算机网络的分类

计算机网络划分的方法有多种,主要有以下几种:按照通信距离分为局域网(LAN―Local Area Network)和广域网(WAN―Wide Area Network);按照网络拓扑结构分为环型网、星型网、总线型网等;按照通信传输的介质分为双绞线网、同轴电缆网、光纤网和卫星网等;按照信号频带占用方式分为基带网和宽带网。

下面介绍一下局域网和广域网的概念。

局域网是指一个小区域内的各种通信设备互连在一起的通信网络。这个小区域可以是一个建筑物、一个校园或者大至几十公里直径的一个区域。局域网的典型特点有:数据传输率高、距离短、误码率低。

广域网的服务地区不局限于某一个地区,它可以是省与省之间,全国乃至全球范围。局域网要想实现远程通信,可以将局域网连接到公共远程通信设备上,例如电报电话网、微波通信站或卫星通信站等,在这种情况下,局域网必须是开放式的,并具有与这些公共通信设备的接口。

3.计算机网络的功能及应用

计算机网络的功能非常多,主要体现在:资源的共享、信息传输与集中处理、均衡负荷与分布式处理、综合信息服务。除了上面的功能外,计算机网络还有以下方面的应用:远程登录、传送电子邮件、电子数据交换、联机会议等。

网络协议与传输介质

1.网络协议

计算机网络中实现通信必须有一些约定,对速率、传输代码、传输控制步骤、出错控制等指定相关的标准,这些标准便是网络协议。

这里我们简单介绍一下TCP/IP协议。

TCP/IP(Transmission Control Protocol /Internet Protocol)即传输控制协议,它是事实上的Internet国际标准协议,要求网络上的计算机均采用TCP/IP协议,UNIX操作系统已把TCP/IP作为它的核心组成部分。

TCP/IP应用层提供若干种服务:

简单远程终端协议Telnet,使用户可以把本地机器变成远程机器的一个仿真终端;

网际文件传送协议FTP,FTP授权用户登录到远程系统中以识别自己,列出远程系统上的目录及文件,可以对远程机器上传或者下载文件;

简单的邮件传送协议SMTP,保证网上两个用户之间可以相互传送邮件。

2.网络传输介质

传输介质主要有以下几种:双绞线、同轴电缆、光纤、无线通信等。其中光纤的使用越来越广泛,因为其带宽大,传输速率快,并且不受外界电磁场的影响。但价格比较昂贵,

41这是它不能马上普及的主要原因。

局域网

局域网在现代办公系统中具有广泛的应用。在局域网中可以共享文件,计算机之间可以相互协同工作,还可以共享磁盘、打印机等资源,这样可以提高办公效率,也大大地降低了资源的重复购置及相关的费用。

通常,局域网包括网络硬件和网络软件两大部分。可以分成传输介质、网络工作站、网络服务器、网卡、网间连接器、网络系统软件等6个基本组成部分。

在计算机网络中,要把工作站、服务器等设备连入一个网络中,需要在设备上插入一块网络接口卡,这就是网卡。网卡是微机与网络相连的必备网络设备。

网间连接器允许两个局域网相连,以形成更大规模、更高性能的网络。常用的网间连接设备有:中继器(repeater)、网桥(bridge)、网关(gateway)。

传输介质、网卡、网间连接器及Modem等设备都属于网络设备。

网络系统软件主要由服务器平台(服务器操作系统)、网络服务软件、工作站重定向软件、传输协议软件组成。目前局域网中最常用的是Novell网,其操作系统为Netware。

Internet基础

传统的Internet应用主要有四类:E-mail、Telnet、FTP和Usernet。从20世纪90年代开始,Internet 获得了广泛的应用,在很大程度上是因为一种新的应用——WWW(WorldWide Web),即万维网的出现和发展,可以说WWW改变了Internet的应用方式。

1.电子邮件

电子邮件又称为E-mail(Electronic Mail),它是Internet的主要应用之一。E-mail为Internet上的用户之间发送和接收消息提供了一种快捷、廉价的现代化通信手段,在电子商务及国际交流中发挥着重要的作用。电子邮件系统不但可以传输各种文字与格式的文本信息,而且还可以传输图像、声音、视频等多种信息。

如果要使用电子邮件服务,首先要拥有一个电子邮箱(Mail Box)。电子邮箱是由提供电子邮件服务的机构为用户建立的。现在有很多免费的邮箱服务网站。一个电子邮件账户,它包括用户名(User Name)与用户名密码(Password)。任何人都可以将电子邮件发送到某个电子邮箱中,但只有电子邮箱的拥有者输入正确的用户名和用户密码,才能查看电子邮件内容或处理电子邮件。

每个电子邮箱都有一个邮箱地址,称为电子邮件地址(E-mail Address)。电子邮件地址的格式是固定的,并且在全球范围内是惟一的。用户的电子邮件地址格式为:用户名@主机名,其中“@”符号的意义是“at”(通常也读作at)。主机名指的是拥有独立IP地址的计算机的名字,用户名是指在该计算机上为用户建立的邮件的账号。假如有这样一个E-mail地址:foxhua@hotmail.com,表示在“hotmail.com”主机上面有一个名为“foxhua”的用户;又如foxhua@sohu.com等。

在电子邮件程序向邮件服务器中发送邮件时,使用的是简单邮件传输协议(SMTP——Simple Mail Transfer Protocol);而在电子邮件程序从邮件服务器中读取邮件时,使用邮局协议(POP3——Post Office Protocol)或交互式邮件存取协议(IMAP——Interactive Mail

42Access Protocol),这取决于服务器支持的协议类型。邮件常用收发软件有Windows系统自带的Outlook Express和国人做的Foxmail等。

2.文件传输(FTP)

FTP(File Transfer Protocol)——文件传输协议,是用于访问远程计算机的文件目录并与之交换文件的一种协议。用文件传输方式可直接进行文字和非文字信息的双向传输,非文字信息包括计算机程序、图像、照片、音乐、录像等。

Internet上有很多FTP资源,但是因为FTP没有统一的标准,除了一些主要命令外,各种不同平台上的FTP可能会有所差别。

3.远程登录(Telnet)

Telnet服务用于在网络环境下实现资源的共享。利用Telnet,用户可以把一台计算机变成另一台计算机(主机)的远程终端,从而使外部用户可以使用该主机的系统资源。它采用Telnet协议,可使多台计算机共同完成一个较大的任务。

WWW服务

WWW(World Wide Web)——万维网,简称“3W”。它是目前Internet上最方便和最受欢迎的信息服务类型之一。

1.WWW是以超文本标注语言(HTML——Hyper Text Markup Language)与超文本传输协议(HTTP——Hyper Text Transfer Protocol)为基础,能够提供面向Internet服务的、一致的用户界面。

超文本(hypertext)与超媒体(hypermedia)是WWW的信息组织形式,也是WWW实现的关键技术之一。WWW的目的是帮助用户在Internet上以统一的方式去获取不同地点、不同存取方式、不同检索方式、不同表达形式的信息资源。从本质上讲,它是超媒体思想在网络上的实现,WWW支持跨计算机的信息连接。

2.统一资源定位器(URL)

在Internet中有很多WWW服务器,而每台服务器中又包含很多的HTML(或其他格式的)网页,如何定位特定的网页呢?这需要使用统一资源定位器(URL——UniformResource Locator),即通常所说的网址。

标准的URL由三部分组成:服务器类型、主机名和路径及文件名。例如:

/index.html

协议类型 主机名 文件名与路径

其中,“http:”指出要使用HTTP协议,指出要访问的服务器的主机名,“index.html”指出要访问的主页的路径和文件名。

这样,通过URL,用户可以指定要访问什么服务器、哪台服务器、服务器中的哪个文件。也就是说,如果用户希望访问某台WWW服务器中的某个页面,只要在浏览器中输入该页面的URL,便可以浏览到该页面了。

3.WWW浏览器(Browser)

43 WWW浏览器,又称Web浏览器,是用来浏览Internet上的主页的客户端软件。WWW浏览器的种类很多,主要有Internet Explorer与Netscape两种。WWW浏览器为用户提供了寻找 Internet 上内容丰富、形式多样的信息资源的便捷条件。现在最常用的浏览器是Windows自带的Internet Explorer(简称IE),其框架如图2-3所示。

图2-3 Internet Explorer框架

WWW浏览器功能强大,利用它可以访问Internet上的各类信息。更重要的是,目前的浏览器基本上都支持多媒体特性,可以通过浏览器来播放声音、动画和视频等。

曾经辉煌一时的浏览器Netscape已经慢慢被Internet Explorer所吞噬,但是它在自由软件Linux操作系统中的应用非常广泛。

4.主页

在WWW环境中,信息以信息页形式来显示与链接。信息页是由HTML语言来实现的(也有采用XML语言的),并在信息页间建立了超文本链接以便于浏览。

主页(Home Page)是指个人或机构的基本信息页面,用户通过主页可以访问有关的信息资源。主页一般包含以下几种基本元素。

文本(Text):最基本的元素,就是通常所说的文字。

图像(Image):WWW浏览器一般只识别GIF和JPEG两种图像格式。

表格(Table):类似Excel中的表格,表格单元内容一般为字符类型。

超链接:(HyperLink):是HTML的重要元素,用于将HTML元素与其他主页相连。

主页是人们通过Internet了解一个公司、政府部门及相关网站的重要手段。人们可以使用主页介绍自己的公司,可以在上面展示自己公司的最新产品,达到广告效应,这样的方式廉价又方便,WWW在商业中的重要作用就体现在这里,当然它还有很多其他的用途,比如发布一些免费软件等。

5.搜索引擎

WWW中拥有数以千万计的WWW服务器,而且WWW服务器所提供的信息种类及所覆盖的领域也极为丰富。如果要求用户了解每台WWW服务器的主机名,以及它所提供的资源种类,那是不可能的。那么,用户要在大量的网站中快速、有效地查找信息,就需要借助于Internet中的搜索引擎。

44 搜索引擎主要任务是在Internet中主动搜索WWW服务器中的信息并对其自动索引,将索引表存储在可供查询的大型数据库中。用户可以利用搜索引擎所提供的分类目录和查询功能查找所需的信息。

用户在使用搜索引擎之前必须知道搜索引擎站点的主机名,通过该主机名用户便可以访问到搜索引擎站点的主页。使用搜索引擎,用户只需知道自己要查找什么,或要查找的信息属于哪一类。当用户将自己要查找信息的关键字告诉搜索引擎后,搜索引擎会返回给用户包含该关键字信息的URL,并提供通向该站点的链接,用户通过这些链接便可以获取所需的信息。(这里只介绍最简单的关键字搜索)

【多媒体技术】

媒体

在计算机领域中,媒体主要包括以下几种。

1.感觉媒体

它实际上是信息的自然表示形式,它们直接作用于人的感官,使人能直接产生感觉。例如,人类的各种语言、音乐,自然界的各种声音、图形、图像,计算机系统中的文件、数据和文字等。

2.表示媒体

是指信息在计算机中的表示,通常是信息的各种编码。例如,字符的ASCII码与汉字的编码都属于表示媒体。再如,语音编码、图像编码等也都是为了加工、处理和传输感觉媒体而人为地进行研究、构造出来的一类媒体。

3.表现媒体

是指感觉媒体与计算机之间的界面。信息需要用计算机来处理,计算机处理的结果还需要输出,因此,表现媒体实际上是用于输入与输出信息的设备,如键盘、摄像机、光笔、话筒、显示器、喇叭、打印机等。

4.存储媒体

用于存放表示媒体,即存放感觉媒体数字化后的代码。存储媒体实际上是存储信息的实体,常见的存储媒体主要有磁带、磁盘和CD-ROM等。

5.传输媒体

实际上是传输介质,它是将媒体从一处传送到另一处的物理载体,如双绞线、同轴电缆、光纤等。

多媒体技术的基本特征

我们现在所说的多媒体,常常不是说多媒体信息本身,而主要是指处理和应用多媒体的综合技术,利用这种技术实现声音、图形、图像等多种媒体的集成应用,使它们建立起逻辑联系,并能进行加工处理的技术。这里所说的“加工处理”主要是指对这些媒体的录入、存储、显示、传输、压缩和解压缩等。

多媒体技术的基本特征包括以下几点。

45 1.综合性

多媒体技术的综合性是指将计算机、声像、通信技术合为一体,是计算机、电视机、录像机、录音机、音响、游戏机、传真机等性能的大综合,将多种媒体有机地组织在一起,共同表达一个完整的多媒体信息,使声、文、图、像一体化。

例如,通过一张光盘可以看到唐诗、宋词、红楼梦等名著的全部文字,还配有赏心悦目的背景音乐和画面,伴随着悦耳的朗读,时而还配合有人物活动的动画,甚至还可以插入一段影视片断,使人能通过多种感官获取信息。

2.交互性

交互性是指人和计算机能“对话”,以便进行人工干预控制。交互性是多媒体技术的关键特征。

例如,在上述光盘中,用户可以自选字体、颜色、阅读速度、是否要配音乐等;还可以自设“书签”,以便于进行前翻后找;根据作者、年代、书名等进行检索,从而可以快速找到所需要的文章等。

电视机虽然也具有视听功能,但它没有交互性,使用者只能被动地接受屏幕上传来的信息。例如,在看电视球赛时,观众只能听着解说员的解说,看着摄影师为观众拍摄的画面,别无选择。作为一个好的多媒体节目,应该结合有互动设计,可以让用户选择听解说员的解说或专家的评论,可以选择从不同的角度观赛,当对某个球员发生兴趣时,不用被动地等待解说员的解说,可以轻松地调出该球员的个人简介等,这样就使看节目的人有更自由的选择权和更多角度的观察点。因此,电视机不能称为多媒体设备。一般来说,多媒体除了具有视听功能外,还应具备交互性。

3.数字化

数字化是指多媒体中的各种媒体都是以数字形式存放在计算机中。

4.实时性

多媒体技术是多种媒体集成的技术,在这些媒体中,有些媒体(如声音和图像)是与时间密切相关的,这就决定了多媒体技术必须要支持实时处理。

多媒体技术是基于计算机技术的综合技术,它包括数字信号处理技术,音频和视频技术,计算机硬件和软件技术,人工智能和模式识别技术,通信和图像技术等。它是正处于发展过程中的一门跨学科的综合性高新技术。

多媒体技术的应用

多媒体技术的应用主要体现在以下几个方面。

1.教育与培训

多媒体技术为教育增加了一种新的手段。多媒体技术可以将课文、图表、声音、动画、影片和录像等组合在一起构成教育产品,这种图、文、声、像并茂的场景将大大提高学生的学习兴趣和接受能力,并且可以方便地进行交互式的指导和因材施教。

用于军事、体育、医学、驾驶等各方面培训的多媒体计算机,不仅可以使受训者在生动直观、逼真的场景中完成训练过程,而且能够设置各种复杂环境,提高受训人员对困难和突发事件的应变能力,并能自动评测学员的学习成绩。

46 2.商业领域

多媒体技术在商业领域中的应用也是十分广泛的,例如,多媒体技术用于商品广告、商品展示、商业演讲等方面,使人们有一种身临其境的感觉。

3.信息领域

利用 CD-ROM 大容量的存储空间,与多媒体声像功能结合,可以提供大量的信息产品,如百科全书、地图系统、旅游指南等电子工具,还有如电子出版物、多媒体电子邮件、多媒体会议、电脑购物等都是多媒体在信息领域中的应用。

4.娱乐与服务

多媒体技术用于计算机后,使声音、图像、文字融于一体,用计算机既能听音乐,又能看影视节目,使家庭文化生活进入一个更加美妙的境地。多媒体计算机还可以为家庭提供全方位的服务,如家庭教师、家庭医生、家庭商场等。

多媒体基本元素

多媒体的元素种类很多,主要包括以下几种。

1.文本(text)

文本主要是指汉字、英语等各种文字。文本的特性可以有字体(如汉字宋体、隶书、楷体等,英语Times New Roman字体等)、字号(如10号字、12号字等)和格式(如黑体、斜体等)等。

2.图形(graphic)

图形是指由点、线、面组成的二维和三维图形。图形可以是黑白的或彩色的。

3.静止的图像(still image)

静止的图像是指书上或其他印刷品上的图片、幻灯片和绘画作品等。照片也属于静止的图像。

4.动画(animation)

动画包括卡通、活页动画和连环图画等。

5.影片(video)

6.音响效果(sound)

包括各种各样的音响效果,如动物的叫声、雷电的声音、东西碰撞的声音等。

7.音乐(music)

包括各种歌曲、乐曲等。

8.交互问答(interaction)

包括对话、问答、按钮、指示等。

多媒体计算机系统的基本组成

所谓多媒体计算机系统是指能综合处理多媒体信息,使多种信息建立联系,并具有交互性的计算机系统。

多媒体计算机系统一般由多媒体计算机硬件系统和多媒体计算机软件系统组成。

47 1.多媒体计算机硬件系统

多媒体计算机硬件系统主要包括以下几部分。

多媒体主机:如PC、工作站、超级微机等。

多媒体输入设备:如摄像机、麦克风、录像机、录音机、扫描仪、CD-ROM等。

多媒体输出设备:如打印机、绘图仪、音响、电视机、喇叭、录音机、录像机、高分辨率屏幕等。

多媒体存储设备:如硬盘、光盘、声像磁带等。

多媒体功能卡:如视频卡、声音卡、压缩卡、家电控制卡、通信卡等。

操纵控制设备:如鼠标器、操纵杆、键盘、触摸屏等。

2.多媒体计算机软件系统

多媒体计算机的软件系统是以操作系统为基础的。除此之外,还有多媒体数据库管理系统、多媒体压缩/解压缩软件、多媒体声像同步软件、多媒体通信软件等。特别需要指出的是,多媒体系统在不同领域中的应用需要有多种开发工具,而多媒体开发和创作工具为多媒体系统提供了方便直观的创作途径,一些多媒体开发软件包提供了图形、色彩板、声音、动画、图像及各种媒体文件的转换与编辑手段。

多媒体主要硬件设备

为了构成一个多媒体计算机系统,在绝大多数情况下,可以从实际出发,以通用的微型计算机为基础,适当增加升级部件后扩充成一台多媒体计算机。常用的升级部件有以下几个。

1.CD-ROM

CD-ROM一般是指小型只读光盘存储器。通常,CD-ROM这个词既可以代表CD-ROM光盘,也可以代表CD-ROM驱动器,还可以是CD-ROM光盘和CD-ROM驱动器的总称。

CD-ROM光盘片的存储容量很大,一片CD-ROM光盘可以存储650MB数据。

光盘驱动器一般是指CD-ROM驱动器,它与磁盘驱动器一样,是用于读取CD-ROM光盘上信息的装置。CD-ROM 驱动器的主要性能指标是数据的传输率,其中单倍速CD-ROM驱动器的数据传输率为150Kb/s。目前常用CD-ROM驱动器一般都是在32倍速以上,其数据传输率为32?150Kb/s。

2.声卡

声卡也称音频卡。声卡可以将模拟波形的声音转换成数字声音;还可以将经计算机处理后的数字声音转换成模拟声音,最后输出到音响设备。

要让一台普通的计算机能够录制和播放声音,就需要给计算机插上一块声卡。声卡的采样频率范围一般在5kHz~44.1kHz之间,采用8位或16位采样,单声道或双声道(立体声)录放。

高质量的音乐文件要占据很大的硬盘空间,因此,理想的声卡应能对声音信号进行压缩。ADPCM压缩标准可将采样文件压缩至原来大小的四分之一,MPEG标准可压缩至十二分之一。

48 MIDI音乐在各种游戏和多媒体光盘上很常见。市场上有的声卡采用FM合成技术来演奏MIDI,由于这是一种模拟乐器的方法,因此,音色与真实乐器有所不同。但如果采用波表(wavetable)合成技术实现MIDI合成,由于它使用存储在声卡ROM中的真实乐器的数字化录音,所以能够产生更饱满、更逼真的音效。

3.音箱

音箱是多媒体系统的重要组成部分,无论声卡多么好,使用劣质音箱的话,放出来的声音也会令人失望。有些多媒体计算机把音箱内置于显示器或系统主机箱内,这样极大地减少了用户安装的麻烦,同时,也减少了机箱外杂乱的电缆线。但要收听高品质的音乐时,这种音箱不一定能满足用户的要求。

人的耳朵只能听到频率在20Hz~20000Hz之间的声音,所以,一般情况下,一种音响系统能否把这个范围的声音完整地重现,是衡量这个音响系统质量高低的标志。一般来说,一个音箱如果具有60Hz~2000Hz的频响范围,就完全能够达到Hi-Fi级别(篇幅有限,关于Hi-Fi级别的详细介绍请参阅其他书籍),经它播放的声音层次清晰、动感强烈、音效逼真。

从理论上说,音箱的大小与音质没有根本的关系,世界上知名度很高的英国ROGERS3/5A音箱,体积较小,但很多录音公司和电台都用它作为监听音箱。小型音箱定位准确、分析力强、大型音箱声场宏大,气势辉煌,可以说各具特色。购买时,用户可根据自己的意愿来挑选。

最好选用具有防磁功能的音箱,这样,在音箱工作时,就不会对显示器和电视产生电磁干扰,或者破坏软盘或硬盘上的数据。如果是防磁音箱,那么,把它们和计算机的显示器或电视机摆放在一起也没有关系,但如果不是防磁音箱,那么最好离开显示器或电视机50cm左右。

音箱有无源音箱和有源音箱两种。所谓有源音箱就是在音箱内装有功率放大器的音箱。那么,无源音箱就是在音箱内未装功率放大器的音箱。一般来说,功率大的音箱都是有源音箱。一般的声卡只提供前级放大器,所以,要获得比较大的音量或更好的音质,应该选用有源音箱。

4.视频卡

多媒体视频卡主要以视频芯片为核心,提供视频加速、视频播放及视频捕捉等功能。

从所处理图像资料的类型考虑,多媒体视频卡可以分为绘图和视频两类。其中,绘图视频卡是具有绘图功能的多媒体视频卡,而另一类仅处理视频数据。绘图视频卡主要面向绘图方面的专业市场,三维图像处理功能较强。

普通视频卡在家庭中更受欢迎。这一类视频卡按功能又可以分为视频捕捉卡(VideoCapture Adaptor)、视频播放卡(Video Broadcasting Adaptor)、视频播放/捕捉卡及视频转换卡等。捕捉功能主要是用来搭配视频编辑软件使用的。通常在市场上见到的MPEG卡(又称为解压缩卡)是视频播放卡。

多媒体出版物由于采用了大量的图像、声音,数据量比传统的以文字为主的出版物要大数百倍,所以数据的压缩及还原成了多媒体发展的一项关键技术。

MPEG可以通过软件来实现,也可以通过硬件来实现。目前软件MPEG多应用于绘图

49视频播放卡,在主频较高的机器上每秒可以播放20~25张各种MPEG视频节目,这样的视频质量已可以为一般消费者所接受,它在价格上比硬件MPEG卡有优势。软件MPEG技术的缺点是:

(1)MPEG软件占用了CPU大部分的时间。

(2)如果在主频较低的PC机上运行,则无法达到用户可以接受的视频质量。

(3)无法执行交互式OMI的标准。

但从大部分用户使用MPEG播放功能的情况来看,大多数用户在观看MPEG节目时,一般不会同时进行其他操作,也就是说,此时能否实现多任务功能是无关紧要的。所以,有人预测,在主频较高的PC上将会以软件MPEG为主,而对于那些CPU速度较低的用户来说,硬件MPEG卡仍为首选。

一个优秀的MPEG卡应具备以下一些功能:

(1)对不同的文件格式、软件平台以及硬件都具有兼容性,目前的MPEG卡一般都具备播放VCD、CD和卡拉OK CD的功能,可以实时解压缩以MPEG方式压缩的、后缀为.MPG的、存储在影盘或光盘上的影音文件。

(2)交互式和视频叠加功能,视频叠加功能可满足用户在屏幕的任何位置、任意大小,以真彩色模式实现动态图像的播放。在播放过程中,用户仍可用计算机进行其他工作。

(3)具有良好的操作界面和视窗。

5.调制解调器

用户为了实现网络功能,还需要安装调制解调器。

目前,很多厂家已把调制解调器的功能与声卡的功能结合在一起,这样就给用户减少了一些不必要的麻烦。

补充与扩展

【常用数制之间的转换】

二进制

因为二进制中只有“0”和“1”两种数码,而在计算机中可以很容易地利用电子元件的饱和、截止两种稳定状态,即高电平和低电平来表示一个数位上的0数码和1数码,因此,计算机存储和计算都是用二进制数。二进制数n可以写成(n)B或(n)2,它的基本特点有以下几点。

(1)只有两个数码:0和1。

(2)逢二进一,例如 1加 1的结果是10。

(3)任意一个n位整数部分,m位小数部分的二进制数B可以表示成:

B=B ?2n-1+B ?2n-2+?+B?2+B?2+B1 0 ?2-1+B ?2-2+?+ B ?2-m

n-1 n-2 1 0 -1 -2 -m

(其中B是数码,取值范围是0或1,2是基数,2是权)i

i

50 上式称为二进制的“权展开式”。

二进制数转换为十进制数

二进制到十进制的转换十分简单,只需把一个二进制数按上述权展开式写成多项式和的形式,算出多项式的结果即可。即将各数位的权和该位上的数码相乘,乘积相加,和就是该二进制数对应的十进制数。例如:

(110101.001)=1?2+1?2+0?2+1?2+0?2+1?2+1?25 4 3 2 1 0 -3

2

=32+16+0+4+0+1+0.125

=53.125

二进制和十进制的转换是等级考试中的一个难点,用公式表达可能不太好理解,其实二进制数向十进制的转换十分简单,可以从右向左进行。

第一位上的“1”就表示1,第二位上的“1”表示2,第三位上的“1”表示4,第四位上的“1”表示8,第五位上的“1”就表示16??以此类推,把所有有“1”的位上表示的数全部加起来就得到答案了。如下,将二进制数11001011转换为十进制数:

上面的二进制数11001011就转换为:

128+64+8+2+1=203

十进制数转换为二进制数

整数部分采用“除2取余”的方法,把十进制数的整数部分除以2,取余数作最低位系数k0;再取商继续除以2,取余数作为高一位的系数;如此继续直到商为0时得到最高位系数,停止运算。依次所得到的余数序列就是转换成的二进制数的整数部分;小数部分转换遵循“乘2取整法”,将十进制数的小数部分乘以2,结果的整数部分是0则取“0”,是1即取“1”,再将结果的小数部分继续乘以2,取其整数部分,依次进行,最后将这些取到的0和1依序连起来作为二进制数的小数部分。

例如:把十进制数100.375转换成二进制数。

转换过程如下所示。

整数部分100的转换如下:

2 100 余数 系数k

i

2 50 0 k 最低位

2 25 0 k1

2 12 1 k2

2 6 0 k3

2 3 0 k4

2 1 1 k5

0 1 k 最高位

6

51 整数部分的转换结果是:(100)10=(1100100)2

又如将55转换为二进制数,连作下面的除法:

55÷2=27????余1

27÷2=13????余1

13÷2= 6????余1

6 ÷2= 3????余0

3 ÷2= 1????余1

1 ÷2= 0????余1

把余数从下往上连起来,即110111就是最后的答案。

小数部分0.375的转换如下:

0.375

? 2

0?????0.750

0.75

? 2

1?????1.50

0.5

? 2

1?????1.0

小数部分的转换结果是:(0.375)10=(0.011)2

所以,总的换算结果就是:(100.375)10=(1100100.011)2

十六进制数转换为十进制数

十六进制数中有16个数字符号0~9以及A、B、C、D、E、F,其特点是“逢十六进一”。其中A、B、C、D、E、F分别代表十进制数10、11、12、13、14、15。与十进制记数一样,在十六进制数中,每一个数字符号(0~9以及A、B、C、D、E、F)在不同的位置上有不同的值,各位上的权值是基数16的若干次幂。例如:

(13F.D8) =1?16+3?16+15?16+13?162 1 0 -1+8?16-2

=319.84375

十进制数转换为十六进制数

十进制整数转换成十六进制整数采用“除16取余法”。具体作法为:将十进制数除以16,得到一个商数和一个余数;再将商数除以16,又得到一个商数和一个余数;继续这个过程,直到商数等于0为止。每次得到的余数(必定是0~9或A~F之一)就是对应十六进制数的各位数字。但必须注意:第一次得到的余数为十六进制数的最低位,最后一次得到的余数为十六进制的最高位。

52 例如,将十进制数1000转换成十六进制数,其过程如下所示。

16 1000 余数 系数ki

16 62 8 k0 最低位

16 3 14(E) k1

0 3 K2 最高位

其结果如下:

(1000)10=( k2 k1 k0)16=(3E8)16

二进制数转换为十六进制数

前面已经介绍了计算机常用数制以及它们与十进制数之间的转换。

二进制与十六进制之间有着简单的关系,它们之间的转换是很方便的。由于16是2的整数次幂,即16=2,因此,4位二进制数相当于1位十六进制数。4

二进制数转换成十六进制数的规律是:从整数的最后一位开始,向前每4位一组构成1位十六进制数。

例如,二进制数(10111001011)2转换成十六进制数为:

101 1100 1011

3 C B

即:(10111001011)2=(3CB)16

十六进制数转换为二进制数

十六进制数转换成二进制数的规律是:每位十六进制数用相应的4位二进制数代替。

例如,十六进制数(8FD1)16转换为二进制数为:

8 F D 1

1000 1111 1101 0001

即:(8FD1)16=(10xxxxxxxxxxxx1)2

表2-1列出了十进制以及计算机常用记数制的基数、位权和所用的数字符号。

表2-1 计算机常用记数制的基数、位权及数字符号

十进制 二进制 八进制 十六进制基数 10 2 8 16

位权 10n 2n 8n 16n

数字符号 0~9 0,1 0~7 0~9和A~F注:表中n为小数点后的位序号

表2-2列出了十进制以及计算机常用记数制的表示法。

53 表2-2 计算机常用记数制的表示

十进制 二进制 八进制 十六进制0 0 0 0

1 1 1 1

1 10 2 2

3 11 3 3

4 100 4 4

5 101 5 5

6 110 6 6

7 111 7 7

8 1000 10 8

9 1001 11 9

10 1010 12 A

11 1011 13 B

12 1100 14 C

13 1101 15 D

14 1110 16 E

15 1111 17 F

16 10000 20 10

【字符编码】

计算机除了用于数值计算外,还有其他许多方面的应用。因此,计算机处理的不只是一些数值,还要处理大量符号,如英文字母、汉字等非数值的信息。例如,当用户使用计算机编写文章时,就需要将文章中的各种符号、英文字母、汉字等输入计算机,然后由计算机进行编辑、排版。因此,计算机要对各种字符进行处理。通常,计算机中的数据可以分为数值型数据与非数值型数据。其中数值型数据就是常说的“数”(整数、实数等),它们在计算机中是以二进制形式存放的;而非数值型数据与一般的“数”不同,通常它们不表示数值的大小,而只表示字符或图形等信息,但这些信息在计算机中也是二进制形式来表示的。

目前,国际上通用的、且使用最广泛的字符有:十进制数字符号0~9,大小写的英文字母,各种运算符、标点符号等,这些字符的个数不超过128个。为了便于计算机识别与处理,这些字符在计算机中是以二进制形式来表示的,通常称之为二进制编码。

由于需要编码的字符不超过128个,因此,用7位二进制数就可以对这些字符进行编码。但为了方便,字符的二进制编码一般占8个二进制位,它正好占计算机存储器的一个字节。

具体的编码方法,即确定每一个字符的7位二进制代码,是人为规定的。但目前国际上通用的是美国标准信息交换码(American Standard Code for Information Interchange),简

54称ASCII码(取英文单词的第一个字母的组合)。用ASCII码表示的字符称为ASCII码字符,详情请见书后附录。

特别需要指出的是,十进制数字字符的ASCII码与它们的二进制值是有区别的。例如,十进制数 3 的7 位二进制数表示为(0000011),而十进制数字字符“3”的 ASCII 码为(0110011)2=(33)16=(51)10。由此可以看出,数值3与数字字符“3”在计算机中的表示是不一样的。数值3能表示数的大小,并可以参与数值运算:而数字字符“3”只是一个符号,它不能参与数值运算。

【计算机病毒的分类】

按危害程度分类

根据计算机病毒的危害程度可以将计算机病毒分为良性病毒和恶性病毒两类。

1.良性病毒

这一类病毒属于恶作剧性质,一般不会破坏计算机系统。例如,屏幕上有时出现莫名其妙的消息、画面等,有时计算机会突然发出奇怪的声音等。这种病毒一般只会降低计算机系统的工作效率,出现短暂性的故障干扰用户的工作,而不会破坏系统和更改磁盘上的文件,例如小球病毒。

2.恶性病毒

这一类病毒再也不是自我表现的恶作剧了,而是为了破坏计算机系统而设计的。这种病毒的危害很大,其破坏性无法估量,可能造成一部分文件更改或丢失,可能损坏计算机中存储的所有信息,甚至可能破坏计算机系统的操作系统或硬件系统,造成整个系统的瘫痪,后果不堪设想,例如CIH病毒。

按感染原理分类

如果根据计算机病毒感染系统的原理分类,则可以分为以下三类。

1.引导扇区病毒

引导扇区(Boot sector)是硬盘的一部分,当开机时,它控制系统如何启动。引导扇区病毒用它自己的数据代替硬盘的原始引导扇区的数据,一旦开机,就将病毒装入内存。病毒进入内存后,就可以传播给其他磁盘并进行破坏。

2.文件感染病毒

文件感染病毒将病毒代码加到可运行程序的文件中,因此这种病毒在运行程序时被激活,传染扩散。被这类病毒感染的可执行文件的扩展名一般为COM、EXE和OVL。一般文件感染病毒会修改它所寄生的程序的第一条执行指令,使得病毒程序先于该程序运行,进行传染扩散。

3.复合型病毒

这类病毒兼有以上两类病毒的特点,既可以感染磁盘的引导区,又可以感染文件。

55 按入侵途径分类

如果根据病毒入侵软件系统的途径,一般可以分为以下四种类型。

1.入侵型。入侵型病毒插进现有程序,成为合法程序的一部分,破坏源程序。

2.操作系统型。系统引导时,这种病毒就随着引导程序装入内存,获得系统控制权,并传播给其他磁盘并进行破坏。

3.源码型。在高级语言和汇编语言编写的源程序编译前,这种病毒就插入到源程序中,编译后成为可执行目标程序的一部分,危害很大。

4.外壳型。这种病毒存在于合法程序的周围,当合法程序运行时,病毒就被激活。【计算机病毒的危害】

病毒危害计算机时一般会出现下列现象:

(1)屏幕上出现不正常的雪花点、闪烁、奇怪的画面和提示语等。

(2)系统启动时间过长。

(3)执行程序的时间比平时长。

(4)对磁盘访问时间比平时长。

(5)程序或数据莫名其妙地丢失。

(6)磁盘的卷标名、文件建立日期、时间和长度发生了变化。

(7)内存空间变小,程序运行不正常,或得出不合理的结果。

(8)喇叭出现异常声响,发出蜂鸣声、尖叫、长鸣等。

(9)死机现象增多,或突然死机。

(10)非法加密或解密用户文件。

(11)外部设备无法正常使用,等等。

【计算机病毒的具体防范措施】

对计算机用户来说,对待计算机病毒应采取预防为主原则。建议采取的措施有:

(1)对重要的系统软件和文件数据定时备份。

(2)避免使用来历不明的软件,自己的软盘应常使用“写保护”。

(3)不要将用户数据和程序写到系统盘上,重点保护系统盘。

(4)定期使用杀毒软件对计算机系统进行检测,定期对杀毒软件升级,一旦发现异常现象,及时检测并清除病毒。

但是,反病毒技术始终是对计算机病毒的“跟踪”,即总是先有病毒,再有反病毒的软件,所以,对于计算机病毒还是应以预防为主。

注意,计算机病毒是新大纲经过调整以后重点突出的一个部分,要了解它的计算机病毒的概念、基本特性,以及可以造成的危害;对于病毒防治,重点要对病毒发作的症状、传播的途径、预防的措施有所了解。

56 典 型 例 题

【例题2-1 中央处理器】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

计算机中运算器的作用是( )。

A)控制数据的输入/输出

B)控制主存与辅存间的数据交换

C)完成各种算术运算和逻辑运算

D)协调和指挥整个计算机系统的操作

解题方法

读者可以参照前面的知识点讲析便可轻松解答此题。

知识点分析

运算器,又称算术逻辑单元(ALU)。其功能包括算术运算和逻辑运算。

答案

C

难度提示

简单。本题涉及的是计算机系统的组成及其组成部分功能的理解。

【例题2-2 计算机软件系统】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

计算机软件系统包括( )。

A)操作系统、网络软件

B)系统软件、应用软件

C)客户端应用软件、服务器端系统软件

D)操作系统、应用软件和网络软件

解题方法

掌握计算机系统(包括计算机硬件和计算机软件)的相关知识之后,这道题便可以轻松解决。计算机系统知识是基础,请读者牢牢掌握。

知识点分析

57 计算机系统的软件分为系统软件和应用软件两大类。系统软件和应用软件又包括许多其他的软件,请读者参看前面的介绍。

答案

B

难度提示

简单。本题只涉及相关概念记忆。

【例题2-3 计算机系统的技术指标】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

在计算机中,一个字长的二进制位数是( )。

A)8 B)16 C)32 D)随CPU的型号而定

解题方法

理解相关概念,了解并区分字长、字节、位之间的关系后,本类题目不难解答。

知识点分析

计算机系统的主要技术指标包括:字长、内存容量、存取周期、运算速度和时钟频率(主频)等。

字长以二进制位为单位,大小是CPU能够同时处理的二进制的位数,它是用来衡量计算机精度的主要指标。

答案

D

难度提示

简单。相关概念题。

【例题2-4 存储设备——软盘】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

软磁盘处于写保护状态时,其中记录的信息( )。

A)绝对不会丢失

B)不能被擦除,但能追加新信息

C)不能通过写磁盘操作被更新

D)不能以常规方式被删除,但可以通过操作系统的格式化功能被擦除

58 解题方法

因为软磁盘处于写保护状态时,则该软盘上的信息只能被读出而不能再写入,因此选项B是错误的;选项D中说可以通过操作系统的格式化功能被擦除,这是错误的,写保护状态下的软盘不能被格式化;而选项A可以很容易地排除;得出正确答案是C。

知识点分析

软磁盘处于写保护状态时,则该软盘上的信息只能被读出而不能再写入。当软盘上存有重要数据且不再改动时,最好使软磁盘处于写保护状态,这样可以保护该软盘上的信息不被破坏或防止染上计算机病毒。

答案

C

难度提示

简单。相关常识题。

【例题2-5 存储器】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

切断计算机电源后,下列存储器中的信息会丢失的是( )。

A)RAM B)ROM C)软盘 D)硬盘

解题方法

请回忆有关存储器的知识。

知识点分析

RAM称为随机存储器,存储信息既可读出也可写入,但断电后,RAM上存放的信息就丢失。ROM称为只读存储器,存储信息只能读出不可写入,但断电后ROM上存放的信息不消失,一般将一些重要的信息存入ROM。软盘和硬盘都属于外存储器,它们都不会因为切掉计算机电源而丢失信息。

答案

A

难度提示

简单。基本概念题。

【例题2-6 数制之间的转换】

(全国计算机等级考试二级笔试试卷20xx年4月)

59 题干

十进制数127转换成二进制数是( )。

A)11111111 B)01111111

C)10000000 D)11111110

解题方法

请参照前面数制之间转换的介绍,具体运算过程如下。

2 127 余数 系数ki

2 63 1 k0 最低位

2 31 1 k1

2 15 1 k2

2 7 1 k3

2 3 1 k4

2 1 1 k5

0 1 k6 最高位

如上面的余数序列所示,得十进制数 127转换成二进制数是1111111,将其填满成 8位二进制数为01111111,因此选择答案B。

知识点分析

将十进制数转换成二进制数时,把十进制数除以2,取余数作最低位系数k0;再取商继续除以2,取余数作为高一位的系数;如此继续直到商为0时得到最高位系数,停止运算。依次所得到的余数序列就是转换成的二进制数。

答案

B

难度提示

中级。数制转换一直是等级考试的重点,请读者认真掌握。

【例题2-7 计算机语言处理软件】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

要把高级语言编写的源程序转换为目标程序,需要使用( )。

A)编辑程序 B)驱动程序

C)诊断程序 D)编译程序

解题方法

这是一道涉及计算机编程语言的问题,在前面我们已经详细介绍,请参照相关资料或书籍。

知识点分析

计算机语言分为机器语言、汇编语言和高级语言。高级语言是为一般程序员使用设计的,

60比较接近日常用语的,对机器依赖性低的,适用于各种计算机的计算机语言。将高级语言所写的程序翻译为机器语言程序,有两种方式,一种叫“编译程序”,另一种叫“解释程序”。

答案

D

难度提示

简单。概念记忆题。

【例题2-8 字符编码】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

英文大写字母B的ASCII码为42H,英文小写字母b的ASCII码为( )。

A)43H B)84H

C)74H D)62H

解题方法

题中应该首先注意给出的是英文大写字母B的ASCII码为42H,而这个值是十六进制数,需要给出的答案也是十六进制数。解这道题可以采用两种方法,一是先将英文大写字母B的十六进制ASCII码转换成十进制ASCII码值,在这个ASCII码值的基础上加上大小写字母之间的差值32,得到一个值,再将这个值转换成十六进制数便可得到正确答案;二是将大小写字母之间的差值32转换成十六进制数,得20,在英文大写字母B的ASCII码值42H的基础上加上20,可以得到最终答案为62H。所以答案为D。

知识点分析

了解ASCII码值的存储情况,知道基本字母的ASCII码值。比如小写字母a的ASCII码值为97(十进制数),而大写字母A的十进制ASCII码值为65,大小写字母ASCII码值之间的差值为32。

答案

D

难度提示

中级。在解答本题的过程中千万不要忘了数制之间的转换。

【例题2-9 网络的功能及应用】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

计算机网络的突出优点是( )。

A)速度快 B)资源共享 C)精度高 D)容量大

61 解题方法

概念记忆题,请读者参照前面的知识点介绍。计算机网络的突出优点是:资源共享。所以正确答案为B。

知识点分析

计算机网络的功能非常多,主要体现在:资源的共享、信息传输与集中处理、均衡负荷与分布式处理、综合信息服务。除了上面的功能外,计算机网络还有以下方面的应用:远程登录、传送电子邮件、电子数据交换、联机会议等。

答案

B

难度提示

简单。概念记忆题。

【例题2-10 网络传输】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

计算机网络能传送的信息是( )。

A)所有的多媒体信息 B)只有文本信息

C)除声音外的所有信息 D)文本和图像信息

解题方法

这是一道计算机网络的常识题,如果概念不够清楚,一时选不出确切答案,则可以使用排除法来解答此题。只要上过网的朋友都知道,网络可以传输文字和图片,因为我们通过浏览器看到的就包括这些,因此可以排除B和C选项。在网吧时常可以看到网友正在语音聊天,或者在线收听音乐,这里传输的就是声音文件,可以排除D选项。得出正确答案为A。

知识点分析

计算机网络通过文件传输(FTP)可直接进行文字和非文字信息的双向传输,非文字信息包括计算机程序、图像、照片、音乐、录像等。

答案

A

难度提示

简单。常识题。

【例题2-11 网络设备】

(全国计算机等级考试二级笔试试卷20xx年4月)

62 题干

目前,一台计算机要连入Internet,必须安装的硬件是( )。

A)调制解调器或网卡 B)网络操作系统

C)网络查询工具 D)WWW浏览器

解题方法

在解此类题目时,应加倍仔细,本题题目中要求选择的是需要安装的硬件,而选项中网络操作系统、网络查询工具、WWW浏览器都不属于硬件范畴,通过排除法就可得到正确答案为A。

知识点分析

目前,大多数网络用户使用的都是通过电话线拨号上网,这就要求用户自己选择网络服务提供商(ISP)。拨号上网的过程中,在计算机与电话线之间需要使用调制解调器(Modem,又被称为“猫”)进行信号的调制与解调。还有大部分企业通过局域网连入Internet,在这种连入方式中,需要安装的硬件设备就是网卡,企业里的所有计算机通过一个公用线路连入Internet。

答案

A

难度提示

简单。概念记忆题。

【例题2-12 邮件地址】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

下述电子邮件地址中正确的是( )。(选项中□表示空格)

A)Malin&ns. B)Malin@ns.

C)Lin□Ma&ns. D)Lin□Ma@ns.

解题方法

由电子邮件地址的固定格式可知,选项A和C都是错误的,因为这两个选项的地址中没有使用“@”符号,而使用了“&”,因此可以排除。在选项D中,用户名中使用了空格,这是不符合要求的,排除,可知正确答案为B。

知识点分析

每个电子邮箱都有一个邮箱地址,称为电子邮件地址(E-mail Address)。电子邮件地址的格式是固定的,并且在全球范围内是惟一的。用户的电子邮件地址格式为:用户名@主机名,其中“@”符号表示“at”。主机名指的是拥有独立IP地址的计算机的名字,用户名是指在该计算机上为用户建立的邮件账号。假如有这样一个 E-mail 地址:foxhua@hotmail.com,表示在“hotmail.com”主机上面有一个名为“foxhua”的用户;又如

63foxhua@sohu.com等。

答案

B

难度提示

简单。概念和常识题。

【例题2-13 多媒体存储介质】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

在多媒体计算机系统中,不能存储多媒体信息的是( )。

A)光盘 B)磁盘 C)磁带 D)光缆

解题方法

题中要求选出的是多媒体信息的存储设备,其中A、B和C都属于多媒体信息存储设备,而选项D则用来传输多媒体信息。因此可知答案为D。

知识点分析

我们现在所说的多媒体,常常不是说多媒体信息本身,而主要是指处理和应用多媒体的综合技术,利用这种技术实现声音、图形、图像等多媒体的集成应用,使它们建立起逻辑联系,并能进行加工处理。而多媒体存储设备包括硬盘、磁盘、光盘、磁带等。

答案

D

难度提示

简单。概念记忆题。

【例题2-14 计算机病毒的清除】

(全国计算机等级考试二级笔试试卷19xx年9月)

题干

设一张软盘已染上病毒,能清除病毒的措施是( )。

A)删除该软盘上的所有文件

B)格式化软盘

C)删除软盘上的所有可执行文件

D)删除软盘上的所有批处理文件

64 解题方法

清除病毒常用的方法是使用杀毒软件进行清除,这同样也适用于软盘。清除软盘中的病毒可以使用杀毒软件,但是题中没有这个答案,而选项C和D明显是不正确的,因为除了可执行文件和批处理文件,病毒还可以感染别的文件,因此这两种操作并不能删除病毒;选项A中删除所有文件,看似已经没有任何文件可被感染,但是有引导区病毒,这类病毒能感染引导区文件,因此,这种做法也是不彻底的,只有选项B是正确的清除病毒的方法。

知识点分析

计算机病毒是人为制造的能够侵入计算机系统并给计算机带来故障的程序或指令集合。它通过不同的途径“潜伏”或“寄生”在存储介质(如内存、磁盘)或程序里,当满足某种条件时,它会自我复制并传播,使信息资源受到不同程度的损坏,严重时会使电脑、计算机网络全部瘫痪甚至无法恢复。要想清除病毒,只是删除一些文件是不够的,必须将病毒源删除,最常用的方法是使用杀毒软件进行清除,使用手工清除最彻底,但如果不小心将系统文件破坏掉,将造成不堪设想的后果。

答案

B

难度提示

中级。要求对病毒的概念、病毒的种类及清除方法有一定的了解。

【例题2-15 计算机病毒的传播】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

使计算机病毒传播范围最广的媒介是( )。

A)硬磁盘 B)软磁盘 C)内部存储器 D)互联网

解题方法

本题中前三个选项作为病毒传播的媒介,其传播范围没有什么明显的区别。现代社会,互联网非常发达,并且互联网的最大优势是资源共享,这给病毒的传播扩散提供了便利,病毒通过互联网传播所造成的损失远远超过了病毒通过其他途径传播所造成的损失。

知识点分析

计算机病毒传染性是指病毒程序在计算机系统中的传播和扩散。病毒在计算机运行过程中不断自我复制,不断感染别的程序。被感染的程序在运行时又会继续感染其他程序,于是很快就传染到整个计算机系统。在计算机网络中,病毒程序的传染速度更快,受害面也更大。

答案

65D

难度提示

简单。常识题。

66 本 章 练 习

【选择题】

1.一个完整的微机系统是由( )组成。

A)主机和外部设备 B)系统软件和应用软件

C)硬件系统和软件系统 D)主机和应用程序

2.微型计算机系统中的中央处理器通常是指( )。

A)内存储器和控制器 B)内存储器和运算器

C)控制器和运算器 D)内存储器、控制器和运算器

3.ROM的中文名称是( )。

A)只读存储器 B)随机存储器

C)内存储器 D)外存储器

4.在微型计算机中访问下面几个部件时,速度最快的是(

A)硬盘 B)软盘

C)显示器 D)内存储器

5.和外存储器相比,内存储器的主要特征是( )。

A)存储大量的信息 B)存储正在运行的程序

C)能存储程序和数据 D)能长期保存信息 。 )

6.微型计算机的性能主要取决于( )的性能。

A)CPU B)硬盘 C)显示器 D)RAM

7.微机系统中,3.5英寸软盘的写保护窗口开着时,表示软盘( )。

A)既能读,又能写 B)只能读不能写

C)只能写不能读 D)不起任何作用

8.某单位的工资管理软件属于( )。

A)编辑软件 B)系统软件

C)应用软件 D)实用软件

9.在表示存储器的容量时,M的准确含义是( )。

A)1000KB B)1024GM

C)1000B D)1024KB

10.在微机的性能指标中,用户可用的内存储器容量通常是指(

A)ROM的容量 B)RAM的容量

C)ROM和RAM的容量总和 D)cache的容量

11.以下哪个现象肯定与计算机病毒无关( )。

A)磁盘的卷标名、文件建立日期、时间和长度发生了变化

B)内存空间变小,程序运行不正常,或得出不合理的结果

C)喇叭出现异常声响,发出蜂鸣声、尖叫、长鸣等

67 D)显示器发热量过大

12.硬盘连同驱动器是一种( )。

A)只读存储器 B)半导体存储器

C)内存储器 D)外存储器 。 )

13.下列描述中,正确的是( )。

A)激光打印机是击打式打印机

B)软盘驱动器是存储器

C)计算机运算速度可用每秒钟执行的指令条数来表示

D)操作系统是一种应用软件

14.计算机软件系统包括( )。

A)程序和数据 B)编辑软件和实用软件

C)数据库软件和字处理软件 D)系统软件和应用软件

15.计算机软件分系统软件和应用软件两大类,其中( )是系统软件的核心。

A)数据库管理系统 B)语言处理系统

C)操作系统 D)工资管理系统

16.计算机的性能主要取决于( )。

A)磁盘容量、内存容量、键盘

B)显示器的分辨率、打印机的配置

C)字长、运算速度、内存容量

D)操作系统、系统软件、应用软件

17.下列选项中,都是硬件的是( )。

A)CPU、RAM和Windows

B)ROM、运算器和BASIC

C)软盘、硬盘和光盘

D)键盘、打印机和Word

18.英文小写字母d的ASCII码为100,英文大写字母D的ASCII码为( )。

A)50 B)66

C)52 D)68

19.二进制数1110111.11转换成十进制数是( )。

A)119.375 B)119.75

C)119.125 D)119.3

20.十六进制数FF.1转换成十进制数是( )。

A)255.0625 B)255.125

C)127.0625 D)127.125

21.下列叙述中正确的是( )。

A)将数字信号变换成便于在模拟通信线路中传输的信号称为调制

B)以原封不动的形式将来自终端的信息送入通信线路称为调制解调

C)在计算机网络中,一种传输介质不能传送多路信号

D)在计算机局域网中,只能共享软件资源,而不能共享硬件资源

68 22.各种网络传输介质( )。

A)具有相同的传输速率和相同的传输距离

B)具有不同的传输速率和不同的传输距离

C)具有相同的传输速率和不同的传输距离

D)具有不同的传输速率和相同的传输距离

23.多媒体计算机系统的两大组成部分是( )。

A)多媒体功能卡和多媒体主机

B)多媒体通信软件和多媒体开发工具

C)多媒体输入设备和多媒体输出设备

D)多媒体计算机硬件和多媒体计算机软件系统

24.下列哪个不是计算机病毒的特性( )。

A)潜伏性 B)传染性

C)破坏性 D)实时性

【填空题】

1.微机的硬件系统由______、______、______、_______四大部分组成。

2.微机的软件系统包括______、______两大部分。

3.微型计算机可以配置不同的显示系统。在CGA、EGA和VGA的标准中,显示性能最好的一种是______。

4.我们通常所讲的主机是由______、______组成。

5.根据工作方式的不同,可将存储器分为______、______两种。

6.一个完整的软盘存储系统是由______、______、______组成的。

7.当前,常用的3.5英寸高密软盘的存储容量是______。

8.在计算机术语中,存储容量1KB=______B。

9.刚刚买回的新磁盘必须经过______才能使用。

10.在微机中,显示器的分辨率越______越好。

11.当前,微机中最常用的输入设备有______、______两种。

12.速度快、分辨率高、价格贵的打印机是______。

13.显示器和主机的开关顺序为:______时应先开显示器再开主机,而______时应先关主机再关显示器。

14.CIH病毒的发作时间是每年的______。

15.______病毒用它自己的数据代替硬盘的原始引导扇区的数据,一旦开机,就将病毒装入内存。病毒进入内存后,它就可以传播给其他磁盘并进行破坏。

16.计算机网络中BBS的中文名是______。

17.计算机网络划分的方法很多,按照通信距离划分,计算机网络可以分为______和______;按照网络拓扑结构来划分,可以分为______、______、______等;按照通信传输的介质来划分,可以分为______、______、______和______等;按照信号频带占用方式来划分,有可以分为______和______。

69【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

70 第 3 章 C 语言的结构

考纲要求

1.程序的构成,main函数和其他函数。

2.头文件、数据说明、函数的开始和结束标志。

3.源程序的书写格式。

4.C语言的风格。

知识点讲析

【程序的构成】

C程序是由函数构成的。一个C源程序至少应包括一个主函数,即main函数。除了main函数,还可以包括若干其他函数。下面用一个完整的程序实例说明C程序的构成。

例:求半径为r=1.0的圆面积。

程序3-1:

#define PI 3.14159 /*编译预处理——宏定义*/

#include <stdio.h> /*编译预处理——文件包含*/

#include <math.h> /*编译预处理——文件包含*/

main() /*主函数*/

{ float r,s; /*定义变量r、s类型为单精度实型*/

r=1.0; /*变量r赋初值*/

s=PI*pow(r,2); /*求圆面积s*/

printf("半径R=%f时,面积S=%f\n", r , s);

/*输出(显示)计算结果*/

}

暂且不讨论该程序实现的功能,只从结构上分析,可以发现一个完整的C程序包含如下若干结构:

1.注释部分/*??*/

这部分内容不会被编译,也不会被执行;可出现在程序中的任何位置。

2.编译预处理

程序编译、执行前系统自动进行的特殊处理(以#开头,结束处无分号)。

(1)宏定义 #define PI 3.14159

其中:PI——符号常量(宏名,最好用大写,以区别于一般变量)

3.14159——宏体(可以是一个表达式)

71 作用:用简单符号代表宏体部分内容,编译时会先自动替换。

意义:直观,可多次使用,便于修改。

注意:#define可出现在程序的任一位置。

(2)文件包含#include <stdio.h>

其中:stdio.h ——“头文件”(标准前导文件)

3.主函数main()

4.语句(均以分号结束)

(1)数据类型定义语句

数据类型定义语句的格式如表3-1所示:

表3-1 C语言的变量类型关键字

变量类型关键字 变量名 意 义

float r,s; 定义r、s为单精度实型变量

int r,s; 定义r、s为整型变量

所有变量都要先定义后使用。可以发现数据类型定义语句包含两部分:

① 变量类型关键字(定义变量类型)

② 变量名

(2)赋值语句

格式:变量名=表达式(常量,也可以是变量、函数、算式等);

如:

r=1.0; /*赋值*/

s=PI*pow(r,2)/4; /*计算表达式值并赋值*/

变量赋初值亦可在数据类型定义时进行,如:

float r=1.0, s;

(3)输出语句

如:

printf("半径R=%f时,面积S=%f\n", r , s);

其中:%f——格式字符串

\n——控制字符

引号中其他部分照原样显示输出。

注意:C语言本身没有输入、输出语句,其功能要借用有关函数实现。

【函数的构成】

通常函数由两部分组成,即函数的说明部分和函数体。下面通过一个简单的例子分析函数的结构。

例:比较任意输入的两个数的大小,并输出较大的数,如程序3-2所示。

72 程序3-2:

main()

{ int a,b,c;

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

c=max(a,b) /*调用函数*/

printf("max=%d",c);

}

int max(x,y) /*功能是比较大小的函数*/

int x,y;

{ int z;

if (x>y) z=x;

else z=y;

return(z);

}

函数的说明部分

包括函数名、函数类型、函数属性、函数参数名、形式参数类型,如程序3-2中语句。

int max(x,y)

int x,y;

其中:第一个int指定了函数类型,第二个int指定了函数形参的类型,max是函数名,括号中的x,y是函数的参数,而另一个x,y是形参。所有这些构成了函数的说明部分。

函数体

函数说明部分下面的大括弧“{}”内的部分是函数体。如果一个函数内有多个大括弧,则最外层的一对大括弧为函数体的范围。

函数体一般包括:

① 变量定义。

② 执行部分,由若干个语句组成。

【main()函数】

main()函数即通常所说的主函数。一个C程序必须有一个主函数,同时只能有一个主函数。

主函数的一般形式:

main()

{

主函数体

}

其中,主函数体可以分成两个部分,即说明部分程序段和执行部分程序段,说明部分程序段用于定义数据类型,但有的简单程序可以没有此部分;执行部分程序段则罗列各种操作指令,如输入、赋值、计算、控制、输出等。

注意:

(1)主函数可以出现在程序中的任意位置,但程序执行时总是从主函数开始,一般情况下,又在主函数中结束(有的程序有可能不在主函数中结束)。

73 (2)主函数可以调用其他各种函数,但其他函数不能调用主函数。

【其他函数】

相对主函数而言,其他函数是指标准函数和自定义函数。

标准函数可以在任何程序(函数)中调用,但一般不允许修改。调用格式为:

函数名(参数)

自定义函数即程序员根据需要,自己编写的函数。调用格式和标准函数相同。【头文件】

可以认为C语言是一种装配式语言,许多常规的工作,如输入、输出、数学函数、符号常量等,往往事先由人做成各种“程序模块”,存放在各种所谓“头文件”(.h)中。编写程序时,使用文件包含功能引用这些头文件,格式如下:

#include <头文件>

文件包含的作用,就是在编译时把相应 “头文件”的内容整体嵌入所编的源程序中。常用的头文件有:stdio.h(标准输入/输出函数)、math.h(数学函数)、stdlib.h(常用函数),等等。

使用“头文件”的优点

(1)现代程序设计方法认为,程序设计是一种艰巨的劳动,人在进行程序设

计的过程中,犯错误的机会很多,如果能找到已经验证的程序模块使用,是提高

程序设计效率和程序可靠性的有效措施。

(2)大大减少程序员重复劳动量,程序员只需将含有所需功能模块(函

数等)的头文件“包含”进来,并在程序适当位置加以“调用”(所谓“站在

巨人肩膀上”)。

【数据说明】

即定义程序中的变量类型。

【函数的开始和结束标志】

程序从主函数开始执行,当执行到调用函数的语句时,程序将控制转移到调用函数中执行,执行结束后,再返回主函数中继续运行,直至程序执行结束。

当程序执行的主体函数的最后一个大括弧“}”处时,程序结束。

74【源程序的书写格式】

程序3-1和3-2都是非常简单的C程序,结构清晰,便于阅读。程序的书写格式不仅保证程序的可读性,有效提高程序的可维护性,更重要的是如果不按照这些约定俗成的格式书写,很可能出现编译不通过等严重的错误。

典型的C程序书写格式可以总结成以下形式:

包含文件

子函数类型说明

全程变量定义

main()

{ 局部变量定义

<程序体>

}

sub1()

{ 局部变量定义

<程序体>

}

sub2()

{ 局部变量定义

<程序体>

. }

.

subN()

{ 局部变量定义

<程序体>

}

其中sub1(),?,subN()代表用户定义的子函数,程序体指Turbo C 2.0 提供的任何库函数调用语句、控制流程语句或其他用子函数调用语句等。

C程序书写格式自由,一行内可以写几个语句,一个语句可以分写在多行上。C程序没有行号,也不像别的语言那样严格规定书写格式。但过于自由的程序书写往往导致可读性较差,所以专业程序员也很讲究书写格式。

可以用/*?*/对C程序中的任何部分作注释。一个好的,有价值的源程序都应当加上必要的注释,以增加程序的可读性。在编译时它被C编译器忽略。

每条语句结束时必须有一个分号,分号是 C语句的必要组成部分,例如:c=a+b;分号不可少;但也不能多于一个分号,否则,认为有多条语句,也就是说,每一条完整的语句对应一个分号,每一个分号对应一条语句。

每个程序体(主函数和每个子函数),必须用一对花括号“{}”括起来。

【C语言的风格】

1.C语言是一种典型的结构化程序设计语言,结构化程序由三种基本结构组成:顺序结构、选择结构和循环结构。

75 2.C语言也可以看做是一种函数式的语言,程序完全是由函数来完成的。每一个函数都是用来实现一种特定的功能,相当于其他语言中的子程序。因此,函数是C程序的基本单位。被主程序调用的函数可以是系统提供的库函数,例如printft和scanf函数(C语言本身没有输入输出语句。输入输出的操作是由库函数scanf和printf函数来完成的,即C对输入输出操作“函数化”了),也可以是用户根据需要自己编制设计的函数。C的函数库十分丰富,标准C提供一百多个库函数。Turbo C更提供了三百多个标准库函数,均存放在若干头文件中。

3.C程序总是从main函数开始执行的,而不论main函数在整个程序中的位置如何。Main函数可以放在程序最前头,也可以放在程序最后,或在一些函数之前在另一些函数之后。每个程序必须有一个而且只能有一个称作主函数的main()函数。

补充与扩展

【C语言的关键字】

标准C共有32个关键字,请参阅本书附录的关键字表。

C语言的关键字都用小写字母。因为C语言区分大写与小写,所以“else”是关键字,“ELSE”则不是。在C程序中,关键字不能用于其他目的,即不允许将关键字作为变量名或函数名使用。

【开发C程序的步骤】

开发C程序包括以下四步。

1.程序设计

即编写程序。程序员用任一编辑软件(编辑器)将编写好的C程序输入计算机,建立C源程序文件。C程序习惯上使用小写英文字母,常量和其他用途的符号可用大写字母。

当然,编写程序之前有许多问题是需要考虑的,越是大型的程序,前期工作越复杂,一般来说,需要解决的问题包括:数据结构和算法。

2.程序编译

是指将编写好的源文件翻译成二进制目标代码的过程。编译过程是使用编译程序(编译器)完成的。不同操作系统下的各种编译器使用的命令不完全相同。编译时,编译器首先检查源程序中的每一条语句的语法,当发现错误时,就在屏幕上显示错误的位置和错误类型的信息。此时,要再次调用编辑器进行查错修改。然后,再进行编译,直至排除所有语法和语义错误。正确的源程序文件经过编译后在磁盘上生成目标文件(.OBJ文件)。

3.连接程序

编译后产生的目标文件是可重定位的程序模块,不能直接运行。连接就是把目标文件

76和其他分别进行编译生成的目标程序模块(如果有的话)及系统提供的标准库函数连接在一起,生成可以运行的可执行文件的过程。连接过程使用连接程序(连接器)完成,生成的可执行文件存在磁盘中。

4.程序运行

生成可执行文件后,就可以在操作系统控制下运行。若执行程序后达到预期目的,则C程序的开发工作到此结束。否则,要进一步检查修改源程序,重复编写—编译—连接—运行的过程,直到取得预期结果为止。大部分C语言都提供一个独立的集成开发环境,它可将前面三步连在一起,让人感觉C程序可以直接变成可执行文件。

典 型 例 题

【例题3-1 C语言的关键字】

(全国计算机等级考试二级笔试试卷19xx年4月)

题干

C语言提供的合法的关键字是( )。

A)swicth B)cher C)Case D)default

解题方法

标准C共有32个关键字,编程中经常用到,所以熟练的程序员根本无需记忆就可以做出这类题目,但对于初学者,由于干扰项影响,往往不太容易选出正确答案,所以需要背诵所有的关键字,希望靠排除法解决这类题不是明智的选择。

知识点分析

选项A拼写错误,选项B有点像字符型数据类型定义关键字char,但C语言没有cher这个关键字,选项C给出的也很像一个关键字,但是C语言的关键字都是小写字母,而C语言区分大小写,所以Case并不是关键字,D给出的是一个没有问题的关键字。

正确答案

D

难度提示

中级。须熟练记忆所有的C语言关键字,否则难度较大。试题中经常出现拼写错误的选项,使得C语言掌握很好的考生也容易出错。

本 章 练 习

【选择题】

77 1.以下关于C语言标志符的描述中,正确的是( )。

A)标志符可以由汉字组成 B)标志符只能以字母开头

C)关键字可以作为用户标志符 D)Area与area是不同的标志符

2.以下正确的叙述是( )。

A)在C语言中,main函数必须位于文件的开头

B)C语言每行中只能写一条语句

C)C语言本身没有输入、输出语句

D)对一个C语言进行编译预处理时,可检查宏定义的语法错误

【填空题】

1.在TURBO C环境中用RUN命令运行一个C程序,所运行的程序的扩展名是______。

2.C语言源程序文件的扩展名是______,经过编译后,生成文件的扩展是名______,经过连接后,生成文件的扩展名是______。

3.结构化程序由______、______、______三种基本结构组成。

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

78 第 4 章 数据类型及其运算

考纲要求:

1.C语言的数据类型(基本类型、构造类型、指针类型、空类型)及其定义

方法。

2.C语言运算符的种类、运算优先级和结合性。

3.不同类型数据间的转换与运算。

4.C语言表达式类型(赋值表达式、算术表达式、关系表达式、逻辑表达式、

条件表达式、逗号表达式)和求值规则。

知识点讲析

【C语言的数据类型】

在程序中经常使用到各种变量,这些变量都必须预先加以说明,即先说明,后使用,这也是C语言的特点之

一。对变量的说明应该包括三个方面:数据类型、存储类型和作用域。其中数据类型是最重要的方面。

所谓数据类型就是按被说明量的性质,表示形式,占据存储空间的多少,构造特点来划分的一种数据结构。C语言的数据类型包含以下四类:基本类型、构造类型、指针类型和空类型,形式如下所示。

整型

字符型 单精度型

基本类型

浮点型

枚举类型 双精度型

数据类型 数组类型

构造类型 结构体

共用体

指针类型

空类型

79【常量和变量】

常量是指在程序运行过程中其值不能被改变的量。常量又可以分为整型常量、字符型常量和实型常量,等等。

变量则是指在程序运行过程中,其值可以被改变的量。同常量相对应,变量也可以分为整型变量、字符型变量和实型变量,等等。与常量不同的是,一个变量应该有一个名字,在内存中占据一定的存储单元,该存储单元中保存的是变量的值。即变量有两个属性:变量名和变量值,这两者是不同的。

在C源程序中,常量是可以不经说明而直接引用的,而变量则必须先说明后使用。【整型数据】

整型常量

整型常量就是整常数。在C语言中可以使用三种形式来表示整型常量。

十进制整数。即普通的日常生活中使用的数据。例如下面的数据均是正确的十进制整型常量:

1、23、963、-56、0

但下面的数据却不是正确的十进制数:

56D、E0、F56(因为含有非法字符)

八进制整数。以0开头的数据即是八进制整数,即以0作为八进制数的前缀。数码取值为0~7,其他数码是无意义的。例如下面的数据是正确的八进制数:

045(十进制为37) 0201(十进制为129) 0177777(十进制为65535)

但下面的数据却不是正确的八进制数:

256(无前缀0) 03A2(包含了非八进制数码)

十六进制整数。十六进制整常数的前缀为0X或0x。其数码取值为0~9,A~F或a~f。以下各数是合法的十六进制整常数:

0X2A(十进制为42) 0XA0 (十进制为160) 0XFFFF (十进制为65535)

但以下各数不是合法的十六进制整常数:

5A (无前缀0X) 0X3H (含有非十六进制数码)

从上面的分析可以看出,程序中是根据前缀来区分各种进制数的,因此在书写常数时需要注意不要把前缀弄错造成结果不正确。

整型变量

整型变量又可以分为基本型、短整型、长整型和无符号型四种。

80 基本型。以int表示,即类型说明符为int。在内存中占两个字节,其取值为基本整常数。

短整型。以short int或者以short表示,即类型说明符为short int或short。在内存中所占字节和取值范围均与基本型相同。

长整型。以long int或者以long表示,即类型说明符为long int或long。在内存中占4个字节,其取值为长整型常数。

无符号型。无符号型又可以分成下面三种类型:无符号整型、无符号短整型和无符号长整型。它们的类型说明符分别为unsigned int 或unsigned)、unsigned short和unsigned long。各种无符号类型量所占的内存空间字节数与相应的有符号类型量相同。但由于省去了符号位,故不能表示负数。

【整型变量的说明】

前面已经说明,C语言规定程序中所有用到的变量都必须在程序中指定其类型,也就是说,要对变量首先进行定义。

变量说明的一般形式为:

类型说明符 变量名标识符1,变量名标识符2,??;

例如:

int a,b,c;(说明a,b,c为整型变量)

long x,y;(说明x,y为长整型变量)

unsigned p,q;(说明p,q为无符号整型变量)

在对变量进行说明时,需要注意下面的问题:C语言允许在一个类型说明符后,说明多个相同类型的变量。各变量名之间用逗号间隔。类型说明符与变量名之间至少用一个空格间隔。最后一个变量名之后必须以“;”号结尾。变量说明的位置必须放在变量使用之前。【实型数据】

实型常量

实型也称为浮点型,实型常量也称为实数或者浮点数。在C语言中有两种表示形式来表示一个实数。这两种表示形式为十进制数形式和指数形式。

十进制数形式。有数字和小数点组成,而且小数点是必需的。例如下面的数据均是合法的实数:0.0,.25,

5.789,0.13,5.0,300.,-267.8230等。

指数形式。由十进制数,加阶码标志“e”或“E”以及阶码(只能为整数,可以带符号)组成。需要注意的是,阶码标志“e”或“E”之前必须有数字,之后的指数必须为整数。例如,下面的数据均是正确的实数:2.1E5,

3.7E-2,0.5E7,-2.8E-2,等等。但下面的数据则不是合法的实数:

E7(阶码标志E之前无数字)

81 .-5(无阶码标志)

53.-E3(负号位置不对)

2.7E(无阶码)

标准C允许浮点数使用后缀。后缀为“f”或“F”即表示该数为浮点数。如356f和356.是等价的。

实型变量

在C语言中,实型变量分为单精度型和双精度型两类,其类型说明符分别为float和double。在一般的系统中单精度型占4个字节(32位)内存空间,能提供7位有效数字。双精度型占8 个字节(64位)内存空间,可提供16位有效数字。而数值的范围随机器系统而异。应当说明的是,实型常量不分float型和double型,一个实型常量可以赋给一个float型或double型变量,这时将根据变量的类型截取实型常量中相应的有效位数字。实型变量说明的格式和书写规则与整型相同。例如:

float x,y; (x,y为单精度实型量)

double a,b,c; (a,b,c为双精度实型量)

【字符型数据】

字符型数据包括字符常量和字符变量。

字符常量

字符常量是用单引号括起来的一个字符。例如'a','b', '=', '+', '?'等都是合法的字符常量。需要注意的是,在C语言中字符常量只能用单引号括起来,不能用双引号或其他括号。并且字符常量只能是单个字符,不能是字符串。字符可以是字符集中的任意字符,但数字被定义为字符型之后就不能参与数值运算。例如 '5' 和5是不同的:'5'是字符常量,不能参与运算。

字符变量

字符变量的取值是字符常量,即单个字符。字符变量的类型说明符是char。字符变量类型说明的格式和书写规则都与整型变量相同。例如:

char a,b;

每个字符变量被分配一个字节的内存空间,因此只能存放一个字符。字符值是以ASCII码的形式存放在变量的内存单元之中的。如x的十进制ASCII码是120,y的十进制ASCII码是121。对字符变量a,b赋予 'x' 和 'y' 值:

a='x';

b='y';

实际上是在a,b两个单元内存放120和121的二进制代码:

a: 01111000

b: 01111001

82所以也可以把它们看成是整型量。

83【C运算符】

C语言的运算符不仅具有不同的优先级,而且还有一个特点,就是它的结合性。在表达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约,以便确定是自左向右进行运算

还是自右向左进行运算。这种结合性是其他高级语言的运算符所没有的,因此也增加了C语言的复杂性。

C语言的运算符可分为以下几类:算术运算符、关系运算符、逻辑运算符、位操作运算符、赋值运算符、条件运算符、逗号运算符、指针运算符、求字节数运算符、特殊运算符。

C语言中,运算符的运算优先级共分为15级。1级最高,15级最低。在表达式中,优先级较高的先于优先级较低的进行运算。而在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。C语言中各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。例如算术运算符的结合性是自左至右,即先左后右。如有表达式x?y+z则y应先与“-”号结合,执行x?y运算,然后再执行+z的运算。这种自左至右的结合方向就称为“左结合性”。而自右至左的结合方向称为“右结合性”。最典型的右结合性运算符是赋值运算符。如x=y=z,由于“=”的右结合性,应先执行y=z再执行x=(y=z)运算。C语言运算符中有不少为右结合性,应注意区别,以避免理解错误。【算术运算符】

用于各类数值运算。包括加(+)、减(?)、乘(*)、除(/)、求余(或称模运算,%)、自增(++)、自减(--)共七种。

加法运算符为双目运算符,即应有两个量参与加法运算。如a+b,4+8等。具有右结合性。减法运算符为双目运算符。但“?”也可作负值运算符,此时为单目运算,如-x,-5等具有左结合性。乘法运算符“*”为双目运算,具有左结合性。除法运算符“/”也为双目运算符,具有左结合性。参与运算量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是实型,则结果为双精度实型。如下面的程序中的运算。

程序4-1: P4-1.c

void main()

{

printf("\n\n%d,%d\n",20/7,-20/7);

printf("%f,%f\n",20.0/7,-20.0/7);

}

参与运算量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是实型,则结果为双精度实型。本程序中,20/7,-20/7的结果均为整型,小数全部舍去。而20.0/7和-20.0/7由于有实数参与运算,因此结果也为实型。

求余运算符(模运算符)“%”也是双目运算符,具有左结合性。要求参与运算的量均为整型。求余运算的结果等于两数相除后的余数。见下面的程序。

84 程序4-2:P4-2.c

void main()

{

printf("%d\n",100%3);

}

求余运算符% 要求参与运算的量均为整型。如本例所示,程序的结果是输出100除以3所得的余数1。

自增1运算符记为“++”,其功能是使变量的值自增1。自减1运算符记为“--”,其功能是使变量值自减1。自增1,自减1运算符均为单目运算,都具有右结合性。可有以下几种形式:

++i (i自增1后再参与其他运算)

--i (i自减1后再参与其他运算)

i++ (i参与运算后,i的值再自增1)

i-- (i参与运算后,i的值再自减1)

下面的程序说明了自增1运算符和自减1运算符的使用方法。

程序4-3:P4-3.c

void main()

{

int i=5,j=5,p,q;

p=(i++)+(i++)+(i++);

q=(++j)+(++j)+(++j);

printf("%d,%d,%d,%d",p,q,i,j);

}

程序分析

在上面4种形式的运算符中,容易出错的是i++和i--。特别是当它们出在较复杂的表达式或语句中时,更让人难于弄清。在上面的程序中,对 P=(i++)+(i++)+(i++)应理解为先是3个i相加,得到P值为15,然后i再自增13次,即相当于加3,所以i的最后值为8。而对于q 的值则不然,q=(++j)+(++j)+(++j)应理解为q先自增1,然后再参与运算。由于q自增13次后值为8,三个8相加的和为24,所以j的最后值仍为8。

【关系运算符】

关系运算是逻辑运算中比较简单的一种。所谓的“关系运算”实际上是“比较运算”。将两个值进行比较,判断比较的结果是否符合给定的条件。C语言提供了6种关系运算符:

< (小于)

<= (小于或等于)

> (大于)

>= (大于或等于)

== (等于)

!= (不等于)

85 关系运算符的优先级别低于算术运算符,但高于赋值运算符。在这六种关系运算符中,除了等于(==)和不等于(!=)之外的四种运算符的优先级别相同,而这两种的优先级也相同,并且低于其他四种关系运算符。

【C表达式】

表达式是由常量、变量、函数和运算符组合起来的式子。一个表达式有一个值及其类型,它们等于计算表达式所得结果的值和类型。表达式求值按运算符的优先级和结合性规定的顺序进行。 单个的常量、变量、函数可以看做是表达式的特例。

【算术表达式】

算术表达式是由算术运算符和括号将运算对象(也称为操作数)连接起来的、符合C语法规则的式子。运算对象包括常量、变量、函数等。例如,以下是合法的算术表达式的例子:

a+b

(a*2)/c

(x+r)*8-(a+b)/7

(++i)-(j++)+(k--)

C语言规定了运算符的优先级和结合性。在表达式求值时,先按照运算符的优先级别高低次序执行。如果在一个运算对象两侧的运算符的优先级别相同,则按照规定的“结合方向”处理。

【关系表达式】

用关系运算符将两个表达式连接起来的式子称为关系表达式。这里所连接的式子可以是算术表达式、关系表达式、逻辑表达式、赋值表达式或者字符表达式,等等。例如下面的表达式即是合法的关系表达式:

a>b

a+b>b+c

'a'>'b'

关系表达式的值是一个逻辑值,即“真”或者“假”。并且用“1”代表真,用“0”代表假。

补充与扩展

【变量名、符号常量名、函数名等命名规则】

86 编写C语言程序时,变量名、符号常量名、函数名等各种标志符有比较严格的命名规则,它们都由字母、数字和下划线组成,虽然数字可以出现在标志符中,但不能出现在开头部分,如下的变量名是非法的:

int 888;

而下面两条语句的标志符命名是合法的:

int a888;

float _888;

尽管如此,在用户自定义的标志符中,下划线通常不出现在开头。这是因为以下划线开头的变量一般是系统变量。

因为C语言识别大小写字符,所以下面两条语句定义的是不同的变量:

int a888;

int A888;

标志符的名字还不能和C语言的关键字相同,否则会出现系统错误,同样因为C语言对大小写敏感,而关键字都是小写,所以下面的语句并没有语法错误:

int Char;

但是,如果书写不注意,很容易忽略大小写,造成不必要的麻烦,有那么多可供选择的命名字符,实在没有必要和关键字过不去。

事实上,标志符的命名反映了一个程序员的编程风格,一种广为提倡的做法是使用“见名知义”的命名策略,有的程序员喜欢用英文单词的首字母,或前几个字母命名,如:

float r; /*r是圆半径*/

float vol; /*vol表示体积*/

也有的程序员喜欢用英文单词的辅音字母,值得一提的是,中国的许多初学者喜欢用汉语拼音来命名,这种方法固然也可以形成“风格”,但因为汉语拼音重码较多,在编写大系统的时候容易造成歧义,故不值得提倡。

另外,在Turbo C中,标志符长度不得超过32个字符,超出的部分将被系统自动截除。

总之,标志符命名时,保障合法的情况下,简短易记对程序设计会很有帮助,值得初学者花些心思,形成良好习惯。

【关于整型数据的说明】

读者已经知道,在16位字长的机器上基本整型数据的长度为16位,因此表示的整数的范围是有限的。十进制无符号整常数的范围为0~65535,有符号数为-32768~+32767。八进制无符号数的表示范围为0~0177777。十六进制无符号数的表示范围为0X0~0XFFFF或0x0~0xFFFF。如果使用的数超过了上述范围,就必须用长整型数来表示。长整型数是用后缀“L”或“l”来表示的。例如:

十进制长整常数 158L(十进制为158) 358000L(十进制为-358000)

87 八进制长整常数 012L(十进制为10) 077L(十进制为63)

0200000L(十进制为65536)十六进制长整常数 0X15L(十进制为21) 0XA5L(十进制为165)

0X10000L(十进制为65536)

长整数158L和基本整常数158 在数值上并无区别。但对158L,因为是长整型量,C编译系统将为它分配4个字节存储空间。而对158,因为是基本整型,只分配两个字节的存储空间。因此在运算和输出格式上要予以注意,避免出错。无符号数也可用后缀表示,整型常数的无符号数的后缀为“U”或“u”。例如:358u,0x38Au,235Lu均为无符号数。前缀,后缀可同时使用以表示各种类型的数。如:

0XA5Lu表示十六进制无符号长整数A5,其十进制为165。

在下面的实例中,使用了上面所说的整型数据,给出了整型数据大的使用方法。

程序4-4:P4-4.c

main()

{

long x,y;

int a,b,c,d;

x=5;

y=6;

a=7;

b=8;

c=x+a;

d=y+b;

printf("c=x+a=%d,d=y+b=%d\n",c,d);

}

程序分析

x,y是长整型变量,a,b是基本整型变量。它们之间允许进行运算,运算结果为长整型。但c,d被定义为整型,因此最后结果为整型。本例说明,不同类型的量可以参与运算并相互赋值。其中的类型转换是由编译系统自动完成的。

【实型数据说明】

这里给出一个程序说明float和double类型的不同。

程序4-5:P4-5.c

void main()

{

float a;

double b;

a=33333.33333;

b=33333.33333333333333;

printf("%f\n%f\n",a,b);

88 }

程序分析

将main说明为返回void,即不返回任何类型的值。由于a是单精度浮点型,有效位数只有七位。而整数已占五位,故小数两位之后均为无效数字。b是双精度型,有效位为16位。但Turbo C规定小数后最多保留六位,其余部分四舍五入。

【字符型数据说明】

转义字符

转义字符是一种特殊的字符常量。转义字符以反斜线"\"开头,后跟一个或几个字符。转义字符具有特定的含义,不同于字符原有的意义,故称“转义”字符。例如,在前面各例题printf函数的格式串中用到的 "\n" 就是一个转义字符,其意义是“回车换行”。转义字符主要用来表示那些用一般字符不便于表示的控制代码。

常用的转义字符及其含义见表2-1。

表2-1 常用转义字符及其含义

字符形式 功 能

\n 回车换行

\t 横向跳到下一制表位置

\v 竖向跳格

\b 退格

\r 回车

\f 走纸换页

\\ 反斜线符"\"

\' 单引号(撇号)符

\ddd 1~3位八进制数所代表的字符

\xhh 1~2位十六进制数所代表的字符

广义地讲,C语言字符集中的任何一个字符均可用转义字符来表示。表 2-1中的\ddd和\xhh正是为此而提出的。ddd和hh分别为八进制和十六进制的ASCII代码。如\101表示字母"A",\102表示字母"B",\134表示反斜线,\XOA表示换行等。

程序4-6:P4-6.c

void main()

{

int a,b,c;

a=5;

b=6;

c=7;

printf("%d\n\t%d %d\n %d %d\t\b%d\n",a,b,c,a,b,c);

}

89 此程序说明转义字符的使用方法。程序在第一列输出a值5之后就是"\n",故回车换行;接着又是"\t",于是跳到下一制表位置(设制表位置间隔为8),再输出b值6;空二格再输出c值7后又是"\n",因此再回车换行;再空两格之后又输出a值5;再空三格又输出b的值6;再次后 "\t" 跳到下一制表位置(与上一行的6对齐),但下一转义字符"\b"又使退回一格,故紧挨着6再输出c值7。

字符串常量

字符串常量是由一对双引号括起的字符序列。例如:"CHINA","C program","$12.5"等都是合法的字符串常量。字符串常量和字符常量是不同的量。它们之间有很多区别:字符常量由单引号括起来,字符串常量由双引号括起来。字符常量只能是单个字符,字符串常量则可以含一个或多个字符。可以把一个字符常量赋予一个字符变量,但不能把一个字符串常量赋予一个字符变量。在C语言中没有相应的字符串变量。这是与 BASIC语言不同的。但是可以用一个字符数组来存放一个字符串常量。字符常量占一个字节的内存空间。字符串常量占的内存字节数等于字符串中字节数加1。增加的一个字节中存放字符"\0"(ASCII码为0)。这是字符串结束的标志。例如,字符串"C program"在内存中所占的字节为:C program\0。字符常量'a'和字符串常量"a"虽然都只有一个字符,但在内存中的情况是不同的。'a'在内存中占一个字节,可表示为:a;"a"在内存中占两个字节,可表示为:a\0。

【赋值运算符和赋值表达式】

赋值运算符就是赋值符号“=”,它的作用是将一个数据赋给一个变量。由“= ”连接的式子称为赋值表达式。

其一般形式为:

变量=表达式

例如:

x=a+b

w=sin(a)+sin(b)

y=i+++--j

赋值表达式的功能是计算表达式的值再赋予左边的变量。赋值运算符具有右结合性。因此a=b=c=5可理解为a=(b=(c=5))。在其他高级语言中,赋值构成了一个语句,称为赋值语句。而在C中,把“=”定义为运算符,从而组成赋值表达式。凡是表达式可以出现的地方均可出现赋值表达式。

例如,式子x=(a=5)+(b=8)是合法的。它的意义是把5赋予a,8赋予b,再把a,b相加,和赋予x,故x应等于13。在C语言中也可以组成赋值语句,按照C语言规定,任何表达式在其未尾加上分号就构成为语句。因此如x=8;a=b=c=5;都是赋值语句,在前面各例中我们已大量使用过了。

如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即把赋值号右边的类型换成左边的类型。规定如下,实型赋予整型,舍去小数部分。整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分(小数部分的值为0)。字符型赋予整型,由

90于字符型为一个字节,而整型为两个字节,故将字符的ASCII码值放到整型量的低八位中,高八位为0。整型赋予字符型,只把低八位赋予字符量。

程序4-7:P4-7.c

void main(){

int a,b=322;

float x,y=8.88;

char c1='k',c2;

a=y;

x=b;

a=c1;

c2=b;

printf("%d,%f,%d,%c",a,x,a,c2);

}

程序分析

本例表明了上述赋值运算中类型转换的规则。a为整型,赋予实型量y值8.88后只取整数8。x为实型,赋予整型量b值322,后增加了小数部分。字符型量c1赋予a变为整型,整型量b赋予c2 后取其低八位成为字符型(b的低八位为01000010,即十进制66,按ASCII码对应于字符B)。

在赋值符“=”之前加上其他二目运算符可构成复合赋值符。如+=,?=,*=,/=,%=,<<=,>>=,&=,^ =,| =。

构成复合赋值表达式的一般形式为:

变量 双目运算符=表达式

它等效于

变量=变量 运算符 表达式

例如:

a+=5 (等价于a=a+5)

x*=y+7 (等价于x=x*(y+7))

r%=p (等价于r=r%p)

复合赋值符这种写法,对初学者可能不习惯,但十分有利于编译处理,能提高编译效率并产生质量较高的目标代码。

【逗号运算符和逗号表达式】

在C语言中逗号“,”也是一种运算符,称为逗号运算符。其功能是把两个表达式连接起来组成一个表达式,称为逗号表达式。其一般形式为:

表达式1,表达式2

其求值过程是分别求两个表达式的值,并以表达式2的值作为整个逗号表达式的值。

程序4-8:P4-8.c

void main()

91 {

int a=2,b=4,c=6,x,y;

y=(x=a+b),(b+c);

printf("y=%d, x=%d",y,x);

}

程序分析

在本程序中,y等于整个逗号表达式的值,也就是表达式2的值,x是第一个表达式的值。

对于逗号表达式需要注意的是:逗号表达式一般形式中的表达式1和表达式2 也可以又是逗号表达式。例如:

表达式1,(表达式2,表达式3)

这就形成了嵌套引用的情形。因此可以把逗号表达式扩展为以下形式:

表达式1,表达式2,??,表达式n

整个逗号表达式的值等于表达式 n的值。程序中使用逗号表达式,通常是要分别求逗号表达式内各表达式的值,并不一定要求整个逗号表达式的值。并不是在所有出现逗号的地方都组成逗号表达式,如在变量说明中,函数参数表中逗号只是用作各变量之间的间隔符。

【强制类型转换运算符】

C语言有两种类型转换:一种是在运算时不必用户指定,系统自动进行类型转换,例如7+4.3。另外一种是强制类型转换。当自动类型转换不能实现目的时,就可以用强制类型转换。

强制类型转换是通过类型转换运算来实现的。其一般形式为:

(类型说明符)(表达式)

其功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如:

(float) a (把a转换为实型)

(int)(x+y) (把x+y的结果转换为整型)

在使用强制转换时应该注意下面的问题:类型说明符和表达式都必须加括号(单个变量可以不加括号),如把(int)(x+y)写成(int)x+y则成了把x转换成int型之后再与y相加了。无论是强制转换或是自动转换,都只是为了本次运算的需要而对变量的数据长度进行的临时性转换,而不改变数据说明时对该变量定义的类型,见下面的程序。

程序4-9:P4-9.c

main()

{

float f=5.75;

printf("(int)f=%d,f=%f\n",(int)f,f);

}

程序分析

92 这个程序表明,f虽强制转为int型,但只在运算中起作用,是临时的,而f本身的类型并不改变。因此,(int)f的值为5(删去了小数)而f的值仍为5.75。

93【逻辑运算符和逻辑表达式】

逻辑运算符

C语言中提供了三种逻辑运算符

&& (与运算)

|| (或运算)

! (非运算)

与运算符&&和或运算符||均为双目运算符。具有左结合性。非运算符!为单目运算符,具有右结合性。三种逻辑运算符按照以下的顺序排列优先次序:

!(非)→&&(与)→||(或)

逻辑运算符和其他运算符优先级的关系是:“&&”和“||”低于关系运算符,“!”高于算术运算符。

按照运算符的优先顺序可以得出:

a>b && c>d 可写成(a>b)&&(c>d)

!b==c||d<a 可写成((!b)==c)||(d<a)

a+b>c && x+y<b 可写成((a+b)>c)&&((x+y)<b)

逻辑运算的值同关系运算的值一样,也为“真”和“假”两种,用“1”和“0 ”来表示。其求值规则如下:与运算&&参与运算的两个量都为真时,结果才为真,否则为假。例如,5>0&&4>2,由于5>0为真,4>2也为真,相与的结果也为真。

或运算||参与运算的两个量只要有一个为真,结果就为真。 两个量都为假时,结果为假。例如:5>0||5>8,由于5>0为真,相或的结果也就为真。

非运算!参与运算量为真时,结果为假;参与运算量为假时,结果为真。例如:!(5>0)的结果为假。

虽然C编译在给出逻辑运算值时,以“1”代表“真”,“0 ”代表“假”。 但反过来在判断一个量是为“真”还是为“假”时,以“0”代表“假”,以非“0”的数值作为“真”。例如:由于5和3均为非“0”因此5&&3的值为“真”,即为1。又如:5||0的值为“真”,即为1。

逻辑表达式

逻辑表达式的一般形式为:

表达式 逻辑运算符 表达式

其中的表达式可以又是逻辑表达式,从而组成了嵌套的情形。例如:(a&&b)&&c根据逻辑运算符的左结合性,上式也可写为:a&&b&&c 。

逻辑表达式的值是式中各种逻辑运算的最后值,以“1”和“0”分别代表“真”和“假”。下面的程序说明了逻辑表达式的应用方法。

程序4-10:P4-10.c

void main()

94 {

char c='k';

int i=1,j=2,k=3;

float x=3e+5,y=0.85;

printf("%d,%d\n",!x*!y,!!!x);

printf("%d,%d\n",x||i&&j-3,i<j&&x<y);

printf("%d,%d\n",i==5&&c&&(j=8),x+y||i+j+k);

}

程序分析

在本程序中,!x和!y分别为0,!x*!y也为0,故其输出值为0。由于x为非0,故!!!x的逻辑值为0。对x|| i && j-3式,先计算j-3的值为非0,再求i && j-3的逻辑值为1,故x||i&&j-3的逻辑值为1。对i<j&&x<y式,由于i<j的值为1,而x<y为0故表达式的值为1和0相与,最后为0,对i==5&&c&&(j=8)式,由于i==5为假,即值为0, 该表达式由两个与运算组成,所以整个表达式的值为0。对于式x+ y||i+j+k由于x+y的值为非0,故整个或表达式的值为1。

典 型 例 题

【例题4-1 数据类型转换】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若有定义:

int a=8,b=5,c;

执行语句c=a/b+0.4;后,c的值为( )。

A)1.4 B)1

C)2.0 D)2

解题方法

本题是一道关于数据类型转换的题。解答这种类型题目的时候,考生首先要掌握各种数据类型的定义以及用法等知识点。在此基础上才能分析如何进行运算,也就是说运算的内在关系如何。比如在本题中,考生需要清楚的是“程序运行过程中是如何处理a/b的”这样一个问题。

知识点分析

在这道题目中,定义了整型变量a、b和c,并且为a和b进行了赋初值。在执行所给

95的语句c=a/b+0.4;时,先进行a/b的运算,得到一个实型数据1.6。但因为前面的定义是整型,所以这里只能得到1而不是1.6。然后再运算1+0.4得到1.4。同样道理,c是整型变量,所以只能得到结果为1。

正确答案

B

难度提示

中级。只要读者能掌握C语言的数据类型,并能对一些特殊问题有较好的了解就很容易解答本题。

【例题4-2 字符型数据】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若变量a是int类型,并执行了语句:

a='A'+1.6;

则正确的叙述是( )。

A)a的值是字符C B)a的值是浮点型

C)不允许字符型和浮点型相加 D)a的值是字符 'A' 的ASCII值加上1。

解题方法

本题仍然是一道关于数据类型以及如何使用的题目。对于这种类型的题目,其知识点一般都比较简单,很多考生都不对其陌生。但这些知识点在运用时却非常灵活,尤其是当涉及到一些细节时就使人感到更加迷惑。

知识点分析

题目开始就已经定义了变量a是一个整型变量,所以在执行了语句a='A'+1.6;之后得到的应该还是整型数据。

另外,为了正确解答本题,读者还应该知道字符数据在内存中的存储形式及其使用方法。在内存中,字符数据是以ASCII码的形式存储,从而使得字符数据的存储形式与整数的存储形式相类似。也就是说,C语言使字符型数据和整型数据之间可以通用。这样一来,就可以对字符型数据进行算术运算,此时相当于对它们的ASCII码进行算术运算。

'A'代表一个整型数据,比如065,进行运算,得到初步结果66.6,又因为a是一个整型数据,所以本题可得答案。

正确答案

D

难度提示

96 中级。本题是一道较好的关于数据类型的综合题。考查了考生对字符型数据的理解,并且考查了考生对数据转换这一知识点的掌握。

97【例题4-3 长整型数据类型】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

在C语言中,合法的长整型常数是( )。

A)0L B)4962710

C)324562& D)216D

解题方法

本题是关于数据类型判断的一道题,数据类型是编程语言中非常重要的一个概念。

知识点分析

在一个整型常量的后面加一个字母l或者L,就可以认为该常量是long int型数据。例如233l、233L、0L,等等。由此判断,只有选项A正确。

正确答案

A

难度提示

初级。本题是非常简单的数据类型的判断题。数据类型是编程语言中最基本的概念之一,对它的掌握是学好其他知识的前提。

【例题4-4 字符常量】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下选项中合法的字符常量是( )。

A)"B" B)'\010'

C)68 D)D

解题方法

数据类型是C语言中重要的知识点,所以对它的考查几乎是必考的内容,在每一次的考试中总有所涉及。

知识点分析

字符常量是用单引号括起来的一个字符。例如'a','b','+','?'等都是合法的字符常量。需要注意的是,在C语言中字符常量只能用单引号括起来,不能用双引号或其他括号。并且字符常量只能是单个字符,不能是字符串。字符可以是字符集中的任意字符,但数字被定义为字符型之后就不能参与数值运算。

98 正确答案

B

难度提示

初级。知识点比较简单,但非常重要。

【例题4-5 逻辑表达式】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

设a、b、c、d、m、n均为int型变量,且a=5、b=6、c=7、d=8、m=2、n=2,则逻辑表达式(m=a>b)&&(n=c>d)运算后,n的值为( )。

A)0 B)1

C)2 D)3

解题方法

表达式是C语言中又一个比较重要的知识点。

知识点分析

对于&&运算符来说,如果有a&&b,则只有当a≠0时才能继续判断右面的运算。只要a=0就不必判别b的值。所以对于逻辑表达式(m=a>b)&&(n=c>d)来说,由于a>b的值为0,m=0,所以(n=c>d)不被执行,因此n的值不是0或者其他值而保持原值2不变。

正确答案

C

难度提示

中级。逻辑表达式这一知识点也是比较重要的,所以对它的考查也是经常的。读者在复习时要注意这一点。

【例题4-6 条件运算符】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

假定w、x、y、z、m均为int型变量,有如下程序段:

w=1;

x=2;

y=3;

99 z=4;

m=(w<x)?w:x;

m=(m<y)?m:y;

m=(m<z)?m:z;

则该程序运行后,m的值是( )。

A)4 B)3

C)1 D)2

解题方法

本题是关于条件运算符的一道试题,运算符是C语言中非常重要的知识点,历来是考试考查的重点之一。

知识点分析

条件运算符要求有三个操作对象,所以称其为三目运算符,这是C语言中惟一的一个三目运算符。条件表达式(m<z)?m:z的执行顺序是:如果(m<z)条件为真,则条件表达式取值m,否则取值z。例如在本题中经过三次条件运算符的运算后,得到的m值为1。

正确答案

C

难度提示

中级。关于条件运算符的知识点考生有可能忽略了它,因为该知识点在实际的编程实践中使用得比较少,但作为一种运算,考生应该掌握。

本 章 练 习

【选择填空题】

1.在C语言中,整型变量可以分为基本型、______、长整型和______四种。

2.在C语言中有两种表示形式来表示一个实数。这两种表示形式为______形式和______形式。

3.C语言的运算符可分为以下几类:______运算符、关系运算符、______运算符、位操作运算符、赋值运算符、______运算符、逗号运算符、______运算符、______运算符、特殊运算符。

4.在C语言中,如果下面的变量都是int类型,则输出的结果是( )。

sum=pad=5;

pad=sum++;

pad++;

++pad;

printf("%d\n",pad);

A)7 B)6

100 C)5 D)4

5.已知在ASCII代码中,字母A的序号为65,以下程序的输出的结果是______。

程序4-11:P4-11.c

#include<stdio.h>

main()

{

char c1='A',c2='Y';

printf("%d,%d\n",c1,c2);

}

6.C语言中,用户能使用的合法标志符是( )。

A)a,b B)-xyz C)fa2 D)5i

void define sort_a x.i

a123 s(x) string malloc

【综合题】

1.C语言的数据类型有什么特点?将C语言的数据类型和其他高级语言的数据类型作一比较,以列表的形式写出。

2.C语言规定其所用到的变量要“先定义,后使用”,这样做对于编程有什么好处?

3.C语言提供了哪些运算符?以其中的几个为例说明运算符的特点和使用方法。

4.归纳C语言的表达式类型,说明表达式和语句的区别是什么。

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

101 第 5 章 基 本 语 句

考纲要求

1.表达式语句,空语句,复合语句。

2.数据的输入与输出,输入输出函数的调用。

3.复合语句。

4.goto语句和语句标号的使用。

知识点讲析

【表达式语句】

在赋值表达式的尾部加上一个“;”号,就构成了赋值语句也称表达式语句。例如a=b+c是赋值表达式,a=b+c;则是赋值语句;i=1,j=2是逗号表达式,而i=l,j=2;则是一条赋值语句。i++;i--;都是赋值语句,程序执行时,首先取出变量i中的值,加1或减1后再把新的数放入变量i中。赋值语句是一种可执行语句,应当出现在函数的可执行部分。

C语言中可由形式多样的赋值表达式构成赋值语句,用法灵活,因此读者首先应当掌握好赋值表达式的运算规律才能写出正确的赋值语句。

【空语句】

C程序中的所有语句都必须有一个分号“;”作为结束。如果只有一个分号,如:

main()

{ ;}

这个分号也是一条语句,称为“空语句”,程序执行时不产生任何动作。程序设计中有时需要加一个空语句来表示存在一条语句;但随意加分号也会导致逻辑上的错误,需要谨慎使用。

【复合语句】

在C语言中,一对花括号“{ }”不仅可用作函数体的开头和结尾的标志,也可用作复合语句的开头和结尾的标志;复合语句也可称为“语句块”,复合语句的语句形式

102如下:

{语句l;语句2;??;语句n}

用一对花括号把若干语句括起来构成一个语句组,一个复合语句在语法上视为一条语句,在一对花括号内的语句数量不限。例如:

{a++;b*=a;printf("b=%d\n",b);}

在复合语句内,不仅可以有执行语句,还可以有定义部分,定义部分应该出现在可执行语句的前面。

【数据的输入与输出】

把数据从计算机内部送到计算机的外部设备上的操作称为“输出”。例如,把计算机运算结果显示在屏幕上或打印在纸上,或者送到磁盘上保留起来。从计算机外部设备将数据送入计算机内部的操作称为“输入”。

C语言本身不提供用于输入和输出的语句。在C的标准函数库中提供了一些输入输出函数,例如:printf函数和scanf函数。

【输入函数的调用】

这里先介绍两个最基本的输出函数,它们都以终端(或系统隐含指定的输出设备)为对象。它们是字符输出函数和格式输出函数。

putchar函数(字符输出函数)

putchar函数的作用是向终端输出一个字符,例如:

putchar(c);

输出字符变量c的值。c可以是字符型变量或整型变量。在使用标准I/O库函数时,要用预编译命令“include”将“stdio.h”文件包括到用户源文件中。即:

#include <stdio.h>

stdio.h是Standard input & output的缩写,它包含了与标准I/O库有关的变量定义和宏定义。在需要使用标准I/O库中的函数时,应在程序前使用上述预编译命令,但在用printf和scanf函数时,则可以不要(只有printf和scanf例外)。

下面来看一个putchar函数的例子。

程序5-1:P5-1.c

#include <stdio.h>

main()

{ char a,b,c;

a='B';b='O';c='Y';

putchar(a); putchar(b); putchar(c);

}

103 运行结果:

BOY

也可以输出控制字符,如putchar('\n') 输出一个换行符。如果将例3-1程序的最后一行改为:

putchar(a);putchar('\n');

putchar(b);putchar('\n');

putchar(c);putchar('\n');

则输出结果为:

B

O

Y

也可以输出其他转义字符,如:

putchar('\0101') (输出字符‘A’)

putchar('\'’) (输出单引号字符’)

putchar('\015') (使输出回车,不换行)

printf函数(格式输出函数)

printf函数是C语言提供的标准输出函数,它的作用是在终端设备(或系统隐含指定的输出设备)上按指定格式进行输出。

printf函数的一般调用形式如下:

printf(格式控制,输出项表)

如果在printf函数调用之后加上“;”,就构成了输出语句。

例如:

printf("a=%d,b=%d",a,b);

其中 printf是函数名;在圆括号中用双引号括起来的字符串,如“a=%d,b=%d”称为格式控串;a,b是输出项表中的输出项,它们都是printf函数的参数。

在这里,格式控制的作用是:

1.格式转换说明

格式转换说明的作用是将要输出的数据转换为指定的格式输出。它总是由“%”符号开始,紧跟其后的是格式描述符。当输出项为int类型时,系统规定用d作为格式描述字符,其形式为%d,如上面的例子;当输出项为float或double类型时,f或e作为格式描述字符,其形式为%f或%e(对于double类型也可用%lf或%le)。

2.提供需要原样输出的文字或字符

如以上输出语句中的“a=”,“b=”等都是希望原样输出的字符。假若a,b的值分别为3和4,则以上输出语句的输出结果为:a=3,b=4。

输出项表中的各输出项要用逗号隔开,输出项可以是合法的常量、变量或表达式。格式转换说明的个数要与输出项的个数相同,使用的格式描述符也要与它们一一对应且类型匹配。

104 例如以下程序。

程序5-2:P5-2.c

main()

{int i=2518;

float a=3.1415;

printf("i=%d,a=%f,a*10=%e\n",i,a,a*10);

}

运行后的输出结果为:

i=2518,a=3.141500,a*10=3.14150e+01

在以上printf的格式控制串中,“i=”按原样输出,在%d的位置上输出变量i的值,接着输出一个逗号和“a=”,在%f的位置上输出变量a的值,又输出一个逗号和“a*10= ”,在%e的位置上输出表达式a*10的值,最后的“\n”是C语言中特定的转义字符,相当于一个回车符使得屏幕光标或打印机头移到下一行的开头。

【输出函数的调用】

现在介绍两个输入函数,它们是以终端(或系统隐含指定的输入设备)为输入设备的。

getchar函数(字符输入函数)

此函数的作用是从终端(或系统隐含指定的输入设备)输入一个字符。getchar函数没有参数,其一般形式为:

getchar()

函数的值就是从输入设备得到的字符。例如:

程序5-2:P5-2.c

#include "stdio.h"

main()

{char c;

c=getchar();

putchar(c);

}

在运行时,如果从键盘输入字符‘a’;

a↙ (输入‘a’后,按“回车”键,字符才送到内存)

a (输出变量c的值‘a’)

请注意,getchar()只能接收一个字符。getchar 函数得到的字符可以赋给一个字符变量或整型变量,也可以不赋给任何变量,作为表达式的一部分。例如,例3.2第4、5行可以用下面一行代替:

putchar(getchar());

因为getchar()的值为‘a’,因此输出‘a’。也可以用printf函数;

105 printf("%c",getchar());

在一个函数中要用getchar函数,应该在函数的前面(或本文件开头)用

#include <stdio.h>

因为在使用标准I/O库中的函数时需要用到“stdio.h”文件中包含的一些信息。

scanf函数(格式输入函数)

scanf函数是C语言提供的标准输入函数,它的作用是在终端设备(或系统隐含指定的输入设备)上输入数据。

scanf函数的一般调用形式如下:

scanf(格式控制,输入项表)

如果在scanf函数调用之后加上“;”,就构成了输入语句。

例如:

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

其中scanf是函数名,在圆括号中用双引号括起来的字符串如“%d%d”为格式控制串;&a,&b是输入项表中的两个输入项,它们都是scanf函数的参数。

格式控制串的作用是指定输入时的数据转换格式,即格式转换说明。格式转换说明也是由“%”符号开始,其后是格式描述符。当输入项为int类型时,系统规定用d作为格式描述字符,其形式为%d,如上面的例子;当输入项为float或double类型时,用f或e作为格式描述字符,其形式为%f或%e,输入项为double类型时,则必须用%lf或%le作为格式描述符,否则输入的数据不正确。

输入项表中的各输入项用逗号隔开,各输入项只能是合法的地址表达式,如上例中的&a和&b。变量a,b前的符号“&”是C语言中的求地址运算符,&a就是取变量a的地址,&b则是取变量b的地址。这也就是说,输入项必须是某个存储单元的地址。这一点,读者一定要给予充分的注意。

【GOTO语句】

goto语句称为无条件转向语句,goto语句的一般形式如下:

goto 语句标号;

goto语句的作用是,把程序的执行转向语句标号所在的位置,这个语句标号必须与此goto语句同在一个函数内。滥用goto语句将使程序的流程毫无规律,可读性差,现在出的新语言已经慢慢放弃使用它,比如在Java语言中已经不再使用它了,在微软新推出的C#语言中也对此作了一定的限制,所以对于初学者来说应尽量不用或少用。

【语句标号的使用】

在C语言中,语句标号不必特殊加以定义,标号可以是任意合法的标志符,当在标识

106符后面加一个冒号,如:flag1:、stop:,该标志符就成了一个语句标号。注意:在C语言中,语句标号必须是标志符,它的命名规则与变量名相同,即有字母、数字和下划线组成,其第一个字符必须为字母或下划线,不能用整数来作标号,因此不能简单地使用 10:、20:等形式。标号可以和变量同名。

通常,标号用作goto语句的转向目标。如:

goto stop;

在C语言中,可以在任何语句前加上语句标号。例如:

stop: printf("END\n");

补充与扩展

前面我们已经简单介绍了考纲里的一些知识点,下面我们对这些知识点做一些扩充。基于前面的一些知识点都比较简单,这里我们主要讨论输入输出函数的格式说明及其相关的注意事项。

【printf函数中常用的格式说明】

每个格式说明都必须用“%”开头,以一个格式字符作为结束,在此之间可以根据需要插入“宽度说明”、左对齐符号“?”、前导0符号“0”等。

格式字符

允许使用的格式字符和它们的功能如表5-1所示。在一些系统中,这些格式字符只允许用小写字母,因此建议读者使用小写字母,使程序具有通用性。

表5-1 格式字符和它们的功能

格式字符 说 明

c 输出一个字符

d或i 输出带符号的十进制整型数

o 以八进制无符号形式输出整型数(不带前导0)

x或X 以十六进制无符号形式输出整型数(不带前导0x或0X)。对于x用abcdef输出:对于X

用ABCDEF输出

u 按无符号的十进制形式输出整型数

f 以带小数点的形式输出单精度和双精度

e或E 以[-]m.dddddde?xx或[?]m.ddddddE?xx的指数形式输出单精度和双精度。D的个数

由精度指定。隐含的精度为6;若指定的精度为0,小数部分(包括小数点)都不输出g或G 由系统决定采用%f格式还是采用%e格式,以使输出宽度最小

s 输出字符串中的字符,直到遇到“\0”,或者输出由精度指定的字符数

p 输出变量的内存地址

% 打印一个%

长度修饰符

107 长度修饰符加在%和格式字符之间,对于长整型数一定要加l(long);h可用于短整型(shot)或无符号短整型数的输出。

输出数据所占的宽度

当使用%d、%c、%f、%e?格式说明时,输出数据所占的宽度由系统决定(通常取数据本身的宽度,不加空格),并采用右对齐的形式。可以用以下三种方法人为控制输出数据所占的宽度。

1.在%与格式字符之间插入一个整数来指定输出宽度,注意,不能用变量。如果指定的输出宽度不够,并不影响数据的完整输出,系统会代之以隐含的输出宽度;如果指定的输出宽度多于数据实际所需宽度,数据右对齐,左边补以空格。

表5-2列举了未指定宽度和指定输出宽度时的输出结果(从第一例开始)。

表5-2 未指定宽度和指定输出宽度时的输出结果

输出语句 输出结果

printf("%d\n",42); 42

printf("%5d\n",42); □□□42

printf("%f\n",123.54); 123.540000

printf(“%12f\n",123.54); □□123.540000

printf("%e\n",123.54); 1.23450e+02

printf("%13e\n",123.54); □□1.23450e+02

printf("%g\n",123.5); 123.5

printf("%8g\n",123.5); □□□123.5

2.对于float或double类型的实型数可以用“整数1.整数2”的形式在指定宽度的同时来指定小数位的位数。其中,“整数1”用以指定输出数据总的所占宽度,“整数2”称为精度。精度对于不同的格式字符有不同的含义。

对于e、E或f,用以指定输出数据小数位所占位数。当输出数据的小数位多于“整数2”指定的宽度时,截去右边多余的小数,并对截去的第一位小数做四舍五入处理;当输出数据的小数位少于“整数2”指定的宽度时,在小数的最右边添0。当输出数据所占的宽度大于“整数1”指定的宽度时,小数位仍按上述规则处理,整数部分并不丢失。

也可以用“.整数2”的形式来指定小数位的位数。这时输出数据的宽度由系统决定。若指定%.0,则不输出小数点和小数部分。

对于g或G,用来指定输出的有效数字。

对于整数,用来指定必须输出的数字个数,若输出的数字少于“整数2”指定的个数,则在数字前面加0补足;若输出的数字多于“整数2”指定的个数时,按数字的实际宽度输出。

对于字符串,用来指定最多输出的字符个数。

表5-3列举了指定精度时的输出结果。

表5-3 指定精度时的输出结果

输出语句 输出结果

Printf("%.5d\n", 42); 42.00000

108 printf("%.0d\n", 42); 42

(续表)

输出语句 输出结果

printf("%8.3f\n", 123.55); □123.550

printf("%8.1f\n", 123.55); □□□123.6

printf("%8.0f\n", 123.55); □□□□□124

printf("%g\n", 123.56789); 123.568

printf("%.7g\n", 123.5); 123.5679

printf("%.5\n","abcdefg"); abcde

注意:输出数据的实际精度并不取决于格式控制中的域宽和小数的位宽,而是取决于数据在计算机内的存储精度。通常,系统对float类型提供7位有效数字,对于double类型提供15或16位有效数字;格式控制中的域宽和小数的位宽指定的再大也不能改变数据的存储精度,所输出的多余位上的数字是无意义的。

4.输出数据左对齐

可以在指定输出宽度的同时指定数据左对齐。这可在宽度前加一个“?”号来实现。表5-4列举了指定左对齐时的输出结果。

表5-4 指定左对齐时的输出结果

输出语句 输出结果

printf("%6d##\n", 123); □123##

printf("%-6d##\n", 123); 123□□□##

printf("%14.8lf##\n", 1.3455); □□□1.34550000##

printf("%-14.8lf##\n", 1.3455); 1.34550000□□□##

5.使输出的数字总是带有+号或-号

可以在%和格式字符间(或指定的输出宽度前)加一个“+”号来实现。例如:

printf("%+d,%+d\n",10,-10);

输出结果为:+10,?10

6.在输出数据前加前导0

可以在指定输出宽度的同时,在数据前面的多余空格处填以数字0。表5-5列举了加前导0时的输出结果。

表5-5 加前导0时的输出结果

输出语句 输出结果

printf("%6d\n", 12); □□□□12

printf("%06d\n", 12); 000012

printf("%10.5f\n", 3.1415); □□□3.14150

printf("%014.5f\n", 3.1415); 00000003.14150

7.在输出的八进制数前添加0,在输出的十六进制数前添加0x

通常在用格式字符0和x按八进制数和十六进制数的形式输出整数时,在数据的前

109面并不出现0和0x,如果需要在输出的八进制数前添加0,在输出的十六进制数前添加0x,可在%号和格式字符0和x之间插入一个#号(注意:#号对其他格式字符通常不起作用)。

例如:

printf("%o,%#o,%x,%#x\n",10,10,10,10);

从第一列开始输出结果如下:

12,012,a,0xa

【调用printf函数时的注意事项】

在调用printf函数进行输出时需要注意以下几方面。

1.在格式控制串中,格式说明与输出项从左到右在类型上必须一一对应匹配。如不匹配,将导致数据不能正确输出,这时,系统并不报错。特别要提醒读者的是:在输出long整型数据时,一定要使用%ld格式说明,如果遗漏了字母l,只用了%d,将会输出错误的数据。

2.在格式控制串中,格式说明与输出项的个数应该相同。如果格式说明的个数少于输出项的个数,多余的输出项不予输出;如果格式说明的个数多于输出项的个数,则对于多余的格式将输出不定值(或0值)。

3.在格式控制串中,除了合法的格式说明外,可以包含任意的合法字符(包括转义字符),这些字符在输出时将“原样照印”。

4.如果需要输出百分号%,则应该在格式控制串中用两个连续的百分号%%来表示。

5.在输出语句中改变输出变量的值,如:i=5,printf("%d%d\,",i,++i);则不能保证先输出i的值,然后再求i++,并输出。

6.printf函数的返回值通常是本次调用中输出字符的个数。

【scanf函数中常用的格式说明】

每个格式说明都必须用%开头,以一个“格式字符”作为结束。

允许用于输入的格式字符和它们的功能如表5-6所示。在一些系统中,这些格式字符只允许用小写字母,因此建议读者使用小写字母,使程序具有通用性。

表5-6 格式字符及其功能

格式字符 说 明

c 输入一个字符

d 输入十进制整型数

i 输入整型数,整数可以是带前导0的八进制数,带前导0x(或0X)的十六进制数o 以八进制形式输入整型数(可以带前导0,也可以不带前导0)

x 以十六进制形式输入整型数(可以带前导0x或0X,也可以不带)

110 u 无符号十进制整数

(续表)

格式字符 说 明

f(lf) 以带小数点的形式或指数形式输入单精度(双精度)数

e(lf) 与f(lf)的作用相同

s 输入字符串

说明:

1.在Turbo C环境下输入long整数时,在%和d之间必须加l;输入double型数时,在%和f(e)之间也必须加l。否则得不到正确的数据。

2.在格式控制中,格式说明的类型与输入项的类型,应该一一对应匹配。如果类型不匹配,系统并不给出出错信息,但不可能得到正确的数据。

3.在scanf函数中的格式字符前可以用一个整数指定输入数据所占宽度。但不可以对实型数指定小数位的宽度。

4.在格式控制串中,格式说明的个数应该与输入项的个数相同。若格式说明的个数少于输入项的个数时,scanf函数结束输入,多余的数据项并没从终端接受新的数据;若格式说明的个数多于输入项的个数时,scanf函数同样也结束输入。

5.当输入的数据少于输入项时,程序等待输入,直到满足要求为止。当输入的数据多于输入项时,多余的数据并不消失,而是留作下一个输入操作时的输入数据。

6.scanf函数在调用结束后将返回一个函数值,其值等于得到输入值的输入项的个数。

【通过scanf函数从键盘输入数据】

当调用scanf函数从键盘输入数据时,最后一定要按下回车键(Enter键),scanf函数才能接受从键盘输入的数据。

1.输入数值数据

从键盘输入数值数据时,输入的数值数据之间用间隔符(空格符、制表符(Tab 键)或回车符)隔开,间隔符数量不限。如果在格式说明中人为指定宽度时,也同样可用此方式输入。例如,假设a、b、c为整型变量,若有以下输入语句:

scanf("%d%d%d",&a,&b,&c);

要求给a赋予10、给b赋予20、给c赋予30,则数据输入形式应当是:

<间隔符>10<间隔符>20<间隔符>30↙

此处<间隔符>可以是空格符、制表符(Tab键)或回车符,“↙”表示Enter键。

2.指定输入数据所占宽度

可以在格式字符前加一个整数,用来指定输入数据所占宽度。

当输入数值数据时,一些C编译系统并不要求必须按指定的宽度输入数据,用户可以像未指定宽度时一样的方式输入。

111 3.跳过输入数据的方法

可以在格式字符和“%”之间加一个“*”号,它的作用是跳过对应的输入数据。

例如:

int a1,a2,a3;

scanf("%d%*d%d%d",&a1,&a2,&a3);

当输入以下数据时:

10□□20□□30□□40↙

将把10赋给a1,跳过20,把30赋给a2,把40赋给a3。

4.输入的数据少于scanf函数要求输入的数据

这时scanf函数将等待输入,直到满足要求或遇到非法字符为止。

5.输入的数据多于scanf函数要求输入的数据

这时,多余的数据将留在缓冲区作为下一次输入操作的输入数据。

6.在格式控制串中插入其他字符

读者首先应该明确:scanf函数中的格式控制串是为输入数据用的,其间的字符不能输出到屏幕上,因此,如果想在屏幕上输出字符串来提示输入,应该另外使用printf函数。

若在scanf的格式控制串中插入了其他字符,则在输入时要求按一一对应的位置原样输入这些字符。例如:

int a1,a2,a3;

scanf("Input a1,a2,a3:%d%d%d",&a1,&a2,&a3);

要求按以下的形式输入:

Input a1,a2,a3: 10 20 30

第1列

注意,这里的“Input a1,a2,a3:”是用户输入的。字符的大小写、字符间的空格数必须和scanf的格式控制串中插入的字符串完全一致。又如:

scanf("%d,%d,%d",&a1,&a2,&a3);

逗号紧跟在格式字符之后,因此要求在每个输入数据之后紧跟一个逗号,以下输入的数据能正确读入:

10,20,30

以下输入的数据也能正确读入:

10, 20, 30

以下输入的数据就不能正确读入,因为逗号没有紧跟在每个输入数据之后:

10 , 20 ,30

如果逗号不是紧跟在格式字符之后,如:

scanf("%d ,%d ,%d",&a1,&a2,&a3);

112则只要求在输入数据之间插入逗号即可,因此以上的各种输入形式都能正确输入。

113 典 型 例 题

【例题5-1 表达式语句】

题干

下列语句中,符合语法的赋值语句是( )。

A)a=7+b+c=a+7; B)a=7+b++=a+7;

C)a=7+b,b++,a+7 D)a=7+b,c+a+7;

解题方法

在分析本题时应该注意:一般的表达式是不能放在赋值号左边的。

在选项A)中,表达式7+b+c=a+7是非法赋值。选项B中的7+b++=a+7是非法赋值。由于 C 语言的语句结束标志必须是分号,因此选项 C 不是语句,C 中的表达式相当于(a=7+b),(b++),(a+7),符合语法,但最后的a+7代码无实际作用,C语言会将其归为警告类错误,如果加上分号,C也是正确语句。选项D中的语法相当于

(a=7+b),(c=a+7),此语句实现了对两个变量a和c的赋值。

知识点分析

赋值运算:赋值运算是指将一个数值存储到某个内存单元的操作,使用形式为:左值=右值。这里,右值是一个普通表达式,但左值必须是一个可以寻址的表达式。实际使用时,左值通常只有两种,赋值形式为“变量名=右值”和“*地址=右值”。

表达式地址:虽然一个表达式的值是要存储的,但其存储地址不可知。例如,类似&(++x)之类的运算总是非法的。

赋值表达式的值:赋值表达式的值等于经过赋值后的左值,其实,也就是右边表达式的值。

逗号运算:逗号运算符就是“,”,使用形式为“表达式1,表达式2”。逗号表达式的值等于表达式2的值。一般,逗号表达式的主要功能体现在将小表达式连接成大表达式,而逗号表达式的值很少使用。一个逗号表达式的数据类型与表达式2的数据类型相同。

赋值与逗号运算的优先级别:赋值与逗号运算都是优先级别很低的运算,且逗号运算是C语言中优先级别最低的运算。

答案

D

难度提示

简单。这道题主要考查应试者关于基本语句的了解情况,以及相关的赋值运算和运算的优先级别的一些情况。

114【例题5-2 基本语句】

题干

下列语句中,正确的语句是( )。

A)int x=y=z=0; B)int z=(x+y)++;

C)x=+3==2; D)x%=2.5;

解题方法

首先,关于C语言的变量初始化应注意同其他语言相区别,尽管若干个变量具有相同的初值,但不允许以A中的方式进行初始化。下述两种方式都是允许的:

int x=0,y=0,z=0;

和int x,y,z;

x=y=z=0;

其次,在选项B中,尽管不知道变量x和y的类型,但x+y总是不可寻址的,对x+y做++运算是非法的。

对于选项D,运算符%要求运算数是整数,而2.5是浮点数,因此,x%=2.5是错误的运算。

至于选项C,运算==的优先级高于表达式x=(+3==2)。因为+和-都可以作为单目运算符使用,+3是正确的表达式,故整个赋值语句正确。

知识点分析

变量初始化:在变量定义时对变量赋值必须针对每个变量进行,即使有几个变量具有相同的初始化值也如此。

++运算的赋值特点:++是一种具有算术运算和赋值运算特点的运算,因此,能够进行此种运算的表达式必须是可寻址的。

答案

C

难度提示

简单。这道题主要考查应试者关于基本语句的了解情况。

【例题5-3 自反赋值语句】

题干

下述程序的输出结果是( )。

程序5-4:P5-4.c

#include <stdio.h>

115 main( )

{int a=2;

a%=4-1;

printf("%d,",a);

a+=a*=a-=a*=3;

printf("%d",a);

}

A)2,0 B)1,0;

C)–1,12 D)2,12;

解题方法

因为“%=”运算的优先级别低于“-”运算,a%=4-1即是a%=3,等价于a=a%3=2%3=2,可见B和C是错误的。正确的只能是A或D。

尽管表达式a+=a*=a- =a*=3表面上很复杂,计算时注意到赋值类表达式的值和变量值随时被更新,就很容易计算出正确的结果。根据赋值类运算符由右至左的结合性,我们将其展开计算。开始时,a=2。表达式a*=3使得a值为6,此表达式的值也为6。于是,表达式a- =a*=3相当于a- =6,使得a=a-6=6-6=0。至此,后面的表达式已不必继续计算,最终a=0。

知识点分析

自反赋值运算:自反赋值运算符的形式是△=,这里的△是任何一个双目运算符。此类赋值运算符与基本赋值运算使用形式相同,即:左值△=右值,但其含义是:左值=左值△右值。例如,x+=1等同于x=x+1。

自反赋值表达式的值等于经过赋值后的左值,此时,它一般不等于右边表达式的值。例如,若x=3,表达式x+=3的值是6而不是3。

自反赋值运算与基本赋值运算的优先级别相同,结合性相同,都是由右向左结合。

答案

A

难度提示

中级。这道题主要考查应试者关于自反赋值的运算过程的了解。

【例题5-4 左对齐数据的输出】

题干

对下述程序段正确的描述是( )。(注:题中的□表示空格)

int x=1234;

printf("ABS(X)=|%-08d|",x);

A)输出为ABS(x)=| -0001234| B)输出为ABS(x)=|0001234|

C)输出为ABS(x)=|1234□□□| D)输出格式描述非法,无输出

解题方法

此函数本身检查和纠错的能力较低,函数中所涉及到的细节也较多,一般只了解常见

116的用法即可。并且,可以根据一些常识去分析其中参数的搭配、优先权等问题。

就本例来说,除描述项%?08d外,皆属普通输出字符,而描述项中,同时使用了?和08,其中08表示数据占8位宽输出,左端空位填0。-号表示数据左对齐输出,此时,左端无空位,因此输出是1234□□□□。那么,右端的空格能否填以0呢?填一下,是1234000,可以看出这是不允许的。因此,本题应选C。选项A和B的错误都是因不了解“?”号的作用所引起。

知识点分析

详细情况可参考前面我们所介绍的“补充与扩展”里的相关知识。

答案

C

难度提示

中级。这道题主要考查应试者关于输出函数printf的左对齐参数的了解。

【例题5-5 数据的一般输出】

题干

以下程序的结果是( )。

程序5-5:P5-5.c

#include <stdio.h>

main( )

{int a=1234;

float b=123.456;

double c=12345.54321;

printf("\n%2d,%2.1f,%2.1lf",a,b,c);

}

A)输出格式中位数不够,无输出

B)输出结果为12,12.4,12.5

C)输出结果为1234,123.5,1234.5

D)输出结果为1234,123.4,1234.5

解题方法

类似选项A的答案在输出时基本不可能发生。本例中数据的整数部分限制宽度都不够,相关知识可知,整数部分将按实际数据输出,可见B和D错误。

知识点分析

printf函数的浮点数默认输出格式:在printf函数的输出中,若无输出宽度限制,每种数据都有一个默认的输出宽度,一般浮点数的小数位数则是 6位,不管输出格式是%f或%lf皆如此。

printf函数的浮点数宽度限制输出:以%mf或%mlf格式输出浮点数时,如果指定的宽

117度大于实际数据宽度,按指定宽度输出,且多余位数补以空格;如果指定的宽度小于实际数据宽度,浮点数的整数部分将以实际数据(位数)输出,小数部分按指定位数输出,且对数据做四舍五入处理。

printf的整数限宽输出:没有宽度限制的整数原数输出。在宽度限制小于数的实际位数时,宽度说明无效,按数的实际位数输出。

答案

C

难度提示

中级。这道题主要考查应试者关于输出函数printf格式字符的了解程度。对整数形式、浮点数形式、长整型等输出形式及规定小数点后面位数的问题,读者应该有较清楚的了解。

【例题5-6 带有数制转换数据的输出】

题干

下述程序的输出是( )。

程序5-6:P5-6.c

#include <stdio.h>

main()

{int a=011,b=101;

printf("\n%x,%o",++a,b++);

}

A)12,145 B)9,145

C)a,145 D)a,5

解题方法

一般涉及++运算的问题中所体现的总是该运算的能力和表达式值这两方面问题,一是能否了解在x++和++x运算后,变量x的值都加1,二是在使用表达式值时是否能够正确理解。另外要注意的细节是011是八进制数,即十进制的9,101是普通的十进制数而非八进制数。在输出时,两个表达式分别以十六进制和八进制输出。表达式++a的值是a加1后的值,等于9+1=10,转换为十六进制则是a。表达式b++的值是b的原值,为101,转换为八进制则为145。

知识点分析

++和--运算:即自加和自减运算。这两个运算具有赋值功能,因此,运算数必须是变量而不能是不可寻址的表达式。以++运算为例,x++或++x运算之后都使变量x的值增加1,但要注意这两个表达式的值不同:表达式x+的值是x增1之前的原值,表达式++x的值为x增1后的新值。或者说,对表达式x++求值时,x并不增1,而是在对其求值后增1,++x恰好相反。

118 答案

C

难度提示

中级。这道题不但考查应试者关于输出函数的相关格式的了解,还考查应试者对数制转换的了解程度。

【例题5-7 数据的输入】

题干

假设预先定义变量如下:

int m;

char cx;

若从键盘输入整数30和字符'A'分别赋给变量m和cx,( )中的输入语句及相应的输入数据是正确的。

A)scanf("%d%d%c", &m, &cx); B)scanf("%2c%2d", &cx, &m);

输入数据:10□30□A↙ 输入数据:BA30↙(或BA□30↙)

C)scanf("%2c%2d", &cx,&m); D)scanf("%2c; %*2d%2d", &cx, &m);

输入数据:AB□30□10↙ 输入数据:AB;1030↙

解题方法

在选项A)中,描述项多于地址项,scanf函数将按由前到后的顺序把输入数据存放到各地址中,这与printf的处理方法类似。因此,按顺序m得到值10,cx得到值30,多余的数据则形成“缓冲区垃圾”。有时,这种描述项多于地址项的语句也会出现“Null pointerassignment”的错误。无论如何,A)不正确。

选项A)中存在一个值得探讨的小问题。cx是字符变量,%d与char类型不匹配,cx能得到数据30吗?事实上,此处的cx用作整型变量,只要输入的数据不超过范围(一个字节),cx得到的值即是正确的。

现在,我们将输入语句修改如下:

scanf("%d%c%c",&m,&cx);

输入的数据仍为“10口30口A↙”,那么,cx得到的值又是多少呢?

其实,scanf虽然可以用空格符或制表符(<Tab>)分隔输入的数据,但在下一个输入项是字符时,就不能用空格符和制表符分隔,否则,该字符就成了输入数据。因此,在上述语句和数据输入情况下,cx的值是空格符而不是30。

对于选项B),通常,字符格式输入时很少指定域宽,若已经指定,第一个输入字符将赋给相应的变量,其他输入的字符毫无用处。此选项中,cx得到的值是字符‘B’,故选项B)错误。

在选项C)中,由于scanf的输入格式中使用的字符“*”,变量cx得到的值为‘A’,

11930被跳过,而变量m得到值10,故此选项也错误。

选项D)中,格式描述项有三个:%2c,%*2d和%d,多余的“;”是普通字符,按次序原样输入即可。对于输入的数据“AB;1030”,其中的“AB”对应格式%2c,使变量cx得到字符‘A’,“;”对应格式控制中的“;”。根据2位域宽,“10”被跳过。最后的“30”赋给变量m,故D)是正确的。

知识点分析

scanf函数的数据分隔:scanf函数中如何分隔输入数据是初学者应该注意的问题。分隔数据主要有以下几种方法。

1.指定宽度:如果使用格式scanf("%2d%3f%f",&x,&y,&z);,而输入数据是连续的,如“12345678↙”,此时x=12,y=345,z=678。

2.默认分隔符:如果使用格式 scanf("%d%f",&x,&y);,输入的两个数据之间可以用空格、Tab符或回车符分隔。

3.依赖数据差别:如果同时输入整数(或浮点数)和字符(或字符串),可以依赖其本身的差别由系统自动识别。若使用格式:scanf("%d%c%f",&x,&y,&z);,连续输入数据“12A34.5↙”,则x=12,y='A',z=34.5。

4.使用普通字符:scanf 函数中的普通字符需要原样输入,可以理解成分隔符。如使用格式scanf(“%d,%d”,&x,&y);,则输入数据的方式应该是“12,34↙”。

scanf函数族中的“*”控制符:scanf 的输入格式中允许使用一个特殊的字符“*”,即输入抑制符,该字符的控制作用是“跳过”一个对应的输入数据项。应该说,scanf函数中的“*”是整个函数族的产物,对该函数来说,很难找到其实际的用处。

答案

难度提示

较难。这道题主要考查应试者关于输入函数scanf格式字符的了解程度。

本 章 练 习

【选择题】

1.以下选项中不是C语句的是( )。

A){int i; i++; printf(“%d\n”,i);} B);

C)a=5,c=10 D){ ; }

2.下列语句中符合C语言语法的赋值语句是( )。

A)x=(3+b,z)=x+3; B)x=7+y,y++,z++;

C)x=y+2=x+y+z; D)x=3+y++=x+3;

3.若a、b、c、d都是int类型变量且初值为0,以下选项中不正确的赋值语句是(

A)a=b=c=100; B)d++;

120 C)c+b; D)d=(c=22) ? (b++);

4.以下合法的C语言赋值语句是( )。

A)a=b=58 B)k=int(a+b);

C)a=58,b=59 D)-i;

5.下面各语句行中,能正确进行赋字符串操作的语句行是( )。

A)char st[4][5]={"ABCDE"};

B)char s[5]={'A','B','C','D','E'};

C)char *s;s="ABCDEF";

D)char *s;scanf("%s",s);

6.若变量已正确定义,要将a和b中的数进行交换,下面不正确的语句组是( )。

A)a=a+b,b+a?b,a=a?b; B)t=a,a=b,b=t;

C)a=t;t=b;b=a; D)t=b;b=a;a=t;

7.以下程序的输出结果是( )。

main()

{int x=10,y=3;

。 )

printf("%d\n",y=x/y);

}

A)0 B)1

C)3 D)不确定的值

8.执行下述程序片段时的输出结果是( )。

int x,y;

x=13;

y=5;

printf("%d",x%=(y/=2));

A)3 B)2

C)1 D)0

9.正确评价下述程序的输出结果的是( )。

#include <stdio.h>

main( )

{printf("%d",null);

}

A)输出0 B)变量无定义,编译出错,没有输出

C)输出-1 D)输出1

10.已知字符 'a' 的ASCII码为97,则下述程序( )。

char ch='a';

int k=12;

printf("%x,%o,",ch,k);

printf("k=%%d",k);

A)因变量类型与格式描述符不匹配,输出不定值

B)输出项与描述项个数不符,输出为0值或不定值

121 C)输出为61,14,k=%d

D)61,14,k=%12

11.执行下述程序片段时输出的结果是( )。

t x=0xdef;

printf("\n%4d\n",x);

printf("%4o\n",x);

printf("%4x\n",x);

A) 3567 B) 3567 C) 3567 D) 3567

6757 6757 06757 06757

def xdef 0xdef 0def

12.执行下述程序片段时输出的结果是( )。

float x=-1023.012;

printf("\n%8.3f,"x);

printf("%10.3f",x);

A)1023.012,□□?1023.012 B)-1023.012,–1023.012

C)1023.012,□?1023.012 D)-1023.012,□-1023.012

13.下述程序的输出结果是( )。

#include <stdio.h>

main( )

{

int x=023;

printf("%d",--x);

}

A)17 B)18

C)23 D)24

14.下述程序的输出结果是( )。

main( )

{

int k=11;

printf("k=%d,k=%o,k=%x\n",k,k,k);

}

A)k=11,k=12,k=11 B)k=11,k=13,k=13

C)k=11,k=013,k=0xb D)k=11,k=13,k=b

15.已知字符'a'的ASCII码值是97,则执行下述程序片段时的输出是( )。char a='a';

a--;

printf("%d,%c",a+'2'-'0',a+'3'-'0');

A)b,c B)98,99

C)98,c D)格式描述与输出项不匹配,输出值不定16.下述程序的输出结果是(

122 int x=1;

y=++x;

z=x++;

。 )

printf("%d,%d,%d",x,y,z);

A)3,2,2 B)3,2,3

C)2,2,2 D)2,2,1

17.有如下定义:

float x;

unsigned y;

则( )是合法的输入语句。

A)scanf("%5.2f%d",&x,&y);

B)scanf("%f%3o",&x,&y);

C)scanf("%f%n",&x,&y);

D)scanf("%f%f",&x,&y);

18.对于下述语句,若将10赋给变量k1和k3,将20赋给变量k2和k4,则应按方式(

int k1,k2,k3,k4;

scanf("%d%d",&k1,&k2);

scanf("%d,%d",&k3,&k4);

A) 1020↙ B) 10□20↙

1020↙ 10□20↙

C) 10,20↙ D)10□20↙

10,20↙ 10,20↙

19.对下述程序,( )是正确的判断。

程序5-7:P5-7.c

#include<stdio.h>

)输入数据。

main()

{

int x,y;

scanf("%d,%d",&x,&y);

if(x>y)

x=y;

y=x;

else

x++;

y++;

printf("%d,%d",&x,&y);

}

A)有语法错误,不能通过编译

B)若输入数据3和4,则输出4和5

C)若输入数据4和3,则输出3和4

D)若输入数据4和3,则输出4和4

123【填空题】

1.阅读下述程序,说明其输出结果,请填空。

#include <stdio.h>

void main( )

{

int a=3,b=4;

printf("%d\n",a=a+1,b+a,b+1); 输出结果__(1)__

printf("%d\n",(a=a+1,b+a,b+1)); 输出结果__(2)__

}

2.若想通过以下输入语句使a=5.0,b=4,c=3,则输入数据的形式应该是______。

int b,c;

float a;

scanf("%f,%d,c=%d",&a,&b,&c);

3.若想通过以下输入语句使a中存放字符串1234,b中存放字符5,则输入数据的形式应该是______

char a[10],b;

scanf("a=%sb=%c",&a,&b);

4.执行下面程序中的输出语句后,输出的结果是______。

main()

{

int a=5;

printf("%d\n",(a=3*5,a*4,a+5));

}

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

124 第 6 章 选择结构程序设计

考纲要求

1.用if语句实现选择结构。

2.用switch语句实现多分支选择结构。

3.选择结构的嵌套。

知识点讲析

【用if语句实现选择结构】

C语言中的if语句有两种基本形式:

不含else子句的if语句

语句形式如下:

if(表达式)

语句

例如:

if(x>y)

printf("%d",x);

其中,if是C语言的关键字,表达式两侧的圆括号不可少,最后是一条语句,称为if子句。如果在if子句中需要多个语句,则应该使用花括号把一组语句括起来组成复合语句,这样在语法上仍满足“一条语句”的要求。

含else子句的if语句

语句形式如下:

if(表达式)

语句1

else

语句2

例如:

if(x>y)

printf("%d",x);

else

125 printf("%d",y);

在这里,“语句1”称为if子句,“语句2”称为else子句,这些子句只允许是一条语句,若需要多条语句时,则应该使用花括号把这些语句括起来组成复合语句。

读者应该注意:else不是一条独立的语句,它只是if语句的一部分,不允许有这样的语句:

else printf("***");

在程序中else必须与if配对,共同组成一条if-else语句。

【用switch语句实现多分支选择结构】

switch语句是多分支选择语句。语句形式如下所示。

switch(表达式)

{ case 常量表达式1:语句1

case 常量表.达式2:语句2

.

case 常量表达式n:语句n

default :语句n+1

}

说明

1.switch是关键字,switch语句后面用花括号括起来的部分称为switch语句体。

2.紧跟在switch后一对括号中的“表达式”可以是整型表达式及后面将要学习的字符型或枚举型表达式等。表达式两边的一对括号不能省略。

3.case也是关键字,与其后面的常量表达式合称case语句标号。常量表达式的类型必须与swicth后的表达式类型相同。各case语句标号的值应该互不相同。

4.default也是关键字,起标号的作用。代表所有case标号之外的那些标号。default标号可以出现在语句体中任何标号位置上。在switch语句体中也可以没有default标号。

5.case语句标号后的语句1,语句2,等等,可以是一条语句,也可以是若干语句。

6.必要时,case语句标号后的语句可以省略不写。

7.在关键字case和常量表达式之间一定要有空格,例如case 10:不能写成case10:。【选择结构的嵌套】

前面我们介绍了用if语句实现的选择结构和用switch语句实现的多分支选择结构,接下来我们来介绍一下选择结构的嵌套。if和else子句可以是任意合法的C语句,因此当然也可以是if语句,通常称此为嵌套的if语句。内嵌的if语句既可以嵌套在if子句中,又可以嵌套在else子句中。

在if子句中嵌套具有else子句的if语句

语句形式如下:

126 if(表达式1)

if(表达式2)

语句1

else

语句2

else

语句3

当表达式1的值为非0时,执行内嵌的if-else语句;当表达式1的值为0时,执行语句3。

在if子句中嵌套不含else子句的if语句

语句形式如下:

if(表达式1)

{

if(表达式2)

语句1

}

else

语句2

注意:在if子句中的一对花括号不可缺少。因为C语言的语法规定:else子句总是与前面最近的不带else的if相结合,与书写格式无关。因此以上语句如果写成:

if(表达式1)

if(表达式2)

语句1

else

语句2

实质上等价于:

if(表达式1)

if(表达式2)

语句1

else语句2

当用花括号把内层if语句括起来后,使得内层if语句在语法上成为一条独立的语句,从而使得else与外层的if配对。

在else子句中嵌套if语句

语句形式如下:

1.嵌套if语句带有else: 2.嵌套if语句不带else:

if(表达式1) if(表达式1)

语句1 语句1

else else

if(表达式2)语句2 if(表达式2)语句2

else语句3

或写成: 或写成:

if(表达式1) if(表达式1)

语句1 语句1

127 else if(表达式2) else if(表达式2)

语句2 语句2

else 语句3

由以上两种语句形式可以看到,内嵌在else子句中的if语句无论是否有else子句,在语法上都不会引起误会,因此建议读者在设计嵌套的if语句时,尽量把内嵌的if语句嵌在else子句中。

C语言程序有比较自由的书写格式,但是过于“自由”的程序书写格式,往往使人们很难读懂,因此要求读者参考本书例题程序中按层缩进的书写格式来写自己的程序。

不断在else子句中嵌套if语句可形成多层嵌套。如:

if(表达式1)

语句1

else

if(表达式2)

语句2

else

if(表达式3)

语句3

.

else

语句n

这时形成了阶梯形的嵌套if语句,此语句可用以下语句形式表示,使得读起来既层次分明又不占太多的篇幅。

if (表达式1)

语句1

else if(表达式2)

语句2

else if(表达式3)

语句3

.

else

语句n

以上形式的嵌套if语句执行过程可以这样理解:从上向下逐一对if后的表达式进行检测。当某一个表达式的值为非0时,就执行与此有关子句中的语句,阶梯形中的其余部分就被越过去。如果所有表达式的值都为0,则执行最后的else子句;此时,如果程序中最内层的if语句没有else子句,即没有最后的那个else子句,那么将不进行任何操作。

补充与扩展

【if语句的执行过程】

128 首先计算紧跟在if后面一对圆括号中的表达式的值,如果表达式的值为非0(“真”),则执行其后

的if子句,然后去执行if语句后的下一个语句。如果表达式的值为0(“假”),则跳过if子句,直接执行if语句后的下一个语句。

流程图如图6-1所示。

假(0)

表达式

真(非0)

语句

图6-1 if语句的执行过程

下面我们举例说明if语句的执行过程。

例如:输入两个数,分别放入x和y中,若两数不相等,则输出其中的大数;若两数相等,则输出字符串“x==y”和x的值。

程序6-1:P6-1.c

main()

{ int x,y;

printf("Enter x&y:\n");

scanf("%d,%d",&x,&y);

if(x>y)

printf("x=%d\n",x);

if(y>x)

printf("y=%d\n",y);

if(x==y)

printf("x==y,x=%d\n",x);

}

本题是一个应用if语句的简单程序,其执行过程如下所示。

1.prinif语句在屏幕上显示了提示信息:Enter x&y:之后,scanf语句等待用户给变量x、y输入两个整数,然后把输入的两个数显示在屏幕上。

2.执行第5行的if语句。计算表达式x>y的值。如果x大于y,表达式的值为1,则调用printf函数输出x的值;否则,如果x小于或等于y,表达式的值为0,则跳过此输出语句继续执行步骤3。

3.执行第6行的if语句。计算表达式y>x的值;如果y大于x,则调用printf函数输出y的值;否则跳过此输出语句继续执行步骤4。

4.执行第7行的if语句。计算表达式x==y的值;如果x等于y,则调用printf函数输出x的值;否则跳过此输出语句继续执行步骤5。

5.程序结束。

【if-else语句的执行过程】

129 首先计算紧跟在if后面一对圆括号内表达式的值。如果表达式的值为非0,执行if子句(即语句1),然后跳过else子句(语句2),去执行if语句后的下一条语句;如果表达式的值为0,跳过if子句,去执行else子句,接着去执行if语句后的下一条语句。

流程图如图6-2所示。

真 表达式 假

语句1 语句2

图6-2 if-else语句的执行过程

例如:输入两个不等的整数分别给x和y,输出其中的大数。

程序6-2:P6-2.c

main()

{ int x,y;

printf("Enter x&y:");

scanf("%d,%d",&x,&y);

printf(“x,y:%d,%d\n",x,y);

if(x>y)

printf("max=x=%d\n",x);

else

printf("max=y=%d\n",y);

printf("**end**");

}

当执行以上程序时,若把5输入给变量x,把3输入给变量y;则if后表达式x>y的值为1,因此,将执行if子句,输出x的值;然后去调用最后一行中的printf函数,输出字符串:**end**。

如果给x输入3,给y输人5,这时if语句中表达式x>y的值为0,因此将跳过if子句,直接执行else分支中的else子句,输出y的值;然后执行最后一行中的语句,输出字符串:**end**。

【if语句的补充说明】

1.if后面圆括号中的表达式,可以是任意合法的C语言表达式(如:逻辑表达式、关系表达式、算术表达式、赋值表达式等),也可以是任意类型的数据(如:整型、实型、字符型、指针型等)。

2.无论是否有else子句,if子句中如果只有一条语句,则此语句后的分号不能省略。如:

if(x!=0)

printf("%f",x);

130else

printf("%f",y);

131【switch语句的执行过程】

当执行switch语句时,首先计算紧跟其后一对括号中的表达式的值,然后在switch语句体内寻找与该值吻合的case标号,如果有与该值相等的标号,则执行该标号后开始的各语句,包括在其后的所有case和default中的语句,直到switch语句体结束。如果没有与该值相等的标号,并且存在default标号,则从default标号后的语句开始执行,直到switch语句体结束,如果没有与该值相等的标号,且不存在default标号,则跳过switch语句体,什么也不做。

例如:用switch语句编写程序,根据输入的学生成绩,给出相应的等级。90分以上的等级为A,60分以下的等级为E,其余每10分为一个等级。

程序如下:

程序6-3:P6-3.c

main()

{ int grade;

printf("Enter a mark: ");

scanf("%d",&grade); /*grade中存放学生的成绩*/

printf("grade=%d:",grade);

switch(grade/10)

{

case 10:

case 9: printf("A\n");

case 8: printf("B\n");

case 7: printf("C\n");

case 6: printf("D\n");

default: printf("E\n");

}

}

当执行以上程序输入了一个85分的学生成绩后,接着执行switch语句,首先计算switch后一对括号中的表达式:85/10,它的值为8;然后寻找与8吻合的case 8分支,开始执行其后的各语句。程序的输出结果如下所示

grade=85:B

C

D

E

在输出了与85分相关的B之后,又同时输出了与85分毫不相关的等级C、D、E,这显然不符合原意。为了改变这种多余输出的情况,switch语句常常需要与break语句配合使用。【switch语句中的break语句】

break语句也称间断语句。可以在case之后的语句最后加上break语句,每当执行到

132break语句时,立即跳出switch语句体。switch语句通常总是和break语句联合使用,使得switch语句真正起到分支的作用。

现用break语句修改的程序6-3,如下:

程序6-4:P6-4.c

main()

{ int grade;

printf("Enter a mark: ");

scanf("%d",&grade); /*grade中存放学生的成绩*/

printf("grade=%d:",grade);

switch(grade/10)

{

case 10;

case 9: printf("A\n"); break;

case 8: printf("B\n"); break;

case 7: printf("C\n"); break;

case 6: printf("D\n"); break;

default: printf("E\n");

}

}

流程图如图6-3所示。

grade

90-100 80-89 70-79 60-69 其他

输出 输出 输出 输出 输出

A B C D E

图6-3 程序流程图

程序执行过程如下所示。

1.当给grade输入100时,switch后一对括号中的表达式:grade/10的值为10。因此选择case 10分支,因为没有遇到break语句,所以继续执行case 9分支,在输出:grade=100:A之后,遇break语句,执行break语句,退出switch语句体。由此可见,成绩90~100分,执行的是同一分支。

2.当输入成绩为45时,switch后一对括号中表达式的值为4,将选择default分支,在输出grade=45:E之后,退出switch语句体。

3.当输入成绩为85时,switch后一对括号中表达式的值为8,因此选择case 8分支,

133在输出grade=85:B之后,执行break语句,退出switch语句体。

典 型 例 题

【例题6-1 基本if语句】

题干

对下述程序,( )是正确的判断。

程序6-5:P6-5.c

#include<stdio.h>

main()

{ int x,y;

scanf("%d,%d",&x,&y);

if(x>y)

x=y;y=x;

else

x++;y++;

printf("%d,%d",x,y);

}

A)有语法错误,不能通过编译

B)若输入数据3和4,则输出4和5

C)若输入数据4和3,则输出3和4

D)若输入数据4和3,则输出4和4

解题方法

不管if语句中的条件为真或假,只能执行一个语句,而程序中的x=y; y=x; 违反了这一点,故选项A是正确的判断。改正的办法是将多个语句合成一个复合语句。即在x=y;y=x;的基础上加上花括号{x=y; y=x;}。

知识点分析

if语句可称为条件语句或分支语句,其基本形式只有两种,即含else与不含else子句的两种情况,前面我们已经有相关的介绍了,在这里就不再重述了。

if语句中else与if的搭配关系:复杂的if语句中可能有许多个if和else,其配对的原则是:一个else应与距离最近且没有与其他else配对的if搭配使用。

这里再指出一点:if语句的结构简单,试题通常集中在格式、错误和不良编程习惯上。例如,可能使用了如下形式的if语句:

if(x>0);

y=x;

这里,if之后的分号是多余的。也有在程序段中漏掉if或else部分的语句后面的分号

134的情况。

答案

A

难度提示

简单。这道题主要考查应试者关于if语句的了解情况。在本题中,命题者利用考生在答题时紧张的情绪,出了一个比较隐蔽的错误,只要考生作过相关的题目,并且在考试的时候仔细审题,这种试题应该是必得的。

【例题6-2 if语句1】

题干

以下程序的输出为( )。

int a,b,c;

a=10;

b=50;

c=30;

if(a>b) a=b,

b=c;

c=a;

printf("a=%d,b=%d,c=%d",a,b,c);

A)a=10,b=50,c=10 B)a=10,b=30,c=10

C)a=50,b=30,c=10 D)a=50,b=30,c=50

解题方法

在回答此题时应先注意到“a=b, b=c;”是一个语句,将原程序按正常格式改写后就一目了然了。

int a,b,c;

a=10;b=50;c=30;

if(a>b)

a=b,b=c;

c=a;

printf("a=%d,b=%d,c=%d",a,b,c);

可见,因a>b为假,if语句什么都不做。再注意到语句“c=a;”与if语句无关,总要执行。所以,程序执行后,a,b值不变,c值为10。

知识点分析

在这题中主要用到了if语句和逗号表达式的相关内容。

答案

A

135难度提示

简单。这道题主要考查应试者关于if语句和逗号表达式的了解情况。

136【例题6-3 if语句2】

题干

下面程序的输出结果是( )。

程序6-6:P6-6.c

#include<stdio.h>

main( )

{ int x=1000,a=10,b=20;

int v1=5,v2=0;

if(a<b)

if(b!=15)

if(!v1)

x=1;

else

if(v2) x=10;

x=-1;

printf("%d",x);

}

A)100 B)?1 C)1 D)10

解题方法

本题与上一题类似,只要注意到语句“x= -1;”是无条件执行的,不必考虑前面那些复杂的if结构。这些语句都只是“幌子”。

知识点分析

相关的if语句知识。

答案

B

难度提示

简单。这道题主要考查应试者的审题仔细程度。

【例题6-4 if语句中的条件表达式】

题干

对下述程序段正确的描述是( )。

程序6-7:P6-7.c

#include <stdio.h>

main()

{ int x=0,y=0,z=0;

137 if(x=y+z)

printf("***");

else

printf("###");

}

A)有语法错误,不能通过编译 B)输出***

C)可以编译,但不能通过连接,因而不能运行 D)输出###

解题方法

在C语言中,比较容易用混的运算是“=”和“==”,尤其在if语句中更是如此。本题中,y+z值为0。因此,表达式x=y+z使变量x的值为0,此赋值表达式的值也为0,逻辑含义为假。所以,程序应执行if结构中else之后的语句,输出为“###”。

知识点分析

if语句,赋值运算 = 与关系运算 == 的差别。在这里再指出一点:试题中经常出现一些混用的相近运算,而其代码又很简单,应多加注意。这样的运算主要包括 = 和 ==,&&和 & 及 || 和 |。

答案

D

难度提示

简单。只要读者作了相关的题目之后,这类题不应该是丢分的题。

【例题6-5 switch语句的基本形式】

题干

下述程序段中,正确的是( )。

A)int x=0,y=10; B)int x=0,y;

switch(x) switch(x)

{ case y: x++;break; { case x>0: y=1;break;

case y+2: x+=10;break; case x==0: y=0;break;

case y-1: x=-7;break; case x<0: y=-1;break;

} }

C)#define y 20 D)int x=0,y;

int x=10,z; switch(x)

switch(x) { case 3:

{ case 12: z=3;break; case –1: y=2;break;

case y+1: x+=10;break; case 2: break;

case y-8:y-=3;break; }

}

138 解题方法

在本题中没有具体运算内容,本题只能考核switch语句的语法现象。前三个选项中各出现了一个错误。在选项A中,case后的表达式含有变量,但C语言要求必须是常量或常量表达式;在选项B中,case之后是表达式,一方面其中含有变量,更主要的是对switch结构的执行机理不理解:执行该语句时,C语言只是将switch后面的表达式与case后的常量做值比较,因此,case之后一般不能使用关系、逻辑等表达式;选项C中的错误是在两个case后的常量都是12(12和y-8),而C语言要求case后的常量不能重复。

尽管选项D)中的switch语句有点怪,但从语法角度看是正确的。如果x的值为3或?1,程序都将执行y=2并结束,子句“case 2”没有实际用处,可以删除。

知识点分析

switch语句的基本结构常识,在前面我们已经有相关的介绍了。

答案

D

难度提示

简单。只要应试者真正掌握了switch语句的结构,这类题目很容易就可以解决的。【例题6-6 switch语句的嵌套】

题干

下述程序的输出结果是( )。

程序6-8:P6-8.c

#include <stdio.h>

main()

{ int x=1,y=0,z=0,b=0;

switch(x)

{

case 1:

switch(y)

{

case 0: a++;break;

case 1: b++;break;

}

case 2: a++;b++;break;

case 3: a++;b++;

}

printf("\na=%d,b=%d",a,b);

}

A)a=1,b=0 B)a=2,b=1 C)a=1,b=1 D)a=2,b=2

解题方法

139 这是“嵌套”形式的switch语句,因为break语句对switch结构影响较大,因此,分析此程序时应清楚地意识到第一个case后没有break语句。

程序执行时,x=1,执行内嵌的switch语句,因y=0,计算a++,使变量a的值为1并终止内层switch结构,回到外层。程序继续执行“case 2:”后面的语句“a++;b++;”,这使变量a,b的值分别为2和1,外层switch语句结束。

知识点分析

嵌套形式的switch语句:switch语句可以嵌套,且任何switch结构中的break语句只对该层switch语句起作用。

switch语句何时终止:当x与某一个case后的表达式值相等时,程序执行其后的语句序列,直到遇到break语句或者switch结构末尾终止。

答案

B

难度提示

中级。这道题不是单纯地考查switch语句,它对switch进行了扩展,加入了嵌套的结构,所以增加了难度。

【例题6-7 switch语句填空题】

题干

某物品原有价值为p,由于使用使其价值降低,价值的折扣率根据时间t(月数)确定如下:

?t<3, 无折扣

?3<t<6, 2%折扣

?6<t<12, 5%折扣

?

?12<t<21, 8%折扣

?

?t>21, 10%折扣

下述程序根据输入的时间和原有价值计算物品的现有价值,请填空。

程序6-9:P6-9.c

#include <stdio.h>

main()

{ int t,d;

float p;

scanf("%d,%f",&t,&p);

switch(__(1)__)

{ case 0: =0;

break;

case 1: d=2;

break;

140 case 2:

case 3: d=5;

break;

case 4:

case 5:

csee 6: d=8;

break;

default: =10;

}

printf("Price=%f",p*(1-__(2)__));

}

解题方法

本题考查switch语句的用法,说明如何将区间表示的数据转换成若干个整数表示。此外,也说明了在进行除法运算时应注意参加运算的数据类型问题。

观察题目给的已知条件t所描述的数据区间,容易发现,区间的边界都是3的倍数,因此,使用表达式t/3即可将区间转化为整数(注意t是整数)。当t处于题目所给定的区间时,表达式t/3的值分别为0(t<3);1(3<t<6);2、3(6<t<12);4、5、6(12<t<21);其他(t>21)。与程序相比较即知,(1)处应填入t/3。

对于空白处(2),显然是计算折扣的百分比表达式,应填入d/100.0,或者(float)/100等,总之,至少d和100应有一个是浮点数,因为直接使用表达式d/100的值为0。

知识点分析

switch语句的结构。

区间到整数的转换。

答案

(1)t/3 (2)d/100.0

难度提示

中级。这道题不是单纯地考查switch语句,还插入了相关的转换机制。

本 章 练 习

【选择题】

1.下述语句中,( )中的if语句语法是错误的。

A)if(x>y);

B)if(x==y) x+=y;

C)if(x!=y) scanf("%d",&x) else scanf("%d",&y);

141 D)if(x<y) {x++;y++;}

2.下述程序的输出结果是( )。

程序6-10:P6-10.c

#include <stdio.h>

main()

{ int a=0,b=0,c=0;

if(++a>0||++b>0)

++c;

printf("\na=%d,b=%d,c=%d",a,b,c);

}

A)a=0,b=0,c=0 B)a=1,b=1,c=1

C)a=1,b=0,c=1 D)a=0,b=1,c=1

3.阅读下述程序段:

y=-1;

if(x!=0)

if(x>0)

y=1;

else

y=0;

该程序段所描述的数学关系是( )。

?? x <0

?1 x <0

? ?

A) 0 x =0 B)

? 0 x =0

? ?

1 x >0 ?1 x >0

? ?

0 x <0 ?? x <0

? ?

C)? D)

?1 x =0 1 x =0

? ?

1 x >0 0 x >0

? ?

4.若执行下述程序时,从键盘输入的数据是3和4,则程序的输出结果是(

#include <stdio.h>

void main()

{ int a,b,s;

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

s=a;

if(a<b)

s=b;

。程序6-11:P6-11.c )

s=s*s;

printf("%d",s);

}

A)14 B)16

142 C)20 D)20

5.请读程序:

程序6-12:P6-12.c

#include <stdio.h>

main()

{ int x=1,y=0,a=0,b=0;

switch(x)

{ case 1:

switch(y)

{ case 0:

a++; break;

case 1:

b++; break;

}

case 2:

a++;b++;break;

}

printf("a=%d,b=%d\n",a,b);

}

上面程序的输出结果是( )。

A)a=2,b=1 B)a=1,b=1

C)a=1,b=0 D)a=2,b=2

6.若执行下面的程序时从键盘上输入5,则输出是( )。

程序6-13:P6-13.c

main()

{ int x;

scanf("%d",&x);

if(x++>5)

printf("%d\n",x);

else

printf("%d\n",x--);

}

A)7 B)6

C)5 D)4

7.两次运行上题中的程序,并且从键盘上分别输入6和4,则输出结果是(

A)7和5 B)6和3

C)7和4 D)6和4

8.若有以下定义:

float x;

int a,b;

。 )

则正确的switch语句是( )。

143 A)switch(x) B) switch(x)

{ case 1.0: printf("*\n"); { case 1,2: printf("*\n");

case 2.0: printf("**\n"); case 3: printf("**\n");

} }

C)switch(a+b) D) switch(a+b);

{ case 1: printf("*\n"); { case 1: printf("*\n");

case 1+2: printf("**\n"); case 2: printf("**\n");

} }

9.假定所有变量均已正确说明,下列程序段运行后x的值是( )。

a=b=c=0;

x=35;

if(!a)

x--;

else if(b);

if(c)

x=3;

else x=4;

A)34 B)4

C)35 D)3

10.与y=(x>0?1:x<0?-1:0);的功能相同的if语句是( )。

A)if(x>0) y=1; B)if(x)

else if(x<0) y=-1; if(x>0) y=1;

else y=0; else if(x<0) y=-1;

else y=0;

C)y=-1; D)y=0;

if(x) if(x>=0)

if(x>0) y=1; if(x>0) y=1;

else if(x==0) y=0; else y=-1;

else y=-1;

【填空题】

1.以下两条if语句可合并成一条if语句为______。

if(a<=b) x=1;

else y=2;

if(a>b) printf("****y=%d\n",y);

else printf("####x=%d\n",x);

2.当a=1,b=3,c=5,d=4时,执行下面一段程序后,x的值为______。

if(a<b)

if(c<d) x=1;

144 else

if(a<c)

if(b<d) x=2;

else x=3;

else x=6;

else x=7;

3.以下程序的输出结果是______。

程序6-14:P6-14.c

main()

{ int a=-1,b=2,k;

if((++a<0)&&!(b--<=0))

printf("%d%d\n",a,b);

else

printf("%d%d\n",b,a);

}

4.在执行下面程序时,为了使输出结果为:t=4,则给a和b输入的值应满足的条件是________。

程序6-15:P6-15.c

main()

{ int s,t,a,b;

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

s=1;t=1;

if(a>0) s=s+1;

if(a>b) t=s+1;

else if(a==b) t=5;

else t=2*s;

printf("t=%d\n",t);

}

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

145 第 7 章 循环结构程序设计

考纲要求:

1.for循环结构。

2.while和do while循环结构。

3.continue语句和break语句。

4.循环的嵌套。

知识点讲析

【for循环结构】

for语句构成的循环结构通常称为for循环。for循环的一般形式如下:

for(表达式1;表达式2;表达式3) 循环体

例如:

for(k=0;k<10;k++) printf("*");

以上for循环的功能是在一行上输出10个“*”号。

for是C语言的关键字,其后的一对圆括号中通常含有三个表达式,各表达式之间用“;”隔开。这三个表达式是任意形式的表达式,通常主要用于for循环的控制。紧跟在for( ? )之后的循环体,在语法上要求是一条语句,若在循环体内需要多条语句,应该用大括号括起来组成复合语句。

【while语句和do while语句】

while循环的一般形式

由while语句构成的循环也称“当”循环,while循环的一般形式如下:

while(表达式) 循环体

例如:

while(i<0) {printf("*"); i++;}

说明:

146 (1)while是C语言的关键字。

(2)while后一对圆括号中的表达式,可以是C语言中任意合法的表达式,由它来控制循环体是否执行。

(3)在语法上,要求循环体可以是一条简单可执行语句;若循环体内需要多个语句,应该用大括号括起来,组成复合语句。

do-while语句构成的循环结构

do-while循环结构的形式如下:

do

循环体

while(表达式);

例如:

do

{

i++;

s+=i;

}

while(i<10);

说明:

(1)do是C语言的关键字,必须和while联合使用。

(2)do-while循环由do开始,用while结束;必须注意的是:在while(表达式)后的“;”不可丢,它表示do-while语句的结束。

(3)while后一对圆括号中的表达式,可以是C语言中任意合法的表达式,由它控制循环是否执行。

(4)按语法,在do和while之间的循环体只能是一条可执行语句;若循环体内需要多个语句,应该用大括号括起来,组成复合语句。

【continue语句和break语句】

break语句

前面我们已经介绍用break语句可以使流程跳出switch语句体。在循环结构中,也可应用break语句使流程跳出本层循环体,从而提前结束本层循环,接着执行循环下面的语句。

例如:在循环体中break语句执行示例。

程序7-1:P7-1.c

main()

{

int i,s;

s=0;

for(i=1;i<=10;i++)

147 {

s=s+1;

if(s>5) break;

printf("s=%d\n",s);

}

}

程序输出的结果如下:

s=1

s=3

上例中,如果没有break语句,程序将进行10次循环。但当i=3时,s的值为6,if语句中的表达式s>5的值为1,于是执行break语句,跳出for循环,从而提前终止循环,即不再继续执行其余的几次循环。

break语句的使用说明:

(1)只能在循环体内和switch语句体内使用break语句。

(2)当break出现在循环体中的switch语句体内时,其作用只是跳出该switch语句体。当break出现在循环体中,但并不在switch语句体内时,则在执行break后,跳出本层循环体。

continue语句

continue语句的一般形式为:

continue;

其作用是结束本次循环,即跳过本次循环中余下未执行的语句,接着再一次进行循环的条件判定。注意:执行continue语句并没有使整个循环终止。

在while和do-while循环中,continue语句使得流程直接跳到循环控制条件的测试部分,然后决定是否继续进行。在for循环中,遇到continue后,跳过循环体中余下的语句,而去对for语句中的“表达式3”求值,然后进行“表达式2”的条件测试,最后根据“表达式2”的值来决定for循环是否执行。在循环体内,不论continue是作为何种语句中的语句成分,都将按上述功能执行,这点与break有所不同。

例如:在循环体中continue语句执行示例。

程序7-2:P7-2.c

main()

{ int k=0,s=0,i;

for(i=1;i<=5;i++)

{

s=s+i;

if(s>5)

{

printf("****i=%d,s=%d,k=%d\n",i,s,k); /*1#输出语句*/

continue;

}

k=k+s;

printf("i=%d,s=%d,k=%d\n",i,s,k); /*2#输出语句*/

148 }

}

运行结果如下:

i=1,s=1,k=1

i=2,s=3,k=4

***i=3,s=6,k=4

***i=4,s=10,k=4

***i=5,s=15,k=4

程序运行时,当i为1和2时,并不执行if子句,仅执行k=k+s;和2#输出语句;执行第三次循环时,s的值已是6,这时表达式s>5的值为1,因此执行if分支中的1#输出语句和continue语句,并跳过k=k+s;语句和2#输出语句,接着执行for后面括号中的i++,继续执行下一次循环。由输出结果可见,后面三次循环中k的值没有改变。

【循环的嵌套】

在一个循环体内又包含了另一个循环结构,称为循环的嵌套。内嵌的循环中还可以嵌套循环,这就是多层循环,每一层循环在逻辑上必须是完整的。循环的嵌套的概念对各种语言都是一样的。

前面介绍的三种类型的循环(while循环、do-while循环、for循环)都可以互相嵌套。例如,下面几种都

是合法的形式:

(1)whil.e( )

{ .

.

while( )

{ ? }

}

(2)do .

{ .

.

do

{ ? }

while( );

}

(3)f.or( ; ; )

{.

.

for( ; ; )

{ ? }

}

(4)w.hile( )

{.

.

do

{ ? }

.while( );

.

}

149 (5)for( .; ; )

.

{

while( )

.{ }

.

}

(6)do

{.

.

for( ; ;)

. { }

.

}

while( );

在编写程序时,嵌套循环的书写要采用缩进形式,像上面的程序中所示,内循环中的语句应该比外循环中的语句有规律地向右缩进2~4列,这样的程序层次分明,易于阅读。

例如:使用双层for循环打印下面的图形(见下图7-1)。

****

****

****

图7-1 使用for循环打印的图形

程序如下所示。

程序7-3:P7-3.c

main()

{ int k,i,j;

for(i=0;i<=2;i++)

{

for(k=1;k<=i;k++)

printf(" ");

for(j=0;j<=3;j++)

printf("*");

printf("\n");

}

}

以上程序中由i控制的for循环中内嵌了两个平行的for循环。由k控制的for循环体只有一个语句,用来输出一个空格。由j控制的for循环体也只有一个语句,用来输出一个“*”号。

当i等于0时,由k控制的for循环,因为k的值为1,表达式k<=i的值为0,循环体一次也不执行,接着执行由j控制的for循环体,连续输出四个“*”号;当i等于1时,由k控制的for循环体执行一次,输出一个空格,这就使得接着输出的四个“*”号右移一

150个字符位置;其他依次类推。

请注意,以上内嵌的第一个for循环的循环结束条件和外循环的控制变量i有关,而第二个for循环的循环结束条件是固定不变的。

表7-1中列出了以上双重循环中i、k和j值的变化规律。

表7-1 i、k和j值的变化规律

k的变化 j的变化

i=0 1 0,1,2,3,4

i=1 1,2 0,1,2,3,4

i=2 1,2,3 0,1,2,3,4

I=3 (当i等于3时退出循环)

下面我们再举一例来加深对嵌套循环的理解。

例如:输入6名学生五门课程的成绩,分别统计出每个学生五门课程的平均成绩。

程序中需要用双重循环来处理。外层循环每循环一次输入一名学生的数据并求出该学生的平均分,然后输出该学生的全部数据。循环6次,可处理6名学生的数据。内层循环中读入第i位学生5门课的成绩,并进行累加。

程序如下所示。

程序7-4:P7-4.c

#define N 6

#define M 5

main()

{

int i,j;

float g,sum,ave;

for(i=1;i<=N;i++) /*用i表示序号*/

{

sum=0; /*sum存放每位学生成绩总和,初值为0*/

/*内循环体执行5次,读入一名学生的五门课程的成绩*/

for(j=1;j<=M;j++)

{

scanf("%f",&g);

sum=sum+g; /*累加五门课程的成绩*/

}

ave=sum/M; /*ave存放当前一名学生的平均分*/

printf("No.%d ave=%5.2f\n",i,ave);

/*输出序号和对应的平均分*/

}

}

以上程序的开始定义了两个符号常量N和M,如果处理的学生人数或课程门数有所改变,只需改变define行中与N和M对应的值而不必改动程序的其他部分。程序中内循环体和外循环体都由大括号括起来组成复合语句。注意:sum=0;赋初值语句不能放在外循环

151体外,如:

.

.

sum=0;

for(i=1;i<=N;i++)

{

.

}

请读者自己分析这将导致什么样的结果。

程序执行时按以下形式输入数据(注意:实型变量可以接受整型数,每行输入的最后敲一下Enter键)。

60 70 80 90 100↙

65 75 85 95 100↙

66 76 86 96 100↙

67 77 87 97 100↙

68 78 88 98 100↙

69 79 89 99 100↙

运行结果如下:

No.1 ave=80.00

No.2 ave=84.00

No.3 ave=84.80

No.4 ave=85.60

No.5 ave=86.40

No.6 ave=87.20

补充与扩展

【for语句的执行过程】

for语句形式为:

for(表达式1;表达式2;表达式3) 循环体

执行过程如下:

1.求解“表达式1”。

2.计算“表达式2”,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行步骤3;若为假,则结束循环,转到步骤5。

3.行指定for循环语句。

4.求解“表达式3”,转回上面步骤2继续执行。

5.结束for循环,执行for循环之后的语句。

152 可以用图7-2来表示for语句的执行过程。

求解表达式1

表达式2

语句

求解表达式3

for语句的

下一条语句

图7-2 for语句的执行过程

【for语句的相关补充说明】

1.for语句一般形式中的“表达式1”可以省略,此时应在for语句之前给循环变量赋初值。注意省略表达式1时,其后的分号不能省略。如:

for( ;i<=20;i++) sum=sum+i;

执行时,跳过“求解表达式1”这一步,其他不变。

2.如果表达式2省略,即不判断循环条件,循环无终止地执行下去。也就是认为表达式2始终为真。例如:

for(i=1; ;i++) sum=sum+i;

3.表达式3也可以省略,但此时程序设计者应另外设法保证循环能正常结束。例如:

for(i=1;i<=100; )

{

sum=sum+i;

i++;

}

本例中将i++的操作放在循环体内,而不放在for语句的表达式3的位置,其实这效果是完全一样的,都能使循环正常结束。

4.for语句后面的括号内的表达式1和表达式3也可省略,只有表达式2,即只给出循环条件。例如:

for( ;i<=100; )

153 {

sum=sum+i;

i++;

}

5.for语句的三个表达式都可以省略,但是两个“;”不能省略。例如:

for( ; ; )

printf("Error\n");

三个表达式均省略后,这样就缺少了判断条件,循环将会无限制地执行,而形成无限循环(即通常所说的“死循环”)。

6.for语句后的括号内的表达式1和表达式3可以是任意有效的C语言表达式,可以是设置循环变量初值的赋值表达式,也可以是与循环变量无关的其他的表达式。例如:

for(sum=0,i=1;i<=100;sum=sum+i,i++) { ? }

其中表达式1和表达式3都是逗号表达式,各包含两个赋值表达式,即同时设两个初值,使两个变量增值,执行情况流程图可参照图7-2画出,请读者自己尝试。

7.表达式2一般是关系表达式(如i<=100)或逻辑表达式(如a>b&&x>y),但也可以是数值表达式或字符表达式,只要其值为真(非0),就执行循环体。例如:

for( ;(c=getchar())!='\n'; )

printf("%c",c);

C语言中的for语句书写灵活,功能较强。在for语句后的括号内允许出现各种形式的与循环控制无关的表达式,虽然这在语法上是合法的,但是这样会使for语句显得杂乱,降低程序的可读性,与现代编程推崇的大方向不符。所以建议初学者在编写程序时,for 后面的括号内仅写一些对循环进行控制的表达式,其他的操作尽量放在循环体内完成。

下面我们将通过两个例题来对前面的知识进行巩固。

例如:求n!。即计算1?2?3???n的值。

程序如下所示。

程序7-5:P7-5.c

main()

{

int i,s,n;

s=1;

printf("Enter n: ");

scanf("%d",%n);

for(i=1;i<=n;i++)

s=s*i;

printf("s=%d\n",s);

}

上面程序是某个已知数的阶乘(即:连乘)的算法的典型例题,与累加一样,连乘也是程序设计中的基本算法之一。程序中的i从1变化到n,每次增1。循环体内的表达式s=s*i

154用来进行连乘。

在连乘算法中,存放连乘积的变量也必须赋初值,虽然初值不能用0。在本例中s的初值为1,当i=1时,进行1*1的运算,给s赋1,当i=2时,将进行1*2运算,重新给s赋2,当i=3时,进行2*3的运算,重新给s赋6,依次类推,当i=n,进行s*n的运算,s中最终将存入1*2*3*?*n的值。

下面给出了以上程序输入特定参数执行后的相关结果。若给n输入5,变量i和s中值的变化如表7-2所示。

表7-2 变量i和s值的变化情况

i的值 1 2 3 4 5

s的值 1 2 6 24 120

下面我们来讨论求和的情况。

100 50 10 1

例如:求 k+ k2+ 。

∑ ∑ ∑k

k=1 k=1 k=1

程序如下所示。

程序7-6:P7-6.c

main()

{ int N1=100,N2=50,N3=10;

float k;

float s1=0,s2=0,s3=0;

for(k=1;k<=N1;k++) /*计算1~100的和*/

{

s1=s1+k;

}

for(k=1;k<=N2;k++) /*计算1~50的平方和*/

{

s2=s2+k*k;

}

for(k=1;k<=N3;k++) /*计算1~10的各倒数和*/

{

s3=s3+1/k;

}

printf("总和=%8.2f\n",s1+s2+s3);

}

运行结果如下:

总和=47977.93

相关的程序分析过程请读者自己尝试。要想在短时间内学会一门计算机语言,最重要的是需要实践,希望读者能多上机实践,在实践中找到自己的不足之处。

【while语句的执行过程】

155 while语句的一般形式如下:

while(表达式) 循环体

执行过程如下:

1.计算圆括号中表达式的值,当值为非0时,执行步骤2;当值为0时,执行步骤4。

2.执行循环体中的语句。

3.转去执行步骤1。

4.退出while循环。

可以用图7-3来表示while语句的执行过程。

表达式 假

语句

图7-3 while语句的执行过程

【while语句的相关补充说明】

1.循环体如果包含一个以上的语句,应该用花括号括起来,以复合语句形式出现。如果不加花括号,则while语句的范围只到while语句后面的第一个分号处。

2.while后圆括号中表达式的值决定了循环体是否执行,因此,进入while循环后一定要有使循环趋向于结束的语句;如果无此语句,则循环将无限制地执行,永不结束。

3.不要把if语句构成的选择结构与由while语句构成的循环结构混同起来。若if后条件表达式的值为非0时,其后的if子句只执行一次;而当while后圆括号内的表达式值为非0时,其后的循环体中的语句将重复执行,所以在设计循环时,通常应在循环体内改变条件表达式中有关变量的值,使条件表达式的值最终变成0,以便能及时地退出循环。

下面我们来看两个例子,对前面的知识进行巩固。

π 1 1 1 ?6

例如:用π ? 公式求π的近似值,直到最后一项的绝对值小于10 为止。

4=1?3+5?7+

本题的基本算法是求累加和,但是还得注意以下几点:

1.用分母来控制循环的次数,若用n存放分母的值,则每累加一次,n应当增加2,每次累加的数不是整数,而是一个实数,因此n应当定义成float类型。

2.可以看成隔一项的加数是负数,若用t来表示相加的每一项,因此,每加一项之后,t的符号应当改变,这可用交替乘1和-1来实现。

3.从求π的公式来看,不能决定n的最终值应该是多少;但可以用最后一项t(1/n)

156的绝对值小于10?6来作为循环的结束条件。

程序如下所示。

程序7-7:P7-7.c

#include <math.h>

main()

{

int s;

float n,t,pi;

t=1.0;

pi=0;

n=1.0;

s=1.0;

while((fabs(t))>=1e-6)

{

pi=pi+t;

n=n+2;

s=-s;

t=s/n;

}

pi=pi*4;

printf("pi=%10.6f\n",pi);

}

运行结果如下:

pi=3.141397

上面的例子要做的都只是按照即定的公式写出相关的程序,都是正向的思维,下面例举一个需要使用逆向思维问题。

例如:这是个猴子吃桃的问题。猴子第一天摘下若干个桃子,当即吃了一半,但觉还不过瘾,又多吃了一个。第二天早上又将剩下的桃子吃掉了一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩一个桃子了。求第一天共摘了多少个桃子。

程序如下所示。

程序7-8:P7-8.c

main()

{

int day,x1,x2;

day=9;

x2=1;

while(day>0)

{

x1=(x2+1) *2; /*第1天的桃子数是第2天桃子数加1后的2倍*/

x2=x1;

day--;

}

printf("桃子总数=%d\n",x1);

157 }

运行结果如下:

桃子总数=1534

上面已经给出了详细的程序及相关的说明,请读者自己分析其中的过程,并且上机实践。在这过程中,读者应该一边实践一边采用多种方法理解其中的逻辑思想,可以使用多种程序流程图画出其流程,便于理解。

【do-while语句的执行过程】

do-while语句的一般形式如下:

do

循环体

while(表达式);

执行过程如下:

1.执行do后面循环体中的语句,即先执行一次指定的内嵌的语句。

2.判断while后括号内的表达式的值。当表达式的值为非0(真)时,返回重新执行步骤1;如此反复,直到表达式的值为0为止,执行步骤3。

3.退出do-while循环。

可以用图7-4来表示do-while语句的执行过程。

语句

真 表达式

图7-4 do-while语句的执行过程

【do-while语句的相关补充说明】

do-while构成的循环与while循环十分相似,在一般情况下,用while语句和用do-while语句处理同一问题时,若二者的循环体部分是一样的,它们的结果也一样。但在while后面的表达式一开始就为假时,两种循环的结果是不同的。

while与do-while循环的重要区别:while循环的控制,出现在循环体之前,只有当while后面的表达式的值为非0时,才可能执行循环体;在do-while构成的循环中,总是先执行一次循环体,然后在求表达式的值,因此,无论表达式的值是0还是非0,循环体至少要

158执行一次。

同样,do-while循环也一定要有能使while后圆括号内表达式的值变为0的操作,否则,循环将会无限制地执行下去。

下面我们还是通过例子来对前面的知识进行加深while与do-while循环印象。

例如:分别用while与do-while循环来求100n 。

n=1

程序7-9:P7-9.c 程序7-10:P7-10.c

main() main()

{ {

int i,sum=0; int i,sum=0;

i=1; i=1;

while(i<=100) do

{ {

sum=sum+i; sum=sum+i;

i++; i++;

} }

printf("%d",sum); while(i<=100);

} printf("%d",sum);

}

上面的程序可以看出:对于同一个问题可以用while语句处理,也可以用do-while语句处理。while结构可以转换成do-while结构,二者完全等价。

下面我们来看一个while循环与do-while循环不一样的情况。

例如:while和do-while循环的比较。

程序7-11:P7-11.c 程序7-12:P7-12.c

main() main()

{ {

int i,sum=0; int i,sum=0;

scanf("%d",&i); scanf("%d",&i);

while(i<=10) do

{ {

sum=sum+i; sum=sum+i;

i++; i++;

} }

printf("%d",sum); while(i<=10);

} printf("%d",sum);

}

运行情况如下:

1↙ 1↙

55 55

可以看出:当输入i的值小于或等于10时,二者得到的结果相同。而当i>10时,二者的结果就不同了。这是因为此时对while循环来说,一次也不执行循环体(表达式“i<=10”为假),而对do-while循环来说则要执行一次循环体。可以得到结论:当while后面的表达

159式的第一次的值为“真”时,两种循环得到的结果相同。否则,二者结果不相同(指二者具有相同的循环体的情况下)。

读者可以尝试输入不同的i初值,看看结果会是如何,并且总结各种情况,这样会对自己的学习很有帮助。

160 典 型 例 题

【例题7-1 循环结构基本常识】

题干

在C语言中,下述正确的是( )。

A)do-while语句构成的循环不能用其他语句构成的循环来代替。

B)do-while语句构成的循环只能用break语句退出。

C)用do-while语句构成的循环,在while后的表达式为非0时结束循环。

D)用do-while语句构成的循环,在while后的表达式为0时结束循环。

解题方法

本题考查考生对基本循环结构的理解。C语言中的三种循环语句中,每种循环都有一个循环终止条件,且都是在条件为真(表达式值为非0)时,循环继续执行,而条件为假(表达式值为0)时循环终止。故此题答案为D。

选项A中说不能用其他语句构成的循环来代替,其实do-while循环与while在很多情况下的执行结果都是一样的(当然是在保证循环体同样的条件下),C语言中的三种循环语句在很多情况下都可以互相代替。

选项B中,其实在C语言的do-while与while循环中,只要相关的条件表达式为0时,就会自动退出循环,无需break语句。当然,使用break语句可以提前退出循环。

选项C与选项D正好相反,所以错误。

知识点分析

循环结构:C语言中共有三种循环语句,其共同的特点是:当条件表达式(循环终止条件)的值为真时,执行循环体语句,否则终止循环。此外,循环体语句必须是一个语句,简单语句、复合语句或空语句均可。

而这三种循环的主要差别在于:while语句和for语句先测试条件表达式,而do-while循环后测试表达式,因此,while和for结构的循环体语句可能一次也不执行,但do-while结构的循环体语句至少要执行一次。

答案

D

难度提示

简单。这道题主要考查应试者对于基本循环结构的了解。

【例题7-2 简单的for循环】

题干

161 以下程序的输出结果为( )。

程序7-13:P7-13.c

#include <stdio.h>

main()

{

int i;

for(i=1;i<=5;i++)

{

if(i%2)

printf("*");

else

continue;

printf("#");

}

printf("$\n");

}

A)*#*#*#$ B)#*#*#*$ C)*#*#$ D)#*#*$

解题方法

在程序中,当i是奇数时(即i%2为真),执行输出语句,否则执行continue语句,即开始i++运算,进入下

一次循环。当i是偶数时(i%2为0)无任何输出。因此,本例的循环体可以改写成如下形式:

for(i=1;i<=5;i+=2)

{

printf("*");

printf("#");

}

循环的执行过程是i=1,i=3,i=5,共3次,所以循环的输出结果如选项A)所示。

知识点分析

相关的for循环结构的知识在前面已经有详细的介绍。

continue 语句:该语句只用于循环结构,作用是“加速”循环,即重新开始新的一次循环而不是终止循环。

注意continue语句和break语句都只对包含它们的最内层循环结构(或break对内层switch结构)起控制作用。

答案

A

难度提示

中级。这道题主要考查应试者关于for语句和continue语句的基本使用方法。【例题7-3 while循环结构】

162 题干

请阅读下面程序。

程序7-14:P7-14.c

#include <math.h>

#include <stdio.h>

main()

{

float x,y,a;

scanf("%f,%f",&x,&y);

z=x/y;

while(1)

{

if(fabs(z)>1.0)

{x=y;y=z;z=x/y;}

else

break;

}

printf("%f",y);

}

若运行时从键盘上输入“3.6,2.4↙”,则输出的结果是( )。

A)1.500000 B)1.600000 C)2.000000 D)2.400000

解题方法

本题是一个使用break语句做出口才能退出的循环。程序中的主要运算是“/”,因为程序中所涉及的数据都是浮点数,所以“/”只是普通除法,不需要考虑数据类型的转换。具体的计算步骤如下:

1.x=3.6,y=2.4,z=1.5

2.x=2.4,y=1.5,z=1.6

3.x=1.5,y=1.6,z=0.9376998

由上可知,最后输出的值y应为1.6。

所以正确答案为B。

知识点分析

相关的while结构知识及break语句对循环结构的控制作用。

break语句:该语句可以用在switch结构和循环结构中,其作用是:终止switch语句或循环语句的执行。用在循环结构中的break语句增加了该循环语句的出口点。

答案

B

难度提示

中级。这道题主要考查应试者对break语句和while循环的了解。

【例题7-4 continue语句的使用】

163 题干

下述程序的输出结果是( )。

程序7-15:P7-15.c

#include <stdio.h>

main()

{

int y=9;

for( ;y>0;y--)

{

if(y%3==0)

{

printf("%d",--y);

continue;

}

}

}

A)741 B)852

C)963 D)875421

解题方法

首先,y的初始值为9,if后括号内的判断表达式y%3==0为真,则输出表达式--y的值,其值为8,故选项A和C不可能是正确的输出结果。在输出8之后,y的值也变成了8。之后因为continue语句继续执行循环,计算y--,使y的值变为7。因为7%3==0的为假,所以没有输出,继续执行循环,所以输出有7的答案都不可能是正确的答案,现在便可使用排除法得到正确答案:B。

依次类推,也可以得到最后结果。程序继续循环时,计算y--,y的值为6;输出表达式--y,值为5;且使y减1,值为5。在循环,y=4,不输出。在计算y--,使y为3;输出2,y的值也是2。因为y>0时执行循环,所以,不会再产生输出。

知识点分析

相关的for循环结构和continue语句的知识。

这些知识点在前面的知识点讲解中或在前面的例题中有详细的分析,请读者在自己独立思考的情况下,再参照前面的讲解对考题进行分析。

答案

B

难度提示

中级。本题涉及到了for循环语句、continue语句和自反运算的相关知识,重点考查了continue语句的作用和在循环体内修改控制变量对循环的影响问题。

【例题7-5 包含switch结构的循环1】

164 题干

运行以下程序后,如果从键盘上输入china#↙,则输出结果为( )。

程序7-16:P7-16.c

#include <stdio.h>

main()

{

int v1=0,v2=0;

char ch;

while((ch=getchar())!='#')

switch(ch)

{

case 'a':

case 'h':

default: v1++;

case 'o': v2++;

}

printf("%d,%d",v1,v2);

}

A)2,0 B)5,0 C)5,5 D)2,5

解题方法

根据内嵌在while循环的switch结构执行的流程可以看出,因为在整个switch结构中都没有break语句,所以不管输入任何字母,程序执行v1++并继续执行v2++;可知两个输出值即为除去“#”后的输入字母的个数。所以正确答案应该是C。

知识点分析

while循环的基本常识。

switch基本结构。

switch结构的流程。switch结构的执行方式是固定的:将switch后的表达式与case后的值比较(这是为什么要求各表达式的值都是定点数的原因),若无匹配项,执行 defalut部分;若有匹配项,执行此case后的部分。此时,其他的case(和default)失效。

分析switch结构的关键一点是无论如何,程序一直执行到一个break语句才结束(或到switch结构末尾)。

default子句:default是switch中的可选项,其位置也不是一定要写在最后,其地位与其他case子句相同,也是表示一种情况。

答案

C

难度提示

中级。只要应试者真正掌握了while循环的结构及其条件语句的判断,熟悉switch语句的结构,加上仔细地审题,这类题目很容易就可以解决。

【例题7-6 包含switch结构的循环2】

165 题干

以下程序的输出结果是( )。

程序7-17:P7-17.c

#include <stdio.h>

main()

{

int a=0,i;

for(i=1;i<5;i++)

{

switch(i)

{

case 0:

case 3: a+=2;

case 1:

case 2: a+=3;

default: a+=5;

}

}

printf("%d\n",a);

}

A)31 B)13

C)10 D)20

解题方法

这道题在for循环中插入了一个switch结构,这无疑增加了难度。下面我将把i处于各个值情况下的具体情况列出来:

i=1时,执行case 1语句后面的所有语句,即:a+=3和a+=5。

i=2时,执行a+=3和a+=5。

i=3时,执行a+=2、a+=3和a+=5。

i=4时,执行a+=5。

i=5时,跳出for循环。

由上可知,a的初值为0,到循环结束时,a的值已经是31了。所以正确答案应该是A。

知识点分析

for循环的基本常识。

switch基本结构。

switch结构的流程。

default子句。

相关知识请参照上例。

答案

A

难度提示

166中级。在for循环中加入了switch结构是难度稍有提高。

167【例题7-7 循环的次数问题1】

题干

有以下程序段:

int k=0;

while(k=1)

k++;

while循环执行的次数是:

A)无限次 B)有语法错,不能执行

C)一次也不执行 D)执行1次

解题方法

在解答本题时,读者必须注意表达式k=1是赋值表达式而非关系表达式,不论k为何值,表达式k=1都使k为1,且此表达式的值也为1,故此循环将无限制地执行下去。所以答案应为A。

若将k=1改写成k!=1或者k>=1都将得到不同的结果。具体的答案请读者自己思考。

知识点分析

相关的while循环结构的知识。

典型错误提示:不要将“=”与“==”运算混淆。“=”只是赋值表达,而“==”是关系表达式。

答案

A

难度提示

简单。考查赋值表达式与关系表达式的区别及应试者的审题能力。

【例题7-8 循环的次数问题2】

题干

以下程序中,while循环的循环次数是( )。

程序7-18:P7-18.c

main()

{

int i=0;

while(i<10)

{

if(i<1)

continue;

168 if(i==5)

break;

i++;

}

}

A)1 B)10 C)6 D)死循环,不能确定次数

解题方法

现在我们按照程序一步一步往下分析,当i=0时,while循环的条件判断表达式的值为真,则进入循环;接着执行循环体内的语句,执行到第一个if语句时,if后面的条件判断表达式值也为真,则执行continue语句跳出循环,即不执行continue之后的任何语句,也就是说i的值一直不变,缺少了能使while循环终止的语句。由上可知此循环将无限制地执行下去,故答案应该选D。

知识点分析

while循环条件判断、if语句及continue语句的使用方法。

答案

D

难度提示

简单。这道题考的是while循环和continue语句的转移功能,稍加注意便可得分。【例题7-9

问题1】

题干

以下程序执行后sum的值是( )。

程序7-19:P7-19.c

main()

{

int i,sum;

for(i=1;i<6;i++)

sum+=i;

printf("%d\n",sum);

}

A)15 B)14

C)不确定 D)0

循环的结果

解题方法

在我们分析这道题之前,我想提醒大家:在答题的时候不要形成思维定势。看完整个程序之后,我们可以发现在程序中始终没有给sum赋初值,有的人在平时做题目,看程序的时候都不够仔细,只是看看循环而已,并不在意这些小问题,命题者往往在这些应试者容易疏漏的地方出题,致使答题者出错。知道了以上几点之后,便可以轻易地选出正确答

169案:C。因为sum根本就没有初值,如果在程序循环体之前有sum=0,则会得到不同的答案,请读者自己思考。

知识点分析

for循环结构;数据类型的定义及初值问题。

答案

C

难度提示

简单。这道题考的其实并不是for循环结构,而是考查答题者的细心程度。【例题7-10

题干

以下程序的输出结果是( )。

程序7-20:P7-20.c

#include <stdio.h>

main()

{

int i=0,a=0;

while(i<20)

{

for( ; ; )

{

循环的结果问题2】

if((i%10)==0)

break;

else

i--;

}

i+=11;

a+=i;

}

printf("%d\n",a);

}

A)21 B)32 C)33 D)11

解题方法

我们顺着程序往下走,当i=0时,首先进入while循环,接着进入for循环,因为if语句的条件判断表达式“(i%10)==0”为真,执行break语句,跳出for循环,执行for循环后面的语句,则i=11,a=11;因为i<20,所以接着执行while循环,进入for循环,但是这次if语句的条件判断表达式“(i%10)==0”为假,执行else语句i--;则i=10,再次执行for循环,这下if语句的条件判断表达式“(i%10)==0”为真,跳出for循环,这时i=10;接着执行i+=11和a+=i语句,则i=21,a=11+21=32;再次判断while后面的表达式时i<20已经为

170假,所以不再执行while循环,到最后输出结果。所以正确答案应该是:B。

知识点分析

while循环结构。

for循环结构。

if语句。

break语句。

答案

B

难度提示

中级。这道题考查的知识点比较多,而且需要综合应用,在掌握上面知识点的情况下,还需要有较强的逻辑推理能力。

本 章 练 习

【选择题】

1.下述循环的循环次数是( )。

int k=2;

while(k=0)

printf("%d",k);

k--;

printf("\n");

A)无限次 B)0次

C)1次 D)2次

2.对于下面①,②两个循环语句,( )是正确的描述。

①while(1);

②for( ; ; );

A)①②都是无限循环 B)①是无限循环,②错误

C)①循环一次,②错误 D)①②皆错误

3.对于下述for循环语句,说法正确的是( )。

int i,k;

for(i=0,k=-1;k=1;i++,k++)

printf("***");

A)判断循环结束的条件非法 B)是无限循环

C)只循环一次 D)一次也不循环

4.在下述程序中,判断i>j共执行了( )次。

171程序7-21:P7-21.c

#include <stdio.h>

void main()

{

int i=0,j=10,k=2,s=0;

for( ; ; )

{

i+=k;

if(i>j)

{

printf("%d\n",s);

break;

}

s+=i;

}

}

A)4 B)7

C)5 D)6

5.下述for语句的循环次数是( )。

int i,x;

for(i=0,x=0;i<=9&&x!=876;i++)

scanf("%d",&x);

A)最多循环10次 B)最多循环9次

C)无限循环 D)一次也不循环

6.若i、j已定义为int类型,则以下段中内循环体的总的次数是( )。

for(i=5;i;i--)

for(j=0;j<4;j++)

A)20 B)24

C)25 D)30

7.以下循环体的执行次数是( )。

main()

{

int i,j;

for(i=0,j=1;i<=j+1;i+=2,j--)

printf("%d\n",i);

}

A)3 B)2

C)1 D)0

8.有如下程序:

#define N 2

#define M N+1

#define NUM 2*M+1

172main()

{

int i;

for(i=1;i<=NUM;i++)

printf("%d\n",i);

}

该程序中的for循环执行的次数是( )。

A)5 B)6

C)7 D)8

9.下述循环语句( )。

for(a=0,b=0;a<3&&b!=3;a++);

A)是无限循环 B)循环次数不定

C)循环3次 D)循环4次

10.下述语句执行后,变量k的值是( )。

int k=1;

while(k++<10);

A)10 B)11

C)9 D)此为无限循环,值不定

11.若执行下面程序时,输入“ADescriptor↙”,则以下程序的输出结果是(

#include <stdio.h>

。程序7-22:P7-22.c )

main()

{

char c;

int v0=0,v1=0,v2=0;

do

switch(c=getchar())

{

case 'a': case 'A':

case 'e': case 'E':

case 'i': case 'I':

case 'o': case 'O':

case 'u': case 'U': v1++;

default: v0++;v2++;

}

while(c!=’\n’);

printf("\nv0=%d,v1=%d,v2=%d",v0,v1,v2);

}

A)v0=7,v1=4,v2=7

B)v0=8,v1=4,v2=8

C)v0=11,v1=4,v2=11

D)v0=12,v1=4,v2=12

17312.若下述程序执行时按如下方式输入数据:

a↙b↙cdef

则该程序的运行结果是( )。

程序7-23:P7-23.c

#include <stdio.h>

main()

{

int k;

char c;

for(k=0;k<=5;k++)

{

c=getchar();

putchar(c);

}

}

A)abcdef B)a C)a D

b b b

c cd cdef

e

f

13.下述程序的输出结果是( )。

程序7-24:P7-24.c

#include <stdio.h>

)a

main()

{

int k=0,m=0;

int i,j;

for(i=0;i<2;i++)

{

for(j=0;j<3;j++)

k++;

k-=j;

}

m=i+j;

printf("k=%d,m=%d",k,m);

}

A)k=0,m=3 B)k=0,m=5

C)k=1,m=3 D)k=1,m=5

14.下述程序的输出结果是( )。

程序7-25:P7-25.c

#include <stdio.h>

main()

174{

char c='A';

int k=0;

do

{

switch(c++)

{

case 'A': k++;

break;

case 'B': k--;

case 'C': k+=2;

break;

case 'D': k%=2;

continue;

case 'E': k*=10;

break;

default: k/=3;

}

k++;

}

while(c<’G’);

printf("k=%d",k);

}

A)k=3 B)k=4

C)k=2 D)k=0

15.对下述程序执行结果的判断中,正确的是( )。

程序7-26:P7-26.c

#include <stdio.h>

void main()

{

int x;

for(x=1;x<=100;x++)

if(++x%2==0)

if(++x%3==0)

if(++x%5==0)

printf("%d,",x);

}

A)输出31,61,91 B)输出30,60,90

C)不输出任何内容 D)输出29,59,89

16.下述程序的输出结果是( )。

程序7-27:P7-27.c

#include <stdio.h>

main()

{

int x=7,y=5,z=1;

do

175{

if(!(z%x))

if(!(z%y))

{

printf("%d",z);

break;

}

z++;

}

while(z!=0);

}

A)70 B) 35

C)105 D) 140

17.下述程序的输出结果是(

程序7-28:P7-28.c

#include <stdio.h>

void main()

{

int x=3,y=6,z=0;

while(x++!=(y-=1))

{

z++;

if(y<x)

。 )

break;

}

printf("x=%d,y=%d,z=%d",x,y,z);

}

A)x=4,y=4,z=1 B)x=5,y=4,z=3

C)x=5,y=5,z=1 D)x=5,y=4,z=1

18.请读下面的程序。

程序7-30:P7-30.c

#include <stdio.h>

main()

{

int a,b;

for(a=1,b=1;a<=100;++)

{

if(b>=20) break;

if(b%3==1)

{

b+=3;

continue;

}

b-=5;

}

176 printf("%d\n",a);

}

上面程序的输出结果是( )。

A)7 B)8

C)9 D)10

19.下面程序输出的结果是( )。

程序7-31:P7-31.c

#include <stdio.h>

main()

{

int x=3;

do

{

printf("%d\n",x-=2;);

}

while(!(--x));

}

A)输出的是1 B)输出的是1和-2

C)输出的是3和0 D)是死循环

20.设x和y均为int型变量,则执行下面的循环后,y的值为(

for(y=1,x=1;y<=50;y++)

{

。 )

if(x>=10) break;

if(x%2==1)

{

x+=5;

continue;

}

x-=3;

}

A)2 B)4 C)6 D)8

【填空题】

1.下面程序输出的结果是______。

程序7-32:P7-32.c

#include <stdio.h>

void main()

{

int s=0,k;

for(k=7;k>4;k--)

{

177 switch(k)

{

case 1:

case 4:

case 7: s++;break;

case 2:

case 3:

case 6: break;

case 0:

case 5: s+=2;break;

}

}

printf("s=%d",s);

}

2.下述程序片段的循环次数是 (1)

int x=0,y=0;

do

{

y++;

x*=x;

}

while(x>0&&y>5);

printf("y=%d,x=%d",y,x);

3.以下程序的输出结果是______。

程序7-32:P7-32.c

#include <stdio.h>

。,输出结果是 (2)

void main()

{

int i,j;

for(i=0;i<3;i++)

{

for(j=4;j>=0;j--)

{

if((j+i)%2)

{

j--;

printf("%d,",j);

continue;

}

--i;

j--;

printf("%d,",j);

}

}

}

178 4.以下程序的功能是:从键盘上输入若干个学生的成绩,统计并输出最高成绩和最低成绩,当输入负数时结束输入,请填空。

main()

{

float x,amax,amin;

scanf("%f",&x);

amax=x;

amin=x;

while(__(1)__)

{

if(x>amax) amax=x;

if(__(2)__) amin=x;

scanf("%f",&x);

}

printf("\namax=%f\namin=%f\n",amax,amin);

}

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □

2.本章复习的效果好吗? 不好 □

3.本章疑难问题汇总:

4.对自己说点什么吧:

179 第 8 章 数组的定义和引用

考纲要求:

1.一维数组和多维数组的定义、初始化和引用。

否 □ 还行 □好□

2.字符串与字符数组。

知识点讲析

【一维数组的定义】

当数组中每个元素只带有一个下标时,称这样的数组为一维数组。在C语言中,定义一维数组的语句一般形式如下:

类型说明符 数组名[常量表达式],? ;

例如:

int a[10];

它表示该数组名为a,数组含有10个元素。

【一维数组元素的引用】

数组必须先定义,然后使用。

由于引用的是一维数组,因此在引用数组元素时只需带一个下标。引用形式如下:

数组名[下标表达式]

例如,若有以下定义语句:

double x[6];

则x[0]、x[1+2]、x[i+k]都是对x数组中的元素合法的引用形式;其中0、1+2和i+k称为下标表达式,由于定义了x数组有6个元素,因此下标表达式的值必须大于等于0,并且小于6。注意:在数组元素x[i+k]中,i+k只是一个下标表达式,不要误认为有i和k两个下标。【一维数组元素的初始化】

可以用赋值语句或输入语句使数组中的元素得到初值,但占用运行时间。可以使数组

180在程序运行之前初始化,即在编译阶段使之得到初值。

对数组元素的初始化可以用下面简单直观的方法实现。

在定义数组时对数组元素赋以初值。

例如:

static int a[10]={0,1,2,3,4,5,6,7,8,9};

将数组元素的初值依次放在一对花括弧内。请注意:在int的前面有一个关键字static,它是“静态存储”的意思。C语言规定只有静态存储(static)数组和外部存储(extern)数组才能初始化。

经过上面的定义和初始化之后,a[0]=0,a[1]=1,a[2]=2,a[3]=3,a[4]=4,a[5]=5,a[6]=6,a[7]=7,a[8]=8,a[9]=9。

【二维数组的定义】

当数组中每个元素带有两个下标时,称这样的数组为二维数组。第一个下标表示该元素在第几行,第二个下标表示在第几列。在逻辑上可以把二维数组看成是一个具有行和列的表格或一个矩阵。

二维数组的定义格式如下:

类型说明符 数组名[常量表达式1][常量表达式2],? ;

二维数组说明符中必须有用两个方括号括起来的常量表达式,常量表达式的值只能是正整数。可以把“常量表达式 1”看成是矩阵(或表格)的行数,它决定了第一维下标值的上限为((常量表达式1)-1);可以把“常量表达式2”看成是矩阵(或表格)的列数,它决定了第二维下标值的上限为((常量表达式2)-1)。例如:

int a[3][4];

在这里int是类型名,a[3][4]是一个二维数组说明符。可以认为此定义语句说明了:

1.定义了一个名为a的二维数组。

2.a数组中的每个元素都是整型。

3.a数组中共有3?4个元素。

4.a数组的逻辑结构是一个具有如表8-1形式的3行4列的表格:

表8-1 数组a的存储结构

第0列 第1列 第2列 第3列第0行 a[0][0] a[0][1] a[0][2] a[0][3]

第1行 a[1][0] a[1][1] a[1][2] A[1][3]

第2行 a[2][0] a[2][1] a[2][2] a[2][3]

在二维数组中的每个元素都有两个下标,第一个方括号中的下标代表行号,称行下标;第二个方括号中的下标代表列号,称列下标。行下标和列下标的下限都为0,在本例中,a数组的行下标的上限为2,列下标的上限

为3。

在C语言中,数组是按行存放的。即:先存放第0行的元素,再存放第1行的元素,??

181数组在内存中占有一系列连续的存储单元。

182【二维数组元素的引用】

二维数组中元素的表示形式如下:

数组名[下标1][下标2]

在引用二维数组元素时必须带有两个下标。

例如,若有如下定义语句:

int a[3][4];

则a[1][2]、a[2][3]都是合法的。下标可以是整型表达式,如a[3-1][2*2-1]。【二维数组元素的初始化】

二维数组的初始化方式多种多样,下面我们介绍最简单的初始化方法:

按行给二维数组赋初值

例如:

static int a[3][4]={

{1,2,3,4},

{5,6,7,8},

{9,10,11,12}

};

这种赋初值方法比较直观,全部初值括在一对花括号中,把第一个花括号内的数据赋给第一行的元素,第二个花括号内的数据赋给第二行的元素,??,即按行赋初值。【字符数组的定义】

用来存放字符数据的数组称为字符数组。字符数组中的每一个元素可存放一个字符。定义的方法和格式都与前面的数组相似。

例如:

char c[8];

本例将c定义为字符数组,包含8个元素。因为字符型与整型是互相通用的,在C语言中都是用ASCII码值存储的,因此上面的例子可以定义成如下形式:

int c[8];

【字符数组的初始化】

一维、二维数组的初始化前面已经介绍,字符数组也类似。对于字符数组的初始化,

183最简单、最易理解的方式就是逐个字符赋给数组中逐个元素。例如:

static char c[8]={'w','h','o',' ','a','m',' ','I'};

它是把8个字符分别赋给c[0]~c[7]8个元素。

【字符数组的引用】

字符数组的输入输出,即“引用”。通常最简单的方法是逐个字符地输入输出,使用格式字符“%c”输入或输出一个字符。

数组的引用已经讨论,下面我们通过一个简单的例子来说明字符数组的引用。

例如,用字符数组输出一个字符串。

程序8-1:P8-1.c

main()

{

static char c[8]={'w','h','o',' ','a','m',' ','I'};

int i;

for(i=0;i<8;i++)

printf("%c",c[i]);

printf("\n");

}

程序运行结果如下:

who am I

【字符串的定义】

C语言本身并不含有用来定义字符串变量的这么一种类型,字符串是作为字符数组来处理的,即字符串是用一维数组来存放的。但字符数组又不等同于字符串变量,它们之间的联系与区别将在后面做详细讨论。

在C语言中,以字符“\0”作为字符串结束标志。其中“\0”代表ASCII码为0的字符。在ASCII码表中可以查到。ASCII码值为0的字符只是一个转义字符,不是一个可以显示的字符,只是一个“空操作符”,又称为“空值”。用它来作字符串结束标志不会产生附加的操作或增加任何有效字符,只起一个辨别标志的功能,它作为标志占用存储空间,但不计入字符串的实际长度。

补充与扩展

【一维数组的补充说明】

184 定义

下面我们通过一个例子对一维数组的定义做更详细的分析。

例如:

int a[10];

在这里,int是类型名,a[10]是一维数组说明符,下面对它进行详细说明。

1.数组名定义和变量名相同,遵循标志符定义规则。

2.数组名后使用方括号括起来的常量表达式,不能用圆括号,下面的用法不对:

int a(10);

3.本例定义了一个名为a的一维数组。

4.常量表达式中的值表示元素的个数,即数组的长度。本例中方括号中的10规定了a数组含有10个元素,它们是a[0]、a[1]、a[2]、?、a[9]。

5.类型名int规定了a数组中每个元素都是整型,在每个元素中只能存放整型数。

6.每个元素只有一个下标,C语言规定每个数组第一个元素的下标总为0(称为数组下标的下界),因此,上例a数组中的最后一个元素的下标应该是9(称为数组下标的上界)。

7.C编译程序将为a数组在内存中开辟如下图所示的10个连续的存储单元,在图中标明了每个存储单元的名字,可以用这样的名字直接来引用每个存储单元。

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]

8.常量表达式中可以包括常量和符号常量,不能包含变量。也就是说,C语言不允许对数组的大小做动态定义,即数组的大小不依赖于程序运行过程中变量的值。例如,下面这样定义数组是不合法的:

int n;

scanf("%d",&n);

int a[n];

.

在一个定义数组的语句中,可以有多个数组说明符,它们之间用逗号隔开,例如:

double a[13],b[28],c[50];

以上语句定义了三个名为a、b、c的双精度类型数组,其中a数组包含13个元素,数组下标的上界为12;b数组包含28个元素,数组下标的上界为27;c数组包含50个元素,数组下标的上界为49;

数组说明符和普通变量名可同时出现在一个类型定义语句中,例如:

char c1,c2,array[33];

引用

在学习一维数组引用的过程中,读者应该注意以下几点。

185 1.一个数组元素实质上就是一个变量名,代表内存中的一个存储单元。一个数组串占有一串连续的存储单元。

2.在C语言中,一个数组不能整体引用。例如:

double x[6];

对于以上定义的x数组,不能用x代表x[0]~x[5]这6个元素。C语言中,数组名中存放的是一个地址常量,它代表整个数组的首地址。

在引用数组元素时,数组元素中下标表达式的值必须是整数,下标表达式值的下限为0。C语言程序在运行过程中,系统并不自动检验数组元素的下标是否越界。因此数组两端都因为可能越界而破坏了其他存储单元中的数据,甚至破坏了程序代码。因此,在编写程序时保证数组下标不越界是十分重要的。

初始化

前面已经介绍了最简单直观的初始化方法,下面我们将对一维数组的初始化方法作一些补充。

1.可以只给一部分元素赋值。例如:

static int a[10]={0,1,2,3,4};

定义a数组有10个元素,但花括弧内只提供5个初值,这表示只给前面5个元素赋初值,后5个元素值为0。

2.如果想使一个数组中全部元素值为0,可以写成:

static int a[10]={0,0,0,0,0,0,0,0,0,0};

不能写成:

static int a[10]={0*10};

这是C与FORTRAN语言不同的,不能给数组整体赋初值。

其实,对static数组不赋初值,系统会对所有数组元素自动赋以0值。即:

static int a[10];

a[0]~a[9]全都被置初值0。

3.对全部数组元素赋初值时,可以不指定数组长度。例如:

static int a[5]={1,2,3,4,5};

可以写成:

static int a[]={1,2,3,4,5};

在第二种写法中,花括号中有5个数,系统就会据此自动定义a数组的长度为5。但若被定义的数组长度与提供初值的个数不相同,则数组长度不能省略。例如,想定义数组长度为10,就不能省略数组长度的定义,而必须写成:

static int a[10]={1,2,3,4,5};

只初始化前5个元素,后5个元素为0。

186 下面通过一个简单的例子对一维数组的相关知识进行一下加深。

一维数组实例

编写程序,定义一个含有20个元素的int类型的数组。依次给数组元素赋奇数1、3、5、7、?;接着按每行十个数输出。

程序如下所示。

程序8-2:P8-2.c

#define N 20

main()

{ int a[N],i,k=1;

for(i=0;i<N;k+=2,i++)

a[i]=k;

for(i=0;i<N;i++)

{ printf("%4d",a[i]);

if((i+1)%10==0)

printf("\n");

}

printf("\n");

}

运行结果如下:

1 3 5 7 9 11 13 15 17 19

21 23 25 27 29 31 33 35 37 39

【二维数组的补充说明】

定义

下面我们来举例并配以图示来加深对二维数组的理解,例如:

float a[3][4],b[5][10];

定义a为3?4(3行4列)的数组,b为5?10(5行10列)的数组,注意不能写成:

float a[3,4],b[5,10];

C语言对二维数组采用这样的定义方式,使我们可以把二维数组看做是一种特殊的一维数组:它所包含的元素又是一个一维数组。上面的例子可以把a看做是一个一维数组,它有3个元素:a[0]、a[1]、a[2],每个元素又是一个包含4个元素的一维数组。见图8-1。可以把a[0]、a[1]、a[2]看做是三个一维数组的名字。上面定义的二维数组可以理解为定义了三个一维数组,即相当于:

float a[0][4],a[1][4],a[2][4];

此处把a[0]、a[1]、a[2]看做一维数组名。C语言的这种处理方法在数组初始化和用指针表示时显得很方便,这在你以后的学习过程中会有所体会。

187 图8-1 二维数组定义示图

C语言中,二维数组中元素排列的顺序是:按行存放,即在内存中先顺序存放第一行的元素,再存放第二行的元素(最右边的下标变化最快,第一维的下标变化最慢),图8-2表示对数组a[3][4]的存放顺序。

图8-2 数组a[3][4]的存放顺序

C语言允许使用多维数组。有了二维数组的基础,再掌握多维数组是不困难的。例如,定义一个三维数组的方法如下:

float a[2][3][4];

多维数组元素在内存中的排列顺序为:第一维的下标变化最快。上述三维数组的元素排列顺序如下:

a[0][0][0]→a[0][0][1]→a[0][0][2]→a[0][0][3]→

a[0][1][0]→a[0][1][1]→a[0][1][2]→a[0][1][3]→

a[0][2][0]→a[0][2][1]→a[0][2][2]→a[0][2][3]→

a[1][0][0]→a[1][0][1]→a[1][0][2]→a[1][0][3]→

a[1][1][0]→a[1][1][1]→a[1][1][2]→a[1][1][3]→

a[1][2][0]→a[1][2][1]→a[1][2][2]→a[1][2][3]

引用

数组元素可以出现在表达式中,也可以被赋值。

例如:

b[1][2]=a[2][3]/2

在使用数组元素时,应该注意下标值应在已定义的数组大小的范围内。常出现的错误如下所述:

int a[3][4];

.

a[3][4]=3;

定义a为3?4的数组,它可用的行下标值最大为2,列下标值最大为3。引用a[3][4]超过了数组的范围。

在引用二维数组元素时要注意以下两点:

188 1.要严格区分在定义数组时用的a[3][4]和引用元素时的a[3][4]的区别。前者a[3][4]用来定义数组的维数和各维的大小,后者a[3][4]中的3和4是下标值,a[3][4]代表某一个元素。

2.一定要把两个下标分别放在两个方括号内。在对上面数组进行引用时,不可以写成:a[2,3]、a[3-1,2*2-1]的形式。

初始化

前面我们只介绍了最简单直观的初始化方法,下面做一些扩展。

1.二维数组初始化时,也可以将所有数据放在一个花括号内,按数组排列的顺序对各元素赋初值。可将上面的例子改写如下:

static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

效果与上例相同。但是第一种方法较好,比较清晰,一行对一行,不至于弄混。使用第二种方法时,如果数据过多,会形成数据写成一大片,容易遗漏,也不易检查。

2.可以对部分元素赋初值,即每行或每列所赋初值个数与数组元素的个数不同。

例如:

static int a[3][4]={{1},{5},{9}};

它的作用是只对各行第1列的元素赋初值,其余元素值自动为0。赋初值后数组各元素用矩阵形式表示的结果如下:

?1 0 0 0?

?5 0 0 0?

?9 0 0 0?

? ?

也可以对各行中的某一个元素进行赋初值:

static int a[3][4]={{1},{0,6},{0,0,11}};

用矩阵形式表示如下:

?1 0 0 0?

?0 6 0 0?

?0 0 11 0?

? ?

这种初始化方法对非0元素少时比较方便,不必将所有的0都写出来,只需输入少量的数据即可。

也可以只对某几行元素赋初值:

static int a[3][4]={{1},{5,6}};

用矩阵形式表示如下:

?1 0 0 0?

?5 6 0 0?

?0 0 0 0?

? ?

189 第三行没有被赋值。也可以对第二行不赋初值:

static int a[3][4]={{1},{},{9,10}};

3.如果对全部元素都赋初值(即提供全部初始数据),则定义数组时对第一维的长度可以不指定,但第二维的长度不能省。

例如:

static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};

与下面的定义是等价的:

static int a[ ][4]={1,2,3,4,5,6,7,8,9,10,11,12};

系统会根据数据总个数分配存储空间,一共12个数据,每行4列,当然可确定为3行。

上面的例子是初值的个数能被第二维的常量表达式除尽时的情况,所得商数就是第一维的大小。

当初值的个数不能被第二维的常量表达式除尽时,则:

第一维大小=所得商数+1

例如:

static int c[][3]={1,2,3,4,5};

等同于:

static int c[2][3]={1,2,3,4,5};

也可以只对部分元素赋值而省略第一维的长度,但应分行赋初值。

例如:

static int a[][4]={{0,0,3},{},{0,10}};

用矩阵形式表示如下:

?0 0 3 0?

?0 0 0 0?

? ?

?0 10 0 0?

由上面介绍的知识可以总结出:C语言在定义数组和表示数组元素时采用a[][]这种两个方括号的方式,对数组初始化十分有用,它使概念清楚,使用方便,不易出错。

二维数组实例

将一个二维数组行和列元素互换,存到另一个二维数组中。如下所示:

?1 4?

?1 2 3? ? ?

a=? ? b= 2 5

? ? ? ?

?4 5 6?

?3 6?

? ?

程序如下所示。

程序8-3:P8-3.c

190 main()

{

static int a[2][3]={{1,2,3},{4,5,6}};

static int b[3][2],i,j;

printf("array a:\n");

for(i=0;i<=1;i++)

{

for(j=0;j<=2;j++)

{

printf("%5d",a[i][j]);

b[j][i]=a[i][j];

}

printf("\n");

}

printf("array b:\n");

for(i=0;i<=2;i++)

{

for(j=0;j<=1;j++)

printf("%5d",b[i][j]);

printf("\n");

}

}

运行结果如下:

array a:

1 2 3

4 5 6

array b:

1 4

2 5

3 6

【字符数组的补充说明】

初始化

在字符数组定义过程中,如果花括号中的初值的个数大于数组的长度,这种情况下,C语言将此作为语法错误处理。如果初值个数小于数组的长度,则将这些字符赋给数组中前面那些元素,其余的元素自动赋给空格。

例如:

static char c[6]={'H','E','L','L','O'};

数组存储状况如下图所示:

H E L L O □

c[0] c[1] c[2] c[3] c[4] c[5]

如果所给的初值的个数与预定的数组长度相同,在定义时可以省略数组长度,系统将

191自动根据初值的个数确定数组的长度。

字符数组实例

要求输出一个钻石图形。

程序如下所示。

程序8-4:P8-4.c

main()

{

static char diamond [][5]={

{' ',' ','*'},

{' ','*',' ','*'},

{'*',' ',' ',' ','*'},

{' ','*',' ','*'},

{' ',' ','*'},

};

int i,j;

for (i=0;i<5;i++)

{

for(j=0;j<5;j++)

printf("%c",diamond[i][j]);

printf("\n");

}

}

运行结果如下:

*

* *

* *

* *

*

【字符串的补充说明】

应用字符串常量对字符数组进行初始化

虽然在C 语言中没有字符串这种数据类型,但允许使用“字符串常量”。在表示字符串常量时,系统对字符串常量自动加一个“\0”作为结束符。

下面我们举几个例子来说明使用字符串常量初始化字符数组的情况。

例如:

static char c[]={"Hello world"};

也可以省略花括号,直接写成下面的形式:

static char c[]="Hello world";

例中不是用单个字符作为初值,而是用一个字符串(两端使用双引号括起来,而不是单引号)作为初值。例中数组c的长度不是双引号中所有字符的个数,而是字符个数再加

192上1,本例中数组c的长度应该是12,而不是11,因为系统自动在字符串常量的最后加上了一个“\0”结束符。所以上例等价于:

static char c[]=

{'H','e','l','l','o',' ','w','o','r','l','d','\0'};

而不等价于:

static char c[]=

{'H','e','l','l','o',' ' ','w','o','r','l','d'};

前者的长度为12,而后者的长度为11。

下面看一下字符串的内存中的存储情况,如果有:

static char c[8]="Hello";

内存中的存储情况如下图所示:

H e l l o \0

可以看到,在内存中数组c的前5个元素是我们所赋的字符串常量,第六个元素为\0,之后的两个元素为空字符。

字符数组与字符串的区别

在C语言中,字符串是用字符数组来处理的。在字符数组中的每个元素都可存放一个字符,但它并不限定数组的最后一个字符是什么;而字符串都是以一个“\0”作为结束符的。因此,如果在字符数组中的有效字符后面加上一个“\0”作为结束符,这种情况下,我们可以把这种一维字符型数组看做是字符串常量。由上可知,只可以在字符数组内存放字符串,不能通过赋值语句对字符串进行直接赋值。可以这么说:字符串是字符数组的一种具体的应用,字符数组的应用范围要比字符串广。

字符串数组

所谓的字符串数组就是数组中的每个元素都存放一个字符串的数组。可以联想到二维数组。一个二维数组可以看做是一个一维数组,而这个一维数组中的每一个元素有都存放一个一维数组。因此,可以这么认为:二维字符数组的第一个下标值决定了字符串的个数;第二个下标值决定了字符串的最大长度,这就是二维数组与字符串数组之间的关系。

【字符串的输入输出】

使用“%s”格式说明符

前面我们介绍过使用“%c”来输出字符数组,对于字符串,也可以利用它对字符进行逐个的输入、输出;只是在输入时在最后需要人为地加入结束标志符“\0”,在输出时用“\0”作为输出结束标志。

在C语言中还提供了整串输入和输出的格式说明符“%s”。

1.用“%s”对字符串进行输出。

193 例如:

static char c[]={"Hello World"};

printf("%s",c);

输出结果如下:

Hello World

其实我们看到的输出结果与内存中存储情况并不完全相同,内存中数组c的最后还有一个结束标志“\0”,但在输出结果中不包含它。

若数组长度大于字符串实际长度,那么也只输出遇到结束标志“\0”为止。

若一个字符数组中包含多个结束标志“\0”,则遇到第一个结束标志“\0”时输出就会结束。

2.用“%s”对字符串进行输入。

例如:

static char c[15];

scanf("%s",c);

执行以上scanf语句时,若从第一列开始输入:

Hello↙

则输入字符串将从c[0]开始依次放在数组c中,在最后一个字母O之后系统会自动增加结束标志“\0”。

在使用scanf进行输入时,需要注意以下几点:

(1)用%s格式符输入字符串时,空格和回车符都作为输入数据的分隔符而不能被读入。因此对以上程序段若输入:

Hello□World↙

则在c中将只存入字符串“Hello”,而不是字符串“Hello□World”。

(2)若输入字符串的长度超过字符数组所能容纳的字符的最大个数时,系统并不报告错误。但是会出现自己所不想出现的情况,相当于下标越界。

(3)当输入项是数组元素的地址时,输入数据将从这一元素开始存放。

gets函数的调用

在调用gets函数和下面马上就要介绍的puts函数时,必须在程序的开头出现包含头文件stdio.h的命令行。

get函数的调用形式如下:

gets(str);

其作用是从终端输入一个字符串到字符数组,并且得到一个函数值,该函数值是字符数组的起始地址。其中str可以包括空格符,直到读入一个换行符为止。换行符读入后,不作为字符串的内容。系统会自动将其用“\0”代替。

puts函数的调用

作用:将一个字符串(以“\0”结束的字符序列)输出到终端。

194 假如已定义str是一个字符数组名,该数组已被初始化为“Hello”,则执行如下语句:

puts(str);

其结果是在终端上输出:Hello。

注意:使用puts和gets函数时只能输入或输出一个字符串,不能写成puts(str1,str2)或gets(str1,str2)。

195【字符串处理函数】

字符串连接函数 strcat

调用形式如下:

strcat (字符数组1, 字符数组2)

作用:连接两个字符数组中的字符串,把字符数组2接到字符数组1的后面,结果存放在字符数组1中。例如:

static char str1[20]={"Hello□"};

static char str2[]={"World"};

printf("%s",strcat (str1,str2));

以上程序段运行结果如下:

Hello□World

在使用这个函数时应注意以下两点:

1.字符数组1必须足够大,以便容纳连接后的新字符串。

2.连接前两个字符串的后面都有一个“\0”,连接时字符1后面的“\0”被取消,只在新串最后保留一个“\0”。

字符串复制函数strcpy

调用形式如下:

strcpy (字符数组1, 字符串2)

作用:将字符数串2复制到字符数组1中去。例如:

static char str1[10],str2[]={"World"};

strcpy (str1,str2);

以上程序段运行结果如下:

字符数组1中的存储变为“World”。

注意点:

1.字符数组1必须足够大,以便容纳被复制的字符串。

2.字符数组1必须写成数组名形式(如str1),字符串2可以是字符数组名,也可以是一个字符串常量。例如:

strcpy(str1,"World");

作用与前相同。

3.复制时连同字符串后面的“\0”一起复制到字符数组1中。

4.不能用赋值语句将一个字符串常量或字符数组直接赋给一个字符数组。如下的例子是不合法的:

str2={"World"};

str1=str2;

196 而只能使用strcpy函数处理。用赋值语句只能将一个字符赋给一个字符型变量或字符数组元素。如下的例子是合法的:

char a[5],c1,c2;

c1='X';c2='Y';

a[0]='W';a[1]='O';a[2]='R';a[3]='L';a[4]='D';

5.可以用strcpy函数将字符串2前面若干个字符复制到字符数组1中去。例如:

strcpy(str1,str2,2);

作用是将str2中前面2个字符复制到str1中去,然后再加上一个“\0”。

字符串比较函数 strcmp

调用形式如下:

strcmp (字符串1, 字符串2)

作用:比较字符串和字符串。例如:

strcmp(str1,str2);

strcmp("Beijing","Dalian");

strcmp(str1,"Beijing");

字符串比较的规则:对两个字符串自左至右逐个字符相比(按ASCII码值大小比较),直到出现不同的字符或遇到“\0”为止。如果全部字符相同,则认为相等;若出现不同的字符,则以第一个不相同的字符的比较结果为准。比较的结果由函数值带回。

1.如果字符串1=字符串2,函数值为0。

2.如果字符串1>字符串2,函数值为为一正整数。

3.如果字符串1<字符串2,函数值为为一负整数。

对两个字符串进行比较时,要注意下面的情况:

不能使用以下形式:

if(str1==str2) printf("ok");

而只能使用:

if(strcmp(str1,str2)==0) printf("ok");

求字符串长度函数strlen

调用形式如下:

strlen (s)

作用:测试字符串的长度,函数的值为字符串中实际长度,不包括“\0”在内。例如:

static char str[10]={"Dalian"};

printf("%d",strlen(str));

输出的结果不是10,也不是7,而是6。

也可以直接测试字符串常量的长度,例如:

197 strlen("Dalian");

strlwr函数

函数作用:将字符串中大写字母转换成小写字母。lwr是lowercase(小写)的缩写。

strupr函数

函数作用:将字符串中小写字母转换成大写字母。upr是uppercase(大写)的缩写。

典 型 例 题

【例题8-1 数组的定义】

题干

以下合法的数组定义是( )。

A)int a[]="string";

B)int a[5]={0,1,2,,3,,4,5};

C)char a="string";

D)char a[]={0,1,2,3,4,5};

解题方法

选项A错误的原因是:它定义的是一个字符串,并且在定义字符串时使用的是int型,所以排除。选项B中的数组中包含了6个字符,而其定义的大小为5,发生越界,因此也可排除。选项C中定义字符串时没有定义其大小,不符合数组定义的格式。可得正确答案为D)。

知识点分析

数组的定义及初始化。

数组的越界。

答案

D

难度提示

简单。本题涉及的都是一些基本的概念,但这些都必须掌握。

【例题8-2 字符串的定义】

题干

以下不能正确进行字符串赋初值的语句是( )。

A)char str[5]="good!";

198 B)char str[]="good!";

C)char *str="good!";

D)char str[5]={'g','o','o','d','\0'};

解题方法

在前面介绍的字符串赋值语句的基础上,我们可以很轻松地解答本题。在答案A中,在good!后面还要加上一个结束标志符“\0”,所以真正在内存中所占用的空间不是5,如将答案A改写成char str[6]="good!";,那么答案A也是正确的。在选项B和D中,利用前面的知识便可知是对的,选项C涉及到指针问题,我们将在后面章节详细介绍。

知识点分析

字符串在进行初始化过程中,系统会自动在字符数组中的有效字符后面加上结束标志符“\0”,所以在计算字符串含有多少字符时,需把它计算在内。

答案

A

难度提示

简单。本题考查应试者对字符串初始化的掌握程度。

【例题8-3 数组的初始化】

题干

下面的程序中( )有错误(每行程序前面的数字是行号)。

程序8-5:P8-5.c

1 #include <math.h>

2 main()

3 {

4 float a[3]={0,0};

5 int i;

6 for(i=0;i<3;i++) scanf("%d",&a[i]);

7 for(i=1;i<3;i++) a[0]=a[0]+a[i];

8 printf("%f\n",a[0]);

9 }

A)没有 B)第4行 C)第6行 D)第8行

解题方法

本题所涉及的就是C语言中数组的初始化问题。关于数组的初始化的特殊性是:如果不对数组进行初始化,那么数组中所有的元素的值都是随机的;如果仅对部分元素初始化,则系统自动将其他元素赋以0值。因此程序中第4行语句的作用等同于语句floata[3]={0,0,0};,这一行是正确的。第6行语句中的数组元素以整型的形式输入,系统会自动进行转换,输入元素的数目也没有超出上限,因此也无错误。第8行也是正确的输出语句。所以本程序体并无任何错误。

199 知识点分析

for循环结构。

一维数组的初始化。

一维数组的初始化可以预先定义数组的大小,如果赋予的字符个数不足,系统将会自动以0补充。在初始化的过程中,也可以通过赋初值来定义数组的大小,就是说这时的数组说明符内的一对括号中可以不指定数组的大小。例如:

int a[]={0,0,0,0,0};

以上的语句等价于下面的语句:

int a[5]={0};

详细的知识点请参考前面我们所介绍的知识点分析及知识点补充与扩展。

答案

A

难度提示

简单。本题只简单涉及了一维数组的初始化问题。

【例题8-4 二维数组的引用】

题干

下述程序的输出结果是( )。

程序8-6:P8-6.c

main()

{

int a[4][4]={

{1,3,5},

{2,4,6},

{3,5,7}

};

printf("%d%d%d%d\n",a[0][3],a[1][2],a[2][1],a[3][0]);

}

A)0650 B)1470 C)5430 D)输出值不定

解题方法

其实本题只要读者明白数组初始化时,数组的下标都是从0开始的,明白这一点之后,这道题便可迎刃而解了。需要输出的第一个元素为a[0][3],即为第1行的第4个数,因为在数组初始化时并没有给a[0][3]赋予初值,系统自动给其赋予0值,可知第一个输出的元素为 0,这样便可判断出正确答案,或可将相关的不正确的答案排除。在本题中,可以将题中所应输出的元素全部列出,如题中需要输出的第二个元素为a[1][2],即为第2行的第3个数,相对应的应该是6。依次类推可以得出第三个元素为5,第四个元素为0,这样便

200可选出正确答案。

知识点分析

二维数组的初始化。

记住这一点:在C语言中规定每个数组的第一个元素的下标总为0(称为数组下标的下界)。

在数组初始化过程中,当所赋初值少于所定义数组的元素个数时,系统会自动将后面的元素赋予初值0。

答案

A

难度提示

简单。这类初值问题只需知道相关的注意点便可轻松得分。

【例题8-5 字符数组的输入与输出】

题干

有以下程序。

程序8-7:P8-7.c

#include <stdio.h>

#defint N 6

main()

{

char c[N];

int i=0;

for(;i<N;c[i]=getchar(),i++);

for(i=0;i<N;putchar(c[i]),i++);

}

输入以下三行,每行输入都是在第一列开始,↙代表一个回车符:

a↙

b↙

cdef↙

则程序的输出结果是( )。

A)abcdef B)a C)a D)a

b b b

c cd cdef

d

e

f

解题方法

201 由下面getchar函数特性可以知道,本题程序中的for语句循环6次,依次接收的字符为‘a’,‘\n’,‘b’,‘\n’,‘c’,‘d’,因此我们可以选出正确答案为 C。因此在解答这类相关的题目时,必须先弄清楚相关的函数的特性,这样便能轻松闯关。

知识点分析

简单的for循环。

输入输出函数:getchar、putchar两个函数。

getchar函数的特性:此函数在输入回车后即读入一个字符,但不取走按回车键所产生的换行符‘\n’。若再有接收字符的语句,‘\n’也会作为一个字符被接收,这正是本题解答关键之所在。

答案

C

难度提示

简单。明白了getchar函数的特性之后便可轻易地解答此类题目。

【例题8-6 字符串的输入与输出】

题干

请选出以下程序段的输出结果是( )。

程序8-8:P8-8.c

#include <stdio.h>

main()

{

char s1[10],s2[10],s3[10],s4[10];

scanf("%s,%s",s1,s2);

gets(s3);

gets(s4);

puts(s1);

puts(s2);

puts(s3);

puts(s4);

}

输入数据如下:(↙代表一个回车符)

aaaa bbbb↙

cccc dddd↙

A) aaaa B) aaaa C) aaaa D

bbbb bbbb bbbb cccc

cccc cccc dddd ddd

cccc dddd ddd eeee ) aaaa bbbb

解题方法

202 解本题的关键在于scanf函数与gets函数的区别,请看下面的知识点分析。

系统在读入上面的数据时,分别用到了scanf函数与gets函数,在此过程中,将aaaa赋给了一维数组s1;将bbbb赋给了s2,因为中间夹着一个空格,这是scanf函数读入字符串时的特性。之后调用gets函数,便遇到了“↙”,使s3无任何内容,系统自动用“\0”代替;后来程序又将cccc dddd赋给了s4,所以当系统输出的时候,将会得到如答案 A 所示的情况。在解本题的时候还应该注意一点,就是数组的赋值问题,如果答案C中的eeee一行换成一个空行,也许很多应试者都会选择答案C,这是一个重点,请读者自己注意。

知识点分析

在scanf函数中使用格式说明%s可以实现字符串的整体输入,字符串中的空格和回车符都将作为输入数据的分隔符而不能被读入。

在使用gets函数时,它从终端读入字符串(包括空格符),直到读入一个换行符为止。换行符读入后,不作为字符串的内容,系统将自动用“\0”代替。

答案

A

难度提示

中级。其实本题真正难度并不大,之所以把这道题定为中级,主要是因为这类题目出的频率不大,容易被应试者忽视,一旦出了,便是容易出错之题,所以读者更应注意。【例题8-7 字符串数组】

题干

以下程序输出的结果是( )。

程序8-9:P8-9.c

#include <stdio.h>

#include <string.h>

main()

{

char w[][10]={"ABCD","EFGH","IJKL","MNOP"},k;

for(k=1;k<3;k++)

printf("%s\n",&w[k][k]);

}

A) ABCD B) ABCD C) EFG D) FGH

FGH EFG JK KL

KL IJ O

M

解题方法

在本题中使用 printf函数来输出字符串,下面知识点分析有详细的解释。在使用格式

203说明符%s实现字符串的整体输出时,printf函数将从函数中相关给出的地址开始输出,直到遇到第一个“\0”为止。再回过头来看本题,当k=1时,从w[1][1],即数组的第二行第二列开始输出一个字符串,遇到第一个“\0”为止,得到:FGH;当k=2时,得到:KL。根据循环可知,此循环只输出了这两个字符串。

知识点分析

简单的for循环。

字符串的输出。在printf函数中使用格式说明符%s可以实现字符串的整体输出,函数的调用形式为:printf("%s",str_adr);。此处str_adr是一个地址值。调用printf函数时,将从这一地址开始,依次输出存储单元中的字符,直到遇到第一个“\0”为止。“\0”为结束标志符,不在输出字符之列。

字符串数组的初始化。字符串数组就是数组中的每个元素都是一个存放字符串的数组。前面已经介绍过,一个二维数组可以看做是一个一维数组,这个一维数组中的每一个元素又是一个一维数组。从这一概念出发,可以将一个二维字符数组视为一个字符串数组。

答案

D

难度提示

中级。考查了字符串的输出和printf函数中格式说明符的应用。

【例题8-8 数组的运算】

题干

以下程序中的输出结果是( )。

程序8-10:P8-10.c

main()

{

int i,k,a[10],p[3];

k=5;

for(i=0;i<10;i++) a[i]=i;

for(i=0;i<3;i++) p[i]=a[i*(i+1)];

for(i=0;i<3;i++) k+=p[i]*2;

printf("%d\n",k);

}

A)20 B)21 C)22 D)23

解题方法

在解此类题目时,一定要仔细,按部就班地来。下面我们来分析一下程序,第一个循环是给数组赋初值,使用第二个循环可以求出数组p的各个元素的值,可得:p[0]=a[0]=0;p[1]=a[2]=2;p[2]=a[6]=6。再通过第三个循环,我们便可以求出k的值,最后的循环相当

204于k=k+2*p[0]+2*p[1]+2*p[2]=5+0+4+12=21。因此我们可以选出正确答案为:B。

知识点分析

简单for循环。

利用for循环进行数组的初始化。

简单的数值运算。

答案

B

难度提示

中级。本题考查了for循环以及利用for循环进行数组的初始化,此类题目只需应试者仔细小心便可得分。

【例题8-9 字符串函数1】

题干

函数调用:strcat(strcpy(str1,str2),str3)的功能是( )。

A)将串str1复制到串str2中后再连接到串str3之后

B)将串str1连接到串str2中后再复制到串str3之后

C)将串str2复制到串str1中后再将串str3连接到串str1之后

D)将串str2复制到串str1中后再将串str1连接到串str3之后

解题方法

前面我们已经介绍过相关的字符串函数作用,本题中在明白函数作用的同时,还要注意运算优先级问题,在本题中,先执行函数strcpy(str1,str2),即先将串str2复制到串str1中,这时字符串str1中所包含的字符串不再是原来的字符串,而是字符串str2,即执行完strcpy(str1,str2)所返回的最后结果是字符串str1;之后再执行的对象是strcat(strcpy(str1,str2),str3),相当于strcat(str1,str3),即将串str3连接到串str1之后。由上可知正确答案应该选C。读者做此类题目之前,应先看清所涉及的函数,并且清楚相关的执行优先级。

知识点分析

字符串处理函数 strcat 是用来连接两个字符数组中的字符串的。而字符串处理函数strcpy 是用来复制字符串的。相关的字符串处理函数在前面我们已经有详细的介绍,请读者参考前面的介绍。

答案

C

难度提示

简单。主要考查应试者对字符串函数的了解程度。

205【例题8-10 字符串函数2】

题干

以下程序的输出结果是( )。

程序8-11:P8-11.c

#include <stdio.h>

main()

{

char str[]="SSSWILTECH1\1\11W\1WALLMP1";

int k;

char c;

for(k=2;(c=str[k])!='\0';k++)

{

switch(c)

{

case 'A': putchar('a');

continue;

case '1': break;

case 1: while((c=str[++k])!='\1'&&c!='\0');

case 9: putchar('#');

case 'E':

case 'L': continue;

default: putchar(c);

continue;

}

putchar('*');

}

printf("\n");

}

A)SWITCH*#WaMP* B)SWITCH*##W#WaMP*

C)SWITCH*#W#aMP* D)SSWITCH*#WaMP*

解题方法

在解这类程序题之前,应试者必须先仔细阅读程序。本题涉及了字符串操作和switch结构及for循环结构的问题。首先应当注意到一些细节问题:k是从2开始的,即串str的第三个字符‘S’开始;整个循环逐个处理str串的字符,不包括结束符;switch中的“case1”对应处理串中的“\1”;switch中的“case 9”对应处理串中的“\11”(此为八进制转义字符)。对于switch中的处理流程,则需理解和注意下述问题(按程序先后顺序说明):

1.字符‘A’转换成‘a’输出。

2.字符‘1’因break语句而跳出switch4结构,执行putchar('*')语句,即‘1’转换成‘*’输出。

3.字符‘\1’导致一个循环,含义是逐个判定后续字符,直到下一个‘\1’为止。因无跳转语句,继续执行putchar('#')语句,可见,两个‘\1’之间的内容(‘\1’)将转换输出

206一个‘#’。

4.字符‘9’转换成‘#’输出,但本例中的‘\11’因夹在两个‘\1’之间而被跳过,故此项无用。

5.字符‘E’和‘L’不处理(二者共用一个continue语句在开始下一次循环)。

6.除以上字符外,原样输出。

上述字符处理后,都继续处理下一字符。

根据以上讨论,字符串str的转换输出如下:

SSS W I L T E C H 1 \1\11W\1 W A LL M P 1

↓ ↓ ↓ ? ↓ ? ↓ ↓ ↓ ↓ ↓ ↓ ? ↓ ↓ ↓

S W I T C H * # W a M P *

由上可得正确答案为A。

知识点分析

字符串相关运算。

for循环。

switch选择结构。

答案

A

难度提示

较难。由于本题涉及的知识面比较广,并且所出的程序中暗藏很多容易诱使应试者上当的地方。本题不但考查了应试者全面的知识,也考查应试者审题的仔细程度。

本 章 练 习

【选择题】

1.下面的程序中( )有错误(每行程序前面的数字是行号)。

程序8-12:P8-12.c

1 #include <stdio.h>

2 main()

3 { float s[5];

4 int i,sz=0;

5 for(i=0;i<5;i++)

6 scanf("%d",s+i);

7 for(i=0;i<5;i++)

8 sz+=s[i];

9 printf("\n%f",(float)sz);

10 }

A)没有错误 B)第4行错误

207 C)第6行错误 D)第9行错误

2.给出以下定义:

char x[]="abcdfeg";

char y[]={'a','b','c','d','e','f','g'};

则正确的叙述为( )。

A)数组x和数组y等价 B)数组x和数组y长度相同

C)数组x的长度大于数组y的长度 D)数组x的长度小于数组y的长度3.下列合法的数组定义是(

A)int a[]= "string";

B)int a[5]={0,1,2,3,4,5};

C)char a= "string";

D)char a[]={0,1,2,3,4,5};

4.以下程序段给数组所有的元素输入数据,请选择正确答案填入( )。

程序8-13:P8-13.c

#include <stdio.h>

main()

{ int a[10],i=0;

while(i<10)

。 )

scanf("%d",______);

}

A)a+(i++) B)&a[i+1]

C)a+i D)&a[i++]

5.设有:

static char str[]="Beijing";

则执行:

printf("%d\n",strlen(strcpy(str,"China")));

后的输出结果为( )。

A)5 B)7

C)12 D)14

6.阅读下面的程序

程序8-14:P8-14.c

#include <stdio.h>

main()

{ int n[2],i,j,k;

for(i=0;i<2;i++)

n[i]=0;

k=2;

for(i=0;i<k;i++)

for(j=0;j<k;j++)

208 n[j]=n[i]+1;

printf("%d\n",n[k]);

}

上面程序的输出结果是( )。

A)不确定的值 B)3

C)2 D)1

7.定义如下变量和数组:

int i;

int x[3][3]={1,2,3,4,5,6,7,8,9};

则下面语句的输出结果是( )。

for(i=0;i<3;i++)

printf("%d ",x[i][2-i]);

A)1 5 9 B)1 4 7

C)3 5 7 D)3 6 9

8.不能把字符串:Hello! 赋给数组b的语句是( )。

A)char b[10]={'H','e','l','l','o','!'};

B)char b[10];b="Hello!";

C)char b[10];strcpy(b,"Hello!");

D)char b[10]="Hello!";

9.若有以下说明:

int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};

char c='a',d,g;

则数值为4的表达式是( )。

A)a[g-c] B)a[4]

C)a['d'-'c'] D)a['d'-c]

10.当执行下面程序且输入:ABC时,输出的结果是( )。

程序8-15:P8-15.c

#include <stdio.h>

#include <string.h>

main()

{ char ss[10]="12345";

strcat(ss,"6789");

gets(ss);

printf("%s\n",ss);

}

A)ABC B)ABC9

C)123456ABC D)ABC456789

11.请选出以下语句的输出结果是( )。

printf("%d\n",strlen("\t\"\065\xff\n));

209 A)5 B)14

C)8 D)输出项不合法,无正常输出12.若有以下定义和语句:

char s[10];

s="abcd";

printf("%s\n",s);

则输出的结果是( )。

A)输出abcd B)输出a

C)输出abcd□□□□□□□□□□ D)编译不通过

13.下述程序输出的结果是( )。

程序8-16:P8-16.c

#include <stdio.h>

main()

{ char s[]="-12345";

int k=0,sign,m;

if(s[k]=='+'&&s[k]=='-')

sign=s[k++]=='+'?1:-1;

for(m=0;s[k]>='0'&&s[k]<='9';k++)

m=m*10+s[k]-'0';

printf("Result=%d",sign*m);

}

A)Result=-12345 B)Result=12345

C)Result=-10000 D)Result=10000

14.下述程序输出的结果是( )。

程序8-17:P8-17.c

#include <stdio.h>

main()

{char ch[7]={"65ab21"};

int I,s=0;

for(I=0;ch[i]>='0'&&ch[i]<='9';I+=2)

s=10*s+ch[i]-'0';

printf("%d\n",s);

}

A)12ba56 B)6521

C)6 D)62

15.下述函数引用中,( )是含有错误的。其中s的定义如下:

char s[10];

A)scanf("%10s",s); B)printf("%.5s",s);

C)puts(s+3); D)gets(s);

16.定义如下数组s:

210 char s[40];

若准备将字符串“This□is□a□string.”记录下来,( )是错误的输入语句。

A)gets(s+2); B)scanf("%20s",s);

C)for(i=0;i<17;i++) D)while((c=getchar())!='\n')

S[i]=getchar(); s[i++]=c;

17.下述程序的输出结果是( )。

程序8-18:P8-18.c

#include <stdio.h>

main()

{int y=18,i=0,j,a[8];

do

{ a[i]=y%2;i++;

y=y/2;

} while(y>=1);

for(j=i-1;j>=0;j--)

printf("%d",a[j]);

printf("\n");

}

A)10000 B)10010

C)00110 D)10100

18.下述程序的输出结果是(

程序8-19:P8-19.c

#include <stdio.h>

main()

{ int n[3][3],i,j;

for(i=0;i<3;i++)

for(j=0;j<3;j++)

n[i][j]=i+j;

for(i=0;i<2;i++)

for(j=0;j<2;j++)

n[i+1][j+1]+=n[i][j];

printf("%d\n",n[i][j]);

}

。 )

A)14 B)0

C)6 D)值不确定

【填空题】

1.阅读下列程序。

程序8-20:P8-20.c

#include <stdio.h>

211main()

{ int i,j,row,colum,m;

static int array[3][3]={

{100,200,300},

{28,72,-30},

{-850,2,6}

}

m=array[0][0];

for(i=0;i<3;i++)

for(j=0;j<3;j++)

if(array[i][j])<m

{ m=array[i][j];

row=i;

column=j;

}

printf("%d,%d,%d\n",m,row,column);

}

上述程序的输出结果是______。

2.设有下列程序:

程序8-21:P8-21.c

#include <stdio.h>

#include <string.h>

main()

{ int i;

char str[10],temp[10];

gets(temp);

for(i=0;i<4;i++)

{ gets(str);

if(strcmp(temp,str<0))

strcpy(temp,str);

}

printf("%s\n",temp);

}

上述程序运行后,如果从键盘上输入:

C++↙

BASIC↙

QuickC↙

Ada↙

Pascal↙

则程序的输出结果是______。

3.若有以下定义:

double w[10];

则w数组元素下标的上限为______,下限为______。

4.下面程序的输出结果是______。

212 程序8-22:P8-22.c

main()

{

int arr[10],i,k=0;

for(i=0;i<10;i++)

arr[i]=i;

for(i=1;i<4;i++)

k+=arr[i]+i;

printf("%d\n",k);

}

5.有如下程序:

程序8-23:P8-23.c

main()

{

int a[3][3]={{1,2},{3,4},{5,6}};

int i,j,s=0;

for(i=1;i<3;i++)

for(j=0;j<=1;j++)

s+=a[i][j];

printf("%d\n",s);

}

该程序运行的输出结果是______。

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

213 第 9 章 函 数

考纲要求:

1.库函数的正确调用。

2.函数的定义方法。

3.函数的类型和返回值。

4.形式参数与实在参数,参数值的传递。

5.函数的正确调用,嵌套调用,递归调用。

6.局部变量和全局变量。

7.变量的存储类别(自动、静态、寄存器、外部),变量的作用域和生存期。

8.内部函数与外部函数。

知识点讲析

【库函数】

库函数,又称为标准函数。这是由系统自己提供的,用户不必自己定义这些函数,可以直接使用它们。每个系统提供的库函数的数量和功能不尽相同,当然有一些基本的函数是共同的。

C语言提供了丰富的库函数,这些函数包括了常用的数学函数,如求平方根值的 sqrt函数。

在调用标准库函数时,用户在源程序include命令中应该包含相应的头文件。例如:

#include <math.h>

include 命令必须以#号开头,系统提供的头文件以.h作为文件的后缀,文件名用一对双引号""或一对尖括号< >括起来。注意,在include命令后面不能加分号。

【函数的类型】

1.从用户使用的角度看函数可以分为以下两种:

(1)标准函数,即库函数。这是由系统提供的,用户不必自己定义这些函数,可以直接使用它们。

(2)用户自己定义的函数,以解决用户的专门需要。

2.从函数的形式看,函数可以分为两类:

214 (1)无参函数。即函数中不需要任何的参数,在调用无参函数时,主调函数并不将数据传送给被调用的函数,一般用来执行指定的一组操作。无参函数可以返回或不返回函数值,但一般不返回函数值的情况占多数。

(2)有参函数。函数中包含相应的参数。在调用函数时,在主调函数和被调用函数之间有参数传递,也就是说,主调函数可以将数据传给被调用函数使用,被调用函数中的数据也可以返回来供主调函数使用。

【函数的定义和返回值】

函数的定义

1.无参函数的定义形式:

类型标识符 函数名()

{声明部分

语句部分

}

2.有参函数的定义形式:

类型标识符 函数名(形式参数1,形式参数2,??)

{声明部分

语句部分

}

例如:

int max(x,y)

int x,y; /*形式参数说明*/

{

int z; /*函数体中的声明部分*/

z=x>y?x:y; /*语句部分*/

return z; /*语句部分*/

}

3.空函数的定义形式:

类型标识符 函数名()

{ }

例如:

dummy() {}

函数的返回值

通过函数调用使主函数能得到一个确定的值,这个值就是函数的返回值。

函数值通过return语句返回,return语句的一般形式如下:

return 表达式; 或 return (表达式); 或 return;

215 return语句中的表达式的值就是所求的函数值。如果return语句后面没有表达式,这时它的作用只是使流程返回到调用函数,没有确定的函数值。在同一个函数中可以有多个return语句,但执行到其中一个return语句时,就结束该函数的调用。

例如:

max(int x,int y)

{

return(x>y?x:y);

}

【形式参数与实在参数】

形式参数与实在参数都是相对于有参函数来说的。在定义函数时函数名后面括号内的变量名称为“形式参数”(简称“形参”);在调用函数时,函数名后面括号内的表达式称为“实在参数”(简称“实参”)。在调用函数时,大多数情况下,主调函数和被调函数之间有数据传递的关系。

函数参数的传递规则:C语言中的参数采用传值方式,形参也具有自己的存储空间,以存放来自实参的值。这种参数处理方式意味着形参变量的操作(如修改值等)与实参变量无关。事实上,因为只是传递实参数值的关系,实参可以是一般表达式而不一定是变量。

【函数的调用】

函数调用的一般形式如下:

函数名 (实在参数列表)

如果实在参数(简称实参)列表包含多个实参时,各个参数之间用逗号隔开。如果函数没有形参,调用形式如下:

函数名 ()

函数后面的括号不能少。

常用的两种函数调用方式如下所示。

1.当函数返回值参与运算时,函数可作为表达式出现在允许表达式出现的任何地方。例如:

z=10*max(x,y);

也可以作为函数的参数,例如:

n=max(a,max(b,c));

2.把函数调用作为一个独立的语句,如:

dummy (); /*注意:后面的分号不能省略*/

216这时不要求函数返回值,只要求函数完成一定的操作。

217【函数的嵌套调用】

C语言中函数的定义都是互相平行、独立的,也就是说在定义函数时,一个函数内不能包含另一个函数。其他一些语言,如PASCAL语言则不同,它允许在定义一个函数时,其函数体内又包含另一个函数的完整定义,这就是嵌套定义,这个内嵌的函数只能被包含它的函数所调用,其他函数不能调用它。

C语言程序不能嵌套定义函数,但可以嵌套调用函数,也就是说,在调用一个函数的过程中又调用另一个函数。两层嵌套调用过程如图9-1所示。

main 函数 m 函数 n 函数

① ② ③ ④

调用m函数 调用n函数 ⑤

⑨ ⑧ ⑦ ⑥

结 束

图9-1 两层嵌套的执行过程

图9-1为两层嵌套的执行过程:①执行main函数的开头部分;②遇函数调用m的操作语句,继而转去执行m函数;③执行m函数的开头部分;④遇到调用n函数的语句,转去执行函数n;⑤执行n函数,如果再无其他嵌套使用的函数,则完成n函数的全部操作;⑥返回调用n函数处,即返回m函数;⑦继续执行m函数中尚未执行的部分,直到m函数结束;⑧返回main函数中调用m函数处;⑨执行main函数的剩余部分直到结束。

我们将在后面的知识点补充与扩展中举例对此进行详细的说明。

【函数的递归调用】

C语言中的函数可以递归调用,即:在调用一个函数的过程中又直接或间接调用该函数本身。前者称为简单递归,后者称为间接递归。例如:

int a(x);

int x;

{ int y,z;

.

z=2*a(y);

.

return (z);

}

在调用函数a的过程中,又要调用a函数,这是直接调用本函数,调用关系见图9-2。

218 a 函数

调用a 函数

图9-2 直接递归调用

下面是间接调用函数:

int a1(x) int a2(t)

int x; int t;

{ {

int y,z; int m,n;

. .

z=2*a2(y); n=3*a1(m);

. .

return (z); return (n);

} }

在调用a1函数过程中要调用a2函数,而在被调用函数a2中又调用了a1函数。调用关系见图9-3。

a1 函数 a2 函数

调用a2函数 调用a1函数

图9-3 间接递归调用

从图中可以看到,这两种递归调用都是无终止的自身调用。显然,程序中不应出现这种无终止的递归调用,而只应出现有限次数的、有终止的递归调用,这可以用if语句来控制,只有某一条件成立时才继续执行递归调用,否则就不再继续。

一个问题要采用递归方法来解决时,必须符合以下三个条件:

1.可以把要解决的问题转化为一个新的问题,而这个新的问题的解法仍与原来的解法相同,只是所处理的对象有规律地递增或递减。

2.可以应用这个转化过程使问题得到解决。

3.必定要有一个明确的结束递归的条件。

【局部变量和全局变量】

在一个函数内部或复合语句内部定义的变量,它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在函数以外是不能使用这些变量的。这称为局部变量,局部变量又称为内部变量。函数的形参也属于局部变量。

在函数外部定义的变量,称为全局变量,又称为外部变量。全局变量可以被本文件的其他函数所共用。

219【变量的存储类别】

前面我们从变量的作用域(即从空间)角度来分,可以分为全局变量和局部变量。

现在我们从另外一个角度:从变量值存在的时间(即生存期)角度来分,可以分为静态存储变量和动态存储变量。有的地方又称为自动类和静态类。局部变量既可以说明成自动类,又可以说明成静态类;而全局变量则只能是静态类。

所谓静态存储方式是指在程序运行期间分配固定的存储空间的方式。而动态存储方式则是在程序运行期间根据需要动态地分配存储空间的方式。

下面的图9-4显示的是一个C程序在内存中的存储映像。

程序区

静态存储区

动态存储区

图9-4 C程序在内存中的存储映像

图中将供用户使用的存储空间分为三个部分:程序区、静态存储区和动态存储区。其中数据分别存放在静态存储区和动态存储区中。

全局变量存放在静态存储区中,在程序开始执行时给全局变量分配存储区,程序执行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不是动态地分配和释放的。

在动态存储区中存放如下数据:①函数形参变量。在调用函数时给形参变量分配存储空间。②局部变量(未加static说明的局部变量,即自动变量)。③函数调用时的现场保护和返回地址等。对以上这些数据,在函数调用开始时分配动态存储空间,函数结束时释放这些空间。在程序执行过程中,这种分配和释放是动态的,如果在一个程序中两次调用同一个函数,分配给此函数中局部变量的存储空间地址可能是不相同的。如果一个程序包含若干个函数,每个函数中的局部变量的生存期并不等于整个程序的执行周期,它只是程序执行周期的一部分,根据函数调用的需要,动态地分配和释放存储空间。

在 C语言中有四个与两种存储类别有关的说明符,具体包括:auto(自动)、register(寄存器)、static(静态)和 extern(外部)。这些说明符通常与类型名一起出现,它们可以放在类型名的左边,也可以放在类型名的右边。例如:

auto int i,j;

等价于:

int auto i,j;

【局部变量的作用域和生存期】

auto变量

220 当在函数内部或复合语句内定义变量时,如果没有指定存储类或使用了auto说明符,系统就认为所定义的变量具有自动类别。因此:

float a;

就等价于:

auto float a;

auto变量的存储单元被分配在内存的动态存储区。每当进入函数体(或复合语句)时,系统会自动为auto变量分配存储单元;退出时自动释放这些存储单元另作他用。因此,这类局部变量的作用域是从变量定义时开始,到函数体(或复合语句)结束为止。例如:

void sub(float a)

{

int .i;

.

if(i>0)

{

int n;

.

printf("%d\n",n);

} .

.

}

这里,变量i、n都是auto变量。但i的作用域是整个sub函数;而n的作用域仅限于if子句内。注意:局部变量的定义必须放在所在函数体(或复合语句)中全部可执行语句之前。

所有自动类局部变量的存储单元都是在进入这些局部变量所在的函数体(或复合语句)时生成,退出其所在的函数体(或复合语句)时消失(变为无定义)。这就是自动类局部变量的“生存期”。当再次进入函数体(或复合语句)时,系统将为它们另行分配存储单元;因此变量的值不可能被保留。由于随着函数的频繁调用,动态存储区内为某个变量分配的存储单元位置随程序的运行而改变,变量中的初值也就随之而变,所以未赋初值的自动变量其值不定,称为“无定义”。

自动变量的赋初值是在程序运行过程中进行的,每进入一次函数体(或复合语句),就赋一次指定的初值。使用这类局部变量的最突出优点是:可在各函数之间造成信息隔离,不同函数中使用了同名变量也不会相互影响。

从而可避免因不慎赋值所导致的错误,影响到其他函数。

register变量

寄存器变量也是自动类变量。它与auto变量的区别仅在于:用register说明的变量建议编译程序将变量的值保留在CPU的寄存器中,而不是像一般变量那样,占内存单元。程序运行时,访问寄存器内的值要比访问内存中的值快得多。因此当程序对运行速度有较高要求时,把那些频繁引用的少数变量,指定为register变量,有助于提高程序的运行速度。

例如:以下函数power用以计算xn。

程序9-1:P9-1.c

221 main()

{

int s;

s=power(5,3);

printf("%d\n",s);

}

power(int x,register int n)

{

register int p;

for(p=1;n;n--)

p=p*x;

return p;

}

在power函数中,用作循环变量的n和存放连乘积的变量p被定义为register变量,以便加快求值速度。

说明:

1.CPU 中寄存器的数目是有限的,因此只能说明少量的寄存器变量。在一个函数中,允许说明为寄存器变

量的数目不仅取决于CPU的类型,也与所用的C编译程序有关。当没有足够的寄存器来存放指定的变量,或编译程序认为指定的变量不适合放在寄存器中时,将自动按auto变量来处理。因此,register说明只是对编译程序的一种建议,而不是强制性的。

2.由于register变量的值是放在寄存器内而不是放在内存中,所以register变量没有地址,也就不能对它进行地址运算。

3.register变量的说明应尽量靠近其使用的地方,用完之后尽快释放,以便提高寄存器的利用效率。这可以通过把对register变量的说明和使用放在复合语句中来实现。

静态存储类的局部变量

当在函数体(或复合语句)内部,用static来说明一个变量时,可以称该变量为静态局部变量。静态局部变量的作用域仍与auto、register类的变量一样;但它与前者有两点本质上的区别:

1.在整个程序运行期间,静态局部变量在内存的静态存储区中占据着永久性的存储单元。即使退出函数以后,下次再进入该函数时,静态局部变量仍使用原来的存储单元。由于并不释放这些存储单元,因此这些存储单元中的值得以保留;因而可以继续使用存储单元中原来的值。由此可知,静态局部变量的生存期将一直延长到程序运行结束。

2.静态局部变量的初值是在编译时赋予的,在程序执行期间不再赋予初值。对未赋初值的静态局部变量,C编译程序自动给它赋初值0。

静态局部变量的上述特点,对于编写那些在函数调用之间必须保留局部变量值的独立函数是非常有用的。

【全局变量的作用域和生存期】

222 全局变量只有静态一种类别。对于全局变量可使用extern和static两种说明符。

全局变量是在函数外部任意位置上定义的变量,它的作用域是从变量定义时开始,到整个文件结束止。例如:

void fun1(void);

void fun2(void);

int sum; /*定义全局变量*/

main()

{? sum++; ?}

void fun1(void)

{? sum++; ?}

int test; /*定义全局变量*/

void fun2(void)

{? sum++;test=1; ?}

此处变量sum和test都是全局变量。sum是在整个源程序的开始定义的,它的作用域是整个程序(覆盖了三个函数)。而test是在函数fun2前定义的,它的作用域从定义处开始直到程序结束(只覆盖了fun2函数)。

全局变量的使用,相当于为函数之间的数据传递另外开辟了一条通道。上述三个函数中对变量sum的自增1都是有效的。因此,使用中要特别小心,以免造成意料不到的改变。

全局变量的生存期是整个程序的运行期间。

若全局变量和某一函数中的局部变量同名,则在该函数中,此全局变量被屏蔽,访问的是局部变量,与同名的全局变量不发生任何关系。

例如,有如下程序。

程序9-2:P9-2.c

int sum; /*定义全局变量*/

void fun(void);

main()

{

sum=10;

printf("**main(1)**:%d\n",sum);

fun();

printf("**main(2)**:%d\n",sum);

}

void fun(void)

{

int sum; /*定义局部变量*/

sum=20;

printf("** fun **:%d\n",sum);

}

程序运行的结果如下:

**main(1)**:10

** fun **:20

**main(2)**:10

223 即:全局变量sum的作用域是除函数fun以外的整个程序;局部变量sum的作用域仅是函数fun内部。

虽然全局变量作用域大,生存期长,用起来似乎方便灵活;但需要提醒读者的是,除十分必要外,一般不提倡使用全局变量,原因主要有以下三个方面:

1.不论是否需要,全局变量在整个程序运行期间都占用内存空间。

2.全局变量必须在函数以外定义,降低了函数的通用性;影响了函数的独立性。

3.使用全局变量,容易因疏忽或使用不当而导致全局变量中的值意外改变,从而引起副作用,产生难以发现的错误。

【内部函数与外部函数】

函数都是全局的,因为不能在函数内部定义另一个函数。但是,根据函数能否被其他源程序调用,将函数区分为内部函数和外部函数。

内部函数

如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型前面加static,即:

static 类型标识符 函数名(行参表)

例如:

static int fun(x,y)

内部函数又称静态函数。使用内部函数,可以使函数只局限于所在文件,如果在不同的文件中有同名的内部函数,互不干扰。

外部函数

在定义函数时,如果冠以关键字extern,表示此函数是外部函数,如:

extern int fun(x,y)

函数fun可以为其他文件调用,如果在定义函数时省略extern,则隐含为外部函数。

在需要调用此函数的文件中,要用extern声明所调用函数的原型(返回值是整型的函数除外)。

补充与扩展

【标准库函数的调用】

224 对库函数的一般调用形式如下:

函数名 (参数列表)

在C语言中,库函数的调用可以以两种形式出现。

1.出现在表达式中。例如,求y= x+5可以通过以下语句调用sqrt函数来求得:

y=sqrt(x)+5;

在这里,函数的调用出现在赋值号右边的表达式中。又如:

for(printf(":"); scanf("%d",&x), t=x; printf(":"));

在此,函数printf和scanf都作为表达式而出现在for语句后的一对圆括号中。

2.为独立的语句完成某种操作。例如有以下调用:

printf("**********\n");

在printf函数调用之后加了一个分号,这就构成了一条独立的语句,完成在一行里输出指定个数的“*”号的操作。

读者在调用库函数之前必须对相应的函数有所了解,必须满足相关函数的条件,在这个前提下,读者可以根据需要调用适当的函数,加入相应的参数,这样便可轻易地得到计数结果或指定的操作。

【函数返回值的补充说明】

1.return语句中表达式的值的类型必须与函数首部所说明的类型一致。若类型不一致,则以函数值的类型为准,由系统自动进行转换,即函数类型决定返回值的类型。

下面我们来看一个相关的例子。例如:

程序9-3:P9-3.c

main()

{

float a,b;

int c;

scanf("%f,%f",&a,&b);

c=max(a,b);

printf("Max is %d\n",c);

}

max(x,y)

float x,y;

{

float z;

z=x>y?x:y;

return z;

}

运行结果如下:

225 1.5,2.5

Max is 2

函数max被自动定义为整型,而return语句中的z为实型,二者不一致,按上述规定,先将z转换为整型,然后max(x,y)返回一个整型值2给主调函数main。如果将main函数中的c定义为实型,用%f格式符输出,也是输出2.000 000。

有时,可以利用这一特点进行类型转换,如在函数中进行实型运算,希望返回的是整型量,可让系统去自动完成类型转换。但这种做法往往使程序不清晰,可读性降低,容易弄错,而且并不是所有的类型都能互相转换的(如实数与字符类型数据之间),因此建议初学者不要采用这种方法,而应做到使函数类型与return返回值的类型一致。

2.如果被调用函数中没有return语句,并不返回一个确定的、用户所希望得到的函数值,但实际上,函数并不是不返回值,而只是不返回有用的值,返回的是一个不确定的值。

3.为了明确表示“不返回值”,可以用“void”定义“无类型”(或称“空类型”)。

例如:

void fun1()

{ ? }

void fun2()

{ ? }

这样,系统就保证不使函数返回任何值,即禁止在调用函数中使用被调用函数的返回值。如果已将fun1和fun2函数定义为void类型,则下面的用法就是错误的:

a=fun1();

b=fun2();

编译时会给出出错信息。

为使程序减少出错,保证正确调用,凡不要求返回函数值的函数,一般应定义为“void”类型。在许多C语言书籍的相关程序中大量用到void类型函数,读者应对此有所了解。

【形式参数与实在参数的补充说明】

1.在定义函数中指定的形参变量,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时函数中的形参才被分配内存单元。在调用结束后,形参所占的内存单元也被释放。

2.实参可以是常量、变量或表达式,例如:

max(0,x-y);

但要求它们必须有确定的值。在调用时将实参的值赋给形参变量(如果形参是数组名,则传递的是数组首地址,而不是变量的值)。

3.在被定义的函数中,必须指定形参的类型。

4.实参与形参的类型应一致。如果这两者不一致,则会发生“类型不匹配”的错误。在C语言中,字符型与整型可以互相通用。

226 5.规定,实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。

在调用函数时,给形参分配存储单元,并将实参对应的值传递给形参,调用结束后,形参单元被释放,实参单元仍保留并维持原值。

因此,在执行一个被调用函数时,形参的值如果发生改变,并不会改变主调函数的实参的值。

6.在对形参类型作说明时,可以有以下两种方法。

例如:

int max(int x,int y)

{ ? }

等价于:

int max(x,y)

int x,y;

{ ? }

又如:

float fun(array,n)

int array[10],n;

等价于:

float fun(int array[10],int n)

【函数调用的补充说明】

在一个函数中调用另一函数(即被调用函数)需要具备哪些条件呢?

1.首先被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。但光有这一条件还不够。

2.如果使用库函数,一般还应该在本文件开头用#include命令将调用有关库函数时所需用到的信息包含到本文件来。例如,前几章中已经用过的:

#include <stdio.h>

其中“stdio.h”是一个“头文件”,stdio是Standard input&output的缩写,意为“标准输入输出”。在stdio.h中放了输入输出库函数所用到的一些宏定义信息。如果不包含“stdio.h”文件中的信息就无法使用输入输出库中的函数。同样,使用数学库中的函数,应该用

#include <math.h>

.h是头文件所用的后缀,标志头文件(header file)。有关宏定义等概念请参见后面章节的介绍。

3.如果要使用用户自己定义的函数,而且该函数与调用它的函数(即主调函数)在同一个文件中,一般还应该在文件的开头或在主调函数中对被调函数的类型进行声明,这种

227类型声明的一般形式为:

返回值的类型名 函数名();

例如:计算a与b的值的平方根之和。

程序9-4:P9-4.c

#include <stdio.h>

#include <math.h> /*在函数中使用数学函数sqrt,因此要写此行*/

main()

{

float add(); /*对被调用函数的声明*/

float a,b,c;

scanf("%f%f"&a,&b);

c=add(a,b);

printf("Sum is %f\n",c);

}

float add(float x,float y) /*定义add函数,返回值类型是float型*/

{

double z;

z=sqrt(x)+sqrt(y); /*相当于sqrt((double)x)或sqrt((double)y),

因为sqrt要求double型实参*/

return z; /*相当于return (float)z;,

因为返回值类型为float型*/

}

运行结果如下:

4 9↙

Sum is 5.000000

请注意程序第四行:“float add();”,是对被调用的函数add的返回值作类型声明。C语言规定,在以下几种情况下可以不在调用函数前对被调用函数作类型声明。

1.如果函数的值(函数的返回值)是整型或字符型,可以不必加以声明,系统对它们自动按整型声明。

2.如果被调用函数的定义出现在主调函数之前,可以不必加以声明,因为编译系统已经事先知道了已定义的函数类型,会自动处理的。

3.如果在所有函数定义之前,文件的开头,在函数的外部已声明了函数类型,则在各个主调函数中不必对所调用的函数再作类型声明,例如:

char fun1(); /*以下3行在所有函数之前*/

float fun2();

int fun3();

main()

{ ? } /*不必声明它所调用的函数的类型*/

char fun1(char c1,char c2) /*定义fun1函数*/

{ ? }

float fun2(float x,float y) /*定义fun2函数*/

{ ? }

int fun3(float j,float k) /*定义fun3函数*/

228 { ? }

除了以上三种情况外,都应该按上述介绍的方法对所调用函数的返回值作类型声明,否则编译时就会出现错误。

4.ANSI标准在对函数进行类型声明时,除了规定要声明函数的类型外,还要求声明参数个数与参数类型,这是为了在编译时检查函数的调用是否合法(如,实参的个数和类型与形参是否一致)。这样的声明称为用函数原型化声明。如果函数原型的一般形式是:

函数类型 函数名(参数类型1,参数类型2,??,参数类型n);

或:

函数类型 函数名(参数类型1 参数名l,参数类型2 参数名2,??);

这种形式就是把定义函数时的函数首部(即第一行)搬过来加一个分号即可。

程序9-4中对函数的声明可以改用函数原型声明,可用下面二者之一:

float add(float,float);

float add(float x,float y);

用原型声明比用上例中的声明方法要好,便于发现错误。如果在上例中调用add函数时,实参的个数和类型与原型中的声明不一致,编译时就会显示错误信息,提醒程序编制者修改。在编制应用程序时,提倡使用函数原型的声明方式。

下面我们再举一例来加深对函数调用及参数传递情况的了解。

例如:阅读下面程序,判断程序能否交换主函数中a和b的值。

程序9-5:P9-5.c

main()

{

int a=1,b=2;

swap(a,b); /*a,b是实参*/

printf("实参:a=%d,b=%d\n",a,b); /*输出实参a,b的值*/

}

swap(int a,int b) /*a,b是形参,开辟与实参a,b不同的存储单元*/

{

int c;

c=a; /*交换形参a,b中的值*/

a=b;

b=c;

printf("形参:a=%d,b=%d\n",a,b); /*输出形参a,b的值*/

} /*调用完毕,释放a,b,c所占存储单元*/

运行结果如下:

形参:a=2,b=1

实参:a=1,b=2

由上可知,程序没能把主函数中a和b的值交换。原因是:虽然实参和形参变量名一样,但它们各占有不同的存储单元,而且是单向传递。请读者仔细思考其中的道理,这对以后的学习会有帮助。

229【嵌套调用实例说明】

现在我们通过实例对嵌套调用进行进一步的说明。

例如:用弦截法求方程的根。

x3-5x+16x-80=02

具体解题方法如下:

1.取两个不同点x1、x2,如果f(x1)和f(x2)符号相反,则(x1,x2)区间必有一个根。如果f(x1)和f(x2)符号相同,则应改变x1和x2,直到f(x1)和f(x2)异号为止。注意x1和x2的值不应差太大,以保证(x1,x2)区间只有一个根。

2.连接f(x1)和f(x2)两点,此线(即弦)交x轴于x,见图9-5。

x点坐标可用下式求出:

x1?f(x2)?x2?f(x1)

x= f(x)?f(x)

2 1

再从x求出f(x)。

3.若f(x)与f(x1)同符号,则根在(x,x2)区间内,此时将x作为新的x1。如果f(x)与f(x2)同符号,则表示根在(x1,x)区间内,将x作为新的x2。

4.重复步骤1和3,直到|f(x)|<ε为止,ε为一个很小的数,例如10-6。此时认为f(x)≈0。 y

读者可根据上述思路画出相应的流程图。 f(x)

2

分别用几个函数来实现个部分功能。

1.用函数f(x)来求x的函数:

x3-5x+16x-80=0。2

2.用函数xpoint(x1,x2)来求f(x1)和f(x2)的连

线与x轴的交点x的坐标。 x x x

1 2

3.用函数root(x1,x2)来求(x1,x2)区间的那 0 x个实根。显然,执行root函数过程中要用到函数 f(x)

xpoint,而执行xpoint函数过程中要用到f函数。 1

图9-5 求解f(x)

程序9-6:P9-6.c

#include <math.h>

float f(x) /*定义f函数,以实现x-5x+16x-80=0*/3 2

float x;

{

float y;

y=((x-5.0)*x+16)*x-80.0;

return y;

}

float xpoint(x1,x2) /*定义xpoint函数,求出弦与x轴的交点*/

float x1,x2;

{

230 float y;

y=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1));

return y;

}

float root(x1,x2) /*定义root函数,求近似根*/

float x1,x2;

{

int i;

float x,y,y1;

y1=f(x1);

do

{

x=xpoint(x1,x2);

y=f(x);

if(y*y1>0) /*f(x)与f(x1)同符号*/

{

y1=y;

x1=x;

}

else

x2=x;

}while (fabs(y)>=0.0001)

return x;

}

main() /*主函数*/

{

float x1,x2,f1,f2,x;

do

{

printf("input x1,x2:\n");

scanf("%f,%f",&x1,&x2);

f1=f(x1);

f2=f(x2);

}while (f1*f2>=0);

x=root(x1,x2);

printf("A root of equation is %8.4f",x);

}

运行结果如下:

input x1,x2:

2,6

A root of equation is 5.0000

从程序可以看到:

1.在定义函数时,函数名为f、xpoint、root的三个函数是互相独立的,并不互相从属。这三个函数均被定义为实型。

2.三个函数的定义均出现在main函数之前,因此在main函数中不必对这三个函数作类型说明。

3.程序从main函数开始执行。先执行一个do-while循环,作用是:输入x1和x2,

231判别f(x1)和f(x2)是否异号,如果不是异号则重新输入x1和x2,直到满足f(x1)与f(x2)异号为止。然后用函数调用root(x1,x2)求根x。调用root函数过程中,要调用xpoint函数来求f(x1)与f(x2)连线的交点x。在调用xpoint函数过程中要用到函数f来求x1和x2的相应的函数值f(x1)与f(x2)。这就是函数的嵌套调用。

4.在root函数中要用到求绝对值的函数fabs,它是对实型数求绝对值的标准函数。它属于数学函数库,故在文件开头用:

#include <math.h>

即把使用数学库函数时所需用到的有关信息包含进来。

【递归调用实例说明】

例如:用递归的方法求n!。

求n!可用以下数学关系表示:

n! = ?1 当 n=0 时

?n(n?1)! 当 n>0 时

?

从以上表达式可以看到,当n>0时,求n!的问题可以转化为求n(n?1)!的新问题,而求(n?1)!的解法与原来求n!的解法相同,只是运算对象由n变成了n?1;而求(n?1)!的问题又可以转化为求(n?1)(n?2)!的新问题,??每次转化为新问题时,运算对象就递减1,直到运算对象的值递减至0时,阶乘的值为1,递归不应当再进行下去,这就是求n!这个递归算法的结束条件。

程序9-7:P9-7.c

fac(int n)

{

int t;

if(n==1||n=0)

return 1;

else

{

t=n*fac(n-1);

return t;

}

}

main()

{

int m,y;

printf("Enter m: ");

scnaf("%d",&m);

if(m<0)

printf("Input data error!\n");

else

{

y=fac(m);

232 printf("\n%d!=%d\n",m,y);

}

}

程序运行结果如下:

4↙

4!=24

在main函数中,整个问题的求解全靠一个fac函数来解决。函数调用过程如图9-6所示。请读者自己思考函数嵌套调用和递归调用之间的区别。

main n=4 n=3 n=2 n=1

输出 t=4*fac(3) t=3*fac(2) t=2*fac(1) t=1

fac(4)

fac(4)=24 fac(3)=6 fac(2)=2 fac(1)=1

图9-6 fac函数调用过程

当函数自己调用自己时,系统将自动把函数中当前的变量和形参暂时保留起来,在新一轮的调用过程中,系统将为该次调用的函数所用到的变量和形参,开辟另外的存储单元。因此,递归调用的层次越多,同名变量所占用的存储单元也就越多。当本次调用的函数运行结束时,系统将释放本次调用时所占用的存储单元,程序的执行流程返回到上一层的调用点,同时取用当初进入该层时,函数中的变量和形参所占用的存储单元中的数据。

【局部变量和全局变量】

局部变量

前面我们介绍了局部变量的基本概念,下面我们这里举例进行详细说明。

例如,有如下程序:

float fun1(int a) /*函数fun1*/

{

int .b,c;

. 在此函数内a、b、c有效

.

}

char fun2(int x,int y) /*函数fun2*/

{

int i,j; 在此函数内x、y、i、j有效

}

main() /*主函数*/

{

int .m,n;

. 在主函数内m、n有效

.

}

233 说明:

1.主函数main中定义的变量(m,n)也只有主函数中有效,并不因为是在主函数中定义而在整个文件或程序中有效。主函数也不能使用其他函数中的定义的变量。

2.不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。若在函数fun1和fun2中定义了相同的变量x和y,它们在内存中占有不同的单元,互不混淆。

3.形式参数也是局部变量。在函数fun1中的形参a,也只在fun1函数中有效。其他的函数不能调用。

4.在一个函数内部,可以在复合语句定义变量,这些变量只在本复合语句中有效,离开该复合语句该变量就无效,释放内存单元。

全局变量

全局变量的有效范围为:从定义变量的位置开始到本源文件结束。

例如,有如下程序:

float e=2.0,f=10.0; /*e,f为全局变量,下面三个函数都可以调用*/

float fun1(int a) /*定义函数fun1*/

{

int b,c;.

. /*a,b,c只在本函数中使用*/

} .

char c1,c2; /*c1,c2为全局变量,下面两个函数可以调用*/

char fun2(int x,int y) /*定义函数fun2*/

{

int i,j;.

. /*x、y、i、j在此函数内有效*/

} .

main() /*定义主函数*/

{

int m,n;.

. /*m、n在主函数内有效*/

}

说明:

1.设全局变量的作用是增加函数间数据联系的渠道。由于同一文件中的所有函数都能引用全局变量的值,因此,如果在一个函数中改变了全局变量的值,就能影响到其他函数,相当于各个函数间有直接的传递通道。由于函数的调用只能带回一个返回值,因此有时可以利用全局变量增加与函数联系的渠道,从函数得到一个以上的返回值。

2.建议不在必要时不要使用全局变量,因为①全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元;②它使函数的通用性降低了,因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去,但若该外部变量与其他文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性;③使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬时各个外部变量的值。在各个函数执行时都可能改变外部变量的值,程

234序容易出错,因此要限制使用全局变量。

3.如果外部变量在文件开头定义,则在整个文件范围内都可以使用该外部变量,如果不在文件开头定义,按上面规定作用范围只限于定义点到文件终了。如果在定义点之前的函数想引用该外部变量,则应该在该函数中用关键字extern作“外部变量声明”。此声明表示这些变量是在该函数后面定义的外部变量。在函数内部,从extern声明之处起,可以使用它们。

4.如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。

当全局变量定义在后,引用它的函数在前时,应该在引用它的函数中用extern对此全局变量进行说明,以便通知编译程序:该变量是一个已在外部定义了的全局变量,已经分配了存储单元,不需再为它另外开辟存储单元。这时其作用域从extern说明处起,延伸到该函数末尾。

注意:全局变量的说明与全局变量的定义不同,变量的定义(开辟存储单元)只能出现一次,在定义全局变量时,不可使用extern说明符;而对全局变量的说明时,则可以多次出现在需要的地方,这时必须用extern进行说明。这不是考试重点,而且不提倡经常使用全局变量,所以这里就做这些介绍,有需要者请查阅相关书籍。

【变量存储类别小结】

1.一共有四种存储类别:

static —— 声明静态内部变量或外部静态变量

auto —— 声明自动局部变量

register —— 声明寄存器变量

extern —— 声明变量是已定义的外部变量

2.从作用域的角度分,有全局变量和局部变量。它们可采取的存储类别为:

自动变量,即动态局部变量(离开函数,值就消失)

局部变量 静态局部变量(离开函数,值仍保持)

寄存器变量(离开函数,值就消失)

形式参数(可以定义为自动变量或寄存器变量)

全局变量 静态局部变量(只限本文使用)

外部变量(非静态的外部变量,允许其他文件引用)

3.从变量存在的时间来区分,可以分为静态存储和动态存储两种类型。静态存储是程序整个运行期间始终存在的,而动态存储则是在调用函数或进入分程序时临时分配单元的。

自动变量(本函数内有效)

动态存储 寄存器变量(本函数内有效)

形式参数

235 静态局部变量(本函数内有效)

静态存储 静态外部变量(本函数内有效)

4.作用域和生存期外部变量(其他文件可以引用)

如果一个变量在某一范围内能被引用,则称该范围为该变量的作用域。换言之,一个变量在其作用域内都能被有效引用。

一个变量占据内存单元的时间,称为该变量的生存期。或者说,该变量值存在的时间就是该变量的生存期。

见表9-1。

表9-1 变量存储类别

变量存储类别 函数内 函数外

作用域 生存期 作用域 生存期自动变量、寄存器变量 √ √ ? ?静态局

部变量 √ √ ? √静态外部变量 √ √ √ √外部变量 √ √ √ √

典 型 例 题

【例题9-1 变量存储类别】

题干

以下只有在使用时才为该类型变量分配内存的存储类说明是( )。

A)auto和static B)auto和register

C)register和static D)extern和register

解题方法

这道题涉及的是变量存储类别的问题,相关概念前面已有介绍。auto和register类型的变量只有在该函数被调用时,它们才被分配内存空间,在函数返回时消失。相对的 extern和static类型的变量占用空间是永久性的。由此可知正确答案为B。

知识点分析

auto和register变量的主要特点:

1.它们只定义在复合语句内,而系统只在程序执行到复合语句时才给变量分配空间,在该复合语句结束时又收回这些空间另行分配,故这些变量的值是不可继承的。

2.此类变量都是局部可用的,它们只能用在被定义的复合语句内。

3.若不对此类变量赋以初值,其值是随机的。

4.register类型的变量比auto类型的变量有更快的执行速度。

236 static和extern变量的主要特点:

1.此类变量在程序编译时已经确定,所占用的内存空间一直持续到程序执行完毕,其值具有可继承性。

2.变量的初始化(指在定义的同时赋值)在编译时确定,程序执行后不再处理。系统为未初始化的变量自动赋以0值。

3.静态变量只能用于被定义的复合语句内,是局部的。

4.外部变量从定义的位置直至文件末都可用。利用extern修饰,可以将使用范围进一步扩大。

答案

B

难度提示

简单。本题涉及的都是一些基本的概念的理解。

【例题9-2 变量作用域和生存期】

题干

以下程序的输出结果是( )。

程序9-8:P9-8.c

int f()

{

static int i=0;

int s=1;

s+=i;

i++;

return s;

}

main()

{

int i,a=0;

for(i=0;i<5;i++)

a+=f();

printf("%d\n");

}

A)20 B)24 C)25 D)15

解题方法

例1-1中我们提到了变量的存储类别的问题,并且当在函数内部或复合语句内定义变量时,如果没有指定存储类,则系统默认所定义的变量具有自动(auto)类别。而auto类型的变量只有在该函数被调用时,它们才被分配内存空间,在函数返回时消失。相对的static类型的变量占用空间是永久性的。所以在主函数中的for循环执行的过程中,调用的f()函

237数中的s值,因为有“int s=1;”语句,每次循环的取值都为1;而i的值则不同,它是逐步累加的。第一次循环结束时,f()=1,i=1,a=1;第二次循环结束时,f()=2,i=2,a=3;第三次循环结束时,f()=3,i=3,a=6;第四次循环结束时,f()=4,i=4,a=10;第五次循环结束时,f()=5,i=5,a=15,循环结束。由上可知,答案应该选D。

知识点分析

简单for循环。

静态(static)局部变量与自动(auto)类型变量之间的区别,前面已经有相关的介绍,这里不再重复。

答案

D

难度提示

中级。本题考查应试者对各种变量的作用域和生存期的了解,利用简单的 for循环来检验变量的变化情况。

【例题9-3 函数的调用和说明】

题干

若有以下程序。

程序9-9:P9-9.c

#include <stdio.h>

void f(int n);

main()

{

void f(int n);

f(5);

}

void f(int n)

{

printf("%d\n",n);

}

则以下叙述中不正确的是( )。

A)若只在主函数中对函数f进行说明,则只能在主函数中正确调用函数f

B)若在主函数前对函数f进行说明,则在主函数和其后的其他函数中都可以正确

调用函数f

C)对于以上程序,编译时系统会提示出错信息:提示对f函数重复说明

D)函数f无返回值,所以可用void将其类型定义为无值型

解题方法

注意题目中要求选出的是不正确的一项。选项A中的说明是正确的,因为函数说明可

238以放在调用函数内的说明部分,如在main函数内部进行说明,则只能在main函数内部才能识别该函数。B选项的知识点都可以在下面的知识点分析中得到答案。选项D中的说明也正确,因为函数f不返回值,为了明确表示“不返回值”,可以用“void”定义“无类型”(或称“空类型”)。选项C中,此程序编译时并不会产生错

误信息,在C语言中这样对函数的重复说明是可以的,如果两次说明的类型不一致,则读者可以用全局变量和局部变量的知识来推理函数说明的“作用范围”。

知识点分析

在C语言中,除了主函数外,对于用户定义的函数要遵循“先定义,后使用”的原则。凡是未在调用前定义的函数,C编译程序都默认函数的返回值为int类型。对于返回值为其他类型的函数,若把函数的定义放在调用之后,应该在调用之前对函数进行说明。

当在所有函数的外部、被调用之前说明函数时,在函数说明的后面所有位置上都可以对该函数进行调用。

答案

C

难度提示

简单。本题考查读者对函数调用及其说明的掌握程度,并且涉及了一点函数返回值的问题,这类题目可以说是“送分型”的题目。

【例题9-4 直接递归调用】

题干

下述程序的输出结果是( )。

程序9-10:P9-10.c

long fun(int n)

{

long s;

if(n==1||n==2)

s=2;

else

s=n-fun(n-1);

return s;

}

main()

{

printf("%1d\n",fun(3));

}

A)1 B)2 C)3 D)4

解题方法

这是一个典型的涉及函数递归调用的程序,程序的结束递归的条件是n=1或者n=2。

239在程序的主函数中调用fun函数,其中的实参n的大小为3,所以执行else后面的语句,产生递归调用,s=3?fun(3-1),这个语句中递归调用函数fun,而这时函数的实参已经变成了2,满足递归结束条件,执行if后面的语句,得s=2,即函数fun(3?1)返回值为2,代入s=3-fun(3-1),则s=3-2=1,在主函数中打印出这个值,可知答案为A。

知识点分析

C语言的特点之一就是允许函数的递归调用,即:函数可以直接或间接地调用自己。前者称为简单递归,后者称为间接递归。本题中所涉及的只是简单递归。初学者在学习这一块的时候总是感觉茫然,而且在调试程序的过程中经常出现死机,或者说死循环,这是因为没有给出明确的使函数递归调用结束的条件。在使用递归调用的时候应该满足三个条件,前面已经有讲解,请参照前面的说明。

答案

A

难度提示

较难。函数的递归调用本身是一个比较难的知识点,虽然本题涉及的只是简单的递归调用,但对于初学者来说还是有点难度,需要读者多看书、看程序、上机锻炼。【例题9-5 间接递归调用】

题干

下列程序执行后的输出结果是( )。

程序9-11:P9-11.c

#include <stdio.h>

void func1(int i);

void func2(int i);

char st[]="hello,friend";

void func1(int i)

{

printf("%c",st[i]);

if(i<3)

{

i+=2;

fun2(i);

}

}

void func2(int i)

{

printf("%c",st[i]);

if(i<3)

{

240 i+=2;

fun1(i);

}

}

main()

{

int i=0;

func1(i);

printf("\n");

}

A)hello B)hel C)hlo D)hlrn

解题方法

相关的知识点上一例中已经有所介绍。下面我们进入主函数,主函数中i=0,调用func1(i)函数,即func1(0),因为i=0,语句“printf("%c",st[0]);”打印出字符‘h’;继续执行func1函数,因为i<3,则i=i+2=2,之后执行func2函数,即执行func2(2),函数func2中的语句“printf("%c",st[2]);”打印出字符‘l’;继续执行func2函数,因为i=2<3,则i=i+2=4,之后执行func1函数,即执行func1(4),函数func1中的语句“printf("%c",st[4]);”打印出字符‘o’;这是因为 i=4>3,已经满足结束递归调用的条件,退出递归调用,即程序最终输出的结果是‘hlo’,可知正确答案为C。

知识点分析

函数的间接递归调用。在本例中,主函数调用func1函数,而函数func1中又调用了func2函数,在这之前的函数调用可以归结为函数的嵌套调用。然而函数func2中又调用了func1函数,结合前面的函数调用,函数func1和func2都间接地调用了自己,这就是函数间接调用的过程。

答案

C

难度提示

较难。本例主要的难度体现在函数的间接递归调用上。

【例题9-6 局部变量和全局变量】

题干

以下程序的输出结果是( )。

程序9-12:P9-12.c

#include <stdio.h>

int a,b;

void fun()

{

a=100;

241 b=200;

}

main()

{

int a=5,b=7;

fun();

printf("%d%d",a,b);

}

A)100200 B)57 C)200100 D)75

解题方法

程序的开头定义了a、b为全局变量,即从a、b定义开始到程序结束,a、b都起作用,在程序的全部执行过程中都占用存储单元。主函数中又定义了a、b,这是局部变量,只在主函数中起作用;子函数中也定义了a、b,只在子函数fun中有效。由上可以知道,在主函数中要输出a、b的值,而在主函数中只有主函数中定义的a、b才有效,所以输出的结果为‘57’,正确答案为B。

知识点分析

全局变量和局部变量之间的差异。

全局变量在程序的全部执行过程中都占用存储单元,而局部变量则是在需要时才开辟单元。如果全局变量在文件开头定义,则在整个文件范围内都可以使用该变量,如果不是在文件开头定义,则作用范围只限于定义处到文件末尾。如果在同一源文件中,全局变量与局部变量同名,则在局部变量作用范围内,全局变量不起作用。

答案

B

难度提示

中级。本题主要考查的是全局变量与局部变量之间的作用范围之间的差异。

本 章 练 习

【选择题】

1.C语言中形参的默认存储类别是( )。

A)自动(auto) B)静态(static)

C)寄存器(register) D)外部(extern)

2.下面对函数嵌套的叙述中,正确的叙述为( )。

A)函数定义可以嵌套,但函数调用不能嵌套

B)函数定义不可以嵌套,但函数调用可以嵌套

C)函数定义和调用均不能嵌套

242 D)函数定义和调用均可以嵌套

3.下面关于形参和实参的说法中,正确的是( )。

A)形参是虚设的,所以它始终不占存储单元

B)实参与它所对应的形参占用不同的存储单元

C)实参与它所对应的形参占用同一个存储单元

D)实参与它所对应的形参同名时可占用同一个存储单元

4.关于全局变量,下列说法正确的是( )。

A)本程序的全部范围

B)离定义该变量的位置最接近的函数

C)函数内部范围

D)从定义该变量的位置开始到本文件结束

5.调用一个函数,此函数中没有return语句,下列说法正确的是:该函数( )。

A)没有返回值

B)返回若干个系统默认值

C)能返回一个用户所希望的函数值

D)返回一个不确定的值

6.以下函数调用语句中含有( )个实参。

fun((exp1,exp2),(exp3,exp4,exp5));

A)1 B)2

C)4 D)5

7.下述程序输出的结果是( )。

程序9-13:P9-13.c

#include <stdio.h>

void fun(int a,int b,int c)

{

a=456;

b=567;

c=678;

}

main()

{

int x=10,y=20,z=30;

fun(x,y,z);

printf("%d,%d,%d",z,y,x);

}

A)30,20,10 B)10,20,30

C)456,567,678 D)678,567,456

8.下述程序输出的结果是( )。

程序9-14:P9-14.c

243fun(int a,int b,int c)

{

c=a*a+b*b;

}

main()

{

int x=22;

fun(4,2,x);

printf("%d",x);

}

A)20 B)21

C)22 D)23

9.下述程序输出的结果是( )。

程序9-15:P9-15.c

#include <stdio.h>

main()

{

int i=2,p;

p=f(i,i+1)

printf("%d",p);

}

int f(int a,int b)

{

int c;

c=a;

if(a>b)

c=1;

else

if(a==b) c=0;

else c=-1;

return c;

}

A)-1 B)0

C)1 D)2

10.下述程序输出的结果是(

程序9-16:P9-16.c 。 )

#include <stdio.h>

main()

{

int a=8,b=1,p;

p=func(a,b);

printf("%d,",p);

p=func(a,b);

printf("%d\n",p);

}

func(int x,int y)

244{

static int m=2,k=2;

k+=m+1;

m=k+x+y;

return(m);

}

A)14,29 B)14,24

C)14,8 D)14,30

11.下述程序输出的结果是(

程序9-17:P9-17.c

#include <stdio.h>

int s=13;

。)

int fun(int x,int y)

{

int s=3;

return(x*y-s);

}

main()

{

int m=7,n=5;

printf("%d\n",fun(m,n)/s);

}

A)1 B)2

C)7 D)10

12.下述程序输出的结果是(

程序9-18:P9-18.c

#include <stdio.h>

main()

{

int x=1;

fun(fun(x));

}

fun(int n)

{

。 )

static int s[3]={1,2,3};

int i;

for(i=0;i<3;i++)

s[i]+=s[i]-n;

for(i=0;i<3;i++)

printf("%d,",s[i]);

printf("\n");

return(s[n]);

}

A)1,3,5 B)1,3,5 C)1,3,5 D3,7

24513.下述程序输出的结果是( )。

程序9-19:P9-19.c

#include <stdio.h>

void fun1()

{

int x=0;

x++;

printf("%d,",x);

}

void fun2()

{

static int x; )1,3,51,5,9 1,3,5 0,4,8 ?1,

x++;

printf("%d,",x);

}

main()

{

int i;

for(i=0;i<3;i++)

{

fun1();

fun2();

}

}

A)1,1,1,1,1,1 B)1,1,1,1,2,3

C)1,1,2,2,3,3 D)1,1,2,1,3,1

14.下述程序输出的结果是( )。

程序9-20:P9-20.c

#include <stdio.h>

int m=3;

main()

{

int m=10;

printf("%d\n",fun(5)*m);

}

fun(int k)

{

if(k==0)

return m;

return(fun(k-1)*k);

}

A)360 B)3600

C)1080 D)1200

15.以下程序的输出结果是( )。

246 程序9-21:P9-21.c

#include <stdio.h>

char cchar(char ch)

{

if(ch>='A'&&ch<='Z')

ch=ch-'A'+'a';

return ch;

}

main()

{

int k=0;

char s[]="ABC+abc=defDEF";

for(k=0;k<strlen(s[]);k++)

{

s[k]=cchar(s[k]);

}

printf("%s\n",s);

}

A)abc+ABC=DEFdef B)abc+abc=defdef

C)abcaABCDEFdef D)abcabcdefdef

【填空题】

1.函数嵌套调用与递归调用的区别是 ______。

2.下面程序的运行结果是______。

程序9-22:P9-22.c

f()

{

int x=7;

static y=4;

x+=1;

y+=1;

printf("x=%d,y=%d\n",x,y);

}

main()

{

f();

f();

}

3.下面程序的输出结果是______。

程序9-23:P9-23.c

main()

{

int i=2,x=5,j=7;

fun(j,6);

247 printf("i=%d,j=%d,x=%d\n",i,j,x);

}

fun(int i,int j)

{

int x=7;

printf("i=%d,j=%d,x=%d\n",i,j,x);

}

4.若已定义:“int a[10],i;”,以下fun函数的功能是:在第一个循环中给前10个数组元素依次赋1、2、3、4、5、6、7、8、9、10;在第二个循环中使a数组前10个元素中的值对称折叠,变成1、2、3、4、5、5、4、3、2、1,请填空。

fun(int a[])

{

int i;

for(i=1;i<=10;i++)

__(1)__=i;

for(i=0;i<5;i++)

__(2)__=a[i];

}

5.若函数fun的类型为void,且有以下定义和调用语句:

#define M 50

main()

{

int a[M].;

.

fun(a);.

.

}

定义fun函数首部可以用三种不同的形式,请至少填写两种:s;使用同一种风格)。

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □

2.本章复习的效果好吗? 不好 □

3.本章疑难问题汇总:

4.对自己说点什么吧:

248 第 10 章 编译预处理

、 (2) 否 □ 还行 □、 (3) 好□注意:形参请用 (1)

考纲要求

1.宏定义:不带参数的宏定义;带参数的宏定义。

2.“文件包含”处理。

知识点讲析

【宏定义】

宏定义是编译预处理方式中的一种。在C语言源程序中允许用一个标志符来表示一个字符串,称为“宏”。被定义为“宏”的标志符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。

宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。

编译预处理4

这是在进行编译的第一遍扫描(词法扫描和语法分析)之前所做的工作。编译预

处理是C语言特有的一个重要功能,它由预处理程序负责完成。当对一个源文件

进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理

完毕自动进入对源程序的编译。C提供三种预处理功能:宏定义、文件包含和条

件编译。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有

利于模块化程序设计。

【不带参数的宏定义】

用一个指定的标志符来代表一个字符串,其定义的一般形式为:

#define 标识符 字符串

其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。

“标志符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。使用这种宏定义就可以在下面的程序中用标志符代替字符串。这样就使得程序代码变得简洁而且易读。从而减小了编写程序的工作量,也增加了

程序的可读性。一个好的C语言程序,必须有良

249好的宏定义。

250【带参数的宏定义】

带参数的宏定义不仅是进行简单的字符串替换,还要进行参数替换。它的一般定义形式为:

#define 宏名(形参表) 字符串

其中#、define等的意义同不带参数的宏定义。

【“文件包含”处理】

“文件包含”处理(又称“文件包括”)是指一个源文件可以将另外一个指定的源文件的全部内容包含进来,即将另外的文件包含到本文件之中。C语言用#include命令来实现“文件包含”的操作。其一般的形式为:

#include "文件名"

文件包含命令的功能是把指定的文件插入该命令行位置取代该命令行,从而把指定的文件和当前的源程序文件连成一个源文件。

文件包含的用处

在程序设计中,文件包含是很有用的。一个大的程序可以分为多个模块,由

多个程序员分别编程。这样就可以节省程序设计人员的重复劳动。有些公用的符

号常量或宏定义等可单独写成一个文件,在其他文件的开头用包含命令包含该文

件即可使用。这样,可避免在每个文件开头都去书写那些公用量,从而节省时间,

并减少出错。

补充与扩展

C语言的编译预处理语句一共有三种。其中条件编译语句在计算机等级考试二级C语言的考试大纲中不作要求。读者如果有兴趣,可以参考其他相关书籍,在此不作赘述。【不带参数的宏定义的扩展介绍】

不带参数的宏定义比较好理解,读者只要在宏观上把握住宏所代表的数据就可以了。比如在下面这个程序中,只要把握住P的宏观定义,那么在其后的程序中就可以将P看做是printf,或者说可以将程序中的P代换为printf而程序不变,参见下面的程序10-1。

程序10-1:p10-1.c

#define P printf

251 #define D "%d\n"

#define F "%f\n"

main(){

int a=5, c=8;

float b=3.8, d=9.7;

P(D F,a,b);

P(D F,c,d);

}

程序执行结果

5

3.8

8

9.7

程序分析

宏定义是用宏名(标志符)来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不做任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。

宏定义不是C的说明或C语句,所以在行末不必加分号,如加上分号则连分号也一起置换。例如:

#define PI 3.14;

.

area=PI*r*r;

则宏展开后的语句为:

area=3.14;*r*r;

这显然是错误的。

宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。可以用#undef命令终止宏定义的作用域。如:

#define PI 3.14;

main()

{ .

.

} .

.

#undef PI

则PI的有效范围为#define到#undef之间。

宏名在源程序中若用引号括起来,则预处理程序不对其做宏代换。

宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换,如程序10-2所示。

程序10-2:p10-2.c

#define R 3.0

#define PI 3.1415926

#define L 2*PI*R

main()

252{

printf("L=%f\n",L);

}

程序运行结果

L=18.849556

实际上,对L进行层层替换后得到2*3.14*3.0,所以得到上述结果。

习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母。

可用宏定义表示数据类型,使书写方便。比如:

#define STU struct stu

在程序中可用STU作变量说明:

STU body[5],*p;

#define INTEGER int

在程序中即可用INTEGER作整型变量说明:

INTEGER a,b;

应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理时完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标志符具有类型定义说明的功能。请看下面的例子:

#define PIN1 int*

typedef (int*) PIN2;

从形式上看这两者相似, 但在实际使用中却不相同。下面用PIN1,PIN2说明变量时就可以看出它们的区别:

PIN1 a,b;

在宏代换后变成:

int *a,b;

表示a是指向整型的指针变量,而b是整型变量。然而:

PIN2 a,b;

表示a,b都是指向整型的指针变量。因为PIN2是一个类型说明符。由这个例子可见,宏定义虽然也可表示数据类型,但毕竟是做字符代换。在使用时要分外小心,以避免出错。

对“输出格式”作宏定义,可以减少书写麻烦。比如程序10-1中的宏定义#define D "%d\n"

253 #define F "%f\n"

【带参数的宏定义的扩展介绍】

先讨论全国计算机等级考试二级笔试试题19xx年4月的一道考题,从中读者可以看出带参数的宏定义一般的用法及其意义。

以下程序的输出结果是______。

程序10-3:p10-3.c

#include<stdio.h>

#define FUDGE(y) 2.84+y

#define PR(a) printf("%d",(int)(a))

#define PRINT1(a) PR(a);putchar('\n')

main()

{

int x=2;

PRINT1(FUDGE(5)*x);

}

程序分析

在这个程序中我们可以看到它包含三个带参宏定义。也就是说,这道题主要是考查应试者对带参宏定义的掌握。对于带参宏定义,读者应该掌握以下内容:

带参数的宏定义中,宏名和形参表之间不能有空格出现。因为预编译时,遇空格即认为宏名部分已经结束,后面部分为代替字符串。如:

将宏定义

#define MAX(a,b) (a>b)?a:b

写为:

#define MAX (a,b) (a>b)?a:b

将被认为是无参数宏定义,宏名MAX代表字符串

(a,b)(a>b)?a:b

宏展开时,宏调用语句:

max=MAX(x,y);

将变为:

max=(a,b)(a>b)?a:b(x,y);

这显然是错误的。

在带参数宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用它们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。

在宏定义中的形参是标志符,而宏调用中的实参可以是表达式。实例可以参见程序10-4。

254 程序10-4:p10-4.c

#define SQ(y) (y)*(y)

main()

{

int a,sq;

printf("input a number: ");

scanf("%d",&a);

sq=SQ(a+1);

printf("sq=%d\n",sq);

}

程序分析

第一行即为宏定义,形式参数为y。程序第七行宏调用中实参为a+1,是一个表达式,在宏展开时,用a+1代换y,再用(y)*(y) 代换SQ,得到如下语句:

sq=(a+1)*(a+1);

这与函数的调用是不同的,函数调用时要把实参表达式的值求出来再赋予形参。 而宏代换中对实参表达式不作计算直接地照原样代换。

带参的宏定义与函数的区别

1.函数调用时,先求出实参表达式的值,然后代入形参。带参数的宏只是

进行字符替换。函数的实参与形参都要定义类型,二者的类型要求一致,而宏

不存在类型问题,宏名无类型,参数也无类型,它仅是一个符号,在展开时代

入相应字符。

2.使用宏时,每使用一次,源程序相应增长,函数调用不因调用次数的增多

使源程序变长。

3.宏替换不占运行时间,只是在编译时展开,占用编译时间,而函数调用

是在运行时处理的,占用运行时间,因为需要分配单元、保留现场、传递值及

返回等。

4.在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把

实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递

的问题。

在宏定义中,字符串内的形参通常要用括号括起来以避免出错。在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的。如果去掉括号,把程序改为以下形式。

程序10-5:p10-5.c

#define SQ(y) y*y

main()

{

int a,sq;

printf("input a number: ");

scanf("%d",&a);

255 sq=SQ(a+1);

printf("sq=%d\n",sq);

}

程序运行结果

input a number:3

sq=7

同样输入 3,但结果却是不一样的。问题在哪里呢? 这是由于代换只作符号代换而不作其他处理而造成的。宏代换后将得到以下语句:

sq=a+1*a+1;

由于a为3故sq的值为7。这显然与题意相违,因此参数两边的括号是不能少的。即使在参数两边加括号还是不够的,请看下面程序。

程序10-6:p10-6.c

#define SQ(y) (y)*(y)

main()

{

int a,sq;

printf("input a number: ");

scanf("%d",&a);

sq=160/SQ(a+1);

printf("sq=%d\n",sq);

}

本程序与前例相比,只把宏调用语句改为:

sq=160/SQ(a+1);

运行本程序如输入值仍为3时,希望结果为10。但实际运行的结果如下:

input a number:3

sq=160

为什么会得这样的结果呢?分析宏调用语句,在宏代换之后变为:

sq=160/(a+1)*(a+1);

a为3时,由于“/”和“*”运算符优先级和结合性相同,则先作160/(3+1)得40,再作40*(3+1)最后得 160。为了得到正确答案应在宏定义中的整个字符串外加括号,程序修改如下所示。

程序10-7:p10-7.c

#define SQ(y) ((y)*(y))

main()

{

int a,sq;

printf("input a number: ");

scanf("%d",&a);

sq=160/SQ(a+1);

256 printf("sq=%d\n",sq);

}

以上讨论说明,对于宏定义不仅应在参数两侧加括号,也应在整个字符串外加括号。

宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。在宏调用时,把这些语句又代换到源程序内,看下面的例子。

程序10-8:p10-8.c

#define SSSV(s1,s2,s3,v) s1=l*w;s2=l*h;s3=w*h;v=w*l*h;

main()

{

int l=3,w=4,h=5,sa,sb,sc,vv;

SSSV(sa,sb,sc,vv);

printf("sa=%d\nsb=%d\nsc=%d\nvv=%d\n",sa,sb,sc,vv);

}

程序第一行为宏定义,用宏名SSSV表示4个赋值语句,4个形参分别为4个赋值符左部的变量。在宏调用时,把4个语句展开并用实参代替形参。使计算结果送入实参之中。【“文件包含”处理】

“文件包含”处理是非常有用的,利用它可以节省编程者的重复劳动。下面利用例子说明。例如在文件file1.c中有这样的语句:

#include"file2.c"

文件包含处理的过程是:在编译预处理时,将file2.c的全部内容复制到file1.c的相应位置,即file1.c中原来写有 #include"file2.c" 命令处。这样文件file2.c就被包含到file1.c中。在接下来的编译过程中,file2.c和file1.c作为一个单位进行编译。注意,编译时并不是作为两个文件连接,而是作为一个源程序编译,得到一个目标文件。因此,被包含的文件file2.c也应该是源文件的一部分而不应该是目标文件。

在使用#include命令时,如果文件名是用" "括起来的,则编译系统首先在源程序文件所属的文件目录中寻找所包含的文件,如果没有找到,再按照系统给定的标准方式检索其他目录;如果文件名是用< >括起来的,则编译程序将只按照系统给定的标准方式来检索文件目录。也就是说,用" "的 #include命令的检索路径将包含使用< >的 #include命令的检索路径。

一个#include 命令只能指定一个被包含文件,如果要包含多个文件,需要使用与文件数相同的#include 命令,并且与宏定义命令类似,文件包含也是可以嵌套的,即在一个被包含文件中还可以包含另外一个被包含文件。

每一种C编译系统都给出一些标准库函数,这些库函数并不是C语言的一部分,而是人们根据需要编制并提供给用户使用的。不同的编译系统提供的库函数是有差别的,无论在数目、函数名还是在功能上都有很大不同。这就需要读者在使用这些函数时查阅所用系统的手册。这些库函数在引用时需要在文件开始处使用#include命令。比如:

#include"stdio.h"

257 #include"math.h"

等。

典 型 例 题

【例题10-1 带参数的宏定义】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

设有如下宏定义:

#define MYSWAP(z,x,y) {z=x; x=y; y=z;}

以下程序段通过宏调用实现变量a、b内容交换,请填空。

float a=5,b=16,c;

MYSWAP( __,a,b);

解题方法

针对这种类型的题,考生首先应对题目中所反映的基础知识有所了解。比如本题是关于带参数的宏定义的一道题。看出这一点之后,就要努力回忆关于基础知识的知识点(参看知识点讲析的内容)。找出解题的突破口,仔细分析所给的程序,按照程序流程,一步步地得出结果。

知识点分析

C语言中的宏定义分为不带参数和带参数的两种。这两种结构各自有其特点。不带参数的宏定义一定是用程序员根据自己喜好和习惯所定义的宏名来代替某一已知的字符串。如:

#define PI 3.1415926

那么在以后的程序中,只要出现PI,就可以用3.141 592 6来代之,即PI=3.141 592 6。而对于带参数的

宏定义又有其特殊的一面。如本题中程序所示,将在下面的程序中用MYSWAP(z,x,y)这样一块字符串来代替{z=x; x=y; y=z;}。

这样做的结果就是完成题干中所说的实现变量x,y之间的交换。而z的作用是一中间变量。也就是说,原来的程序片段:

float a=5,b=16,c;

MYSWAP( __,a,b);

完全可以用下面的语句实现:

float a=5,b=16,c;

c=a;

a=b;

258 b=c;

这样就完成了a、b之间的数值交换。

正确答案

C

难度提示

中级。这道题主要考查应试者的单一知识点,没有形成综合题,所以其难度并不大。只要掌握好带参数的宏定义这一知识点就可以作出解答。

【例题10-2 形参和其后的字符串的匹配】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下程序的输出结果是( )。

程序10-9:p10-9.c

#define M(x,y,z) x*y+z

main()

{

int a=1,b=2, c=3;

printf("%d\n", M(a+b,b+c, c+a));

}

A)19 B)17 C)15 D)12

解题方法

本题跟例题10-1非常接近。解题方法可以参看例题10-1。需要指出的一点是在判断形参和其后的字符串的匹配时要十分小心,以免弄错。

知识点分析

分析程序,在宏定义#define M(x,y,z) x*y+z中,形参x,y,z分别与x*y+z中的相应字符对应,即宏定义表明:

M(x,y,z)=x*y+z

式子成立。这样就可以将程序中的M()变成它所代表的式子。而在

printf("%d\n", M(a+b,b+c, c+a))

语句中,M()的实参是a+b,b+c和c+a。则结果就是(a+b) * (b+c) + (c+a)。

正确答案

A

难度提示

中级。这道题主要考查应试者关于带参数的宏定义这一知识点,比前一道题有一些综

259合性,但其难度亦不大。只要掌握好带参数的宏定义这一知识点就可以作出解答。

260【例题10-3 运算符的应用】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下程序的输出结果是______。

程序10-10:p10-10.c

#define MAX(x,y) (x)>(y)?(x):(y)

main()

{ int a=5,b=2,c=3,d=3,t;

t=MAX(a+b,c+d)*10;

printf("%d\n",t);

}

解题方法

本题的程序非常简单,主要问题就是分析带参数的宏定义和C语言运算符的应用。

知识点分析

分析程序,在宏定义#define MAX(x, y) (x)>(y)?(x):(y)中,形参x,y分别与x*y+z中的相应字符对应,即宏定义表明 MAX(x, y)=(x)>(y)?(x):(y)式子成立。这样就可以将程序中的MAX()变成它所代表的式子。而在t=MAX(a+b,c+d)*10语句中,MAX()的实参是a+b和c+d。则结果就是(a+b)>( c+d)?( a+b):( c+d)*10。

正确答案

7

难度提示

高级。这道题主要考查应试者关于带参数的宏定义和C语言的运算符这两个知识点,有一定的综合性,其难度较大。

【例题10-4 带参数的宏定义】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序的输出结果是( )。

程序10-11:p10-11.c

#define SQR(X) X*X

main()

261 { int a=16, k=2, m=1;

a=(k+a)/SQR(k+m);

printf("%d\n",a);

}

A)16 B)2 C)9 D)1

解题方法

本题的程序非常简单,问题仍然是分析带参数的宏定义。

知识点分析

分析程序,在宏定义#define SQR(X) X*X中,形参X分别与X*X中的相应字符对应,即宏定义表明 SQR(X)=X*X式子成立。这样就可以将程序中的SQR(X)变成它所代表的式子。而在 a=(k+a)/SQR(k+m);语句中,SQR()的实参是 k+m。则结果就是a=(k+a)/[(k+m)*(k+m)]。

正确答案

B

难度提示

初级。这道题主要考查应试者关于带参数的宏定义这一知识点。

【例题10-5 宏定义的嵌套引用】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

有如下程序。

程序10-12:p10-12.c

#define N 2

#define M N+1

#define NUM 2*M+1

main()

{int i;

for(i=1;i<=NUM;i++)

printf("%d\n",i);

}

该程序中的for循环执行的次数是( )。

A)5 B)6 C)7 D)8

解题方法

本题考查的是宏定义的嵌套引用。主要就是考查考生对宏定义的理解。

知识点分析

262 在进行宏定义时,可以引用已经定义的宏名,可以层层置换。

比如在本例中,开始已经定义了一个宏名N(#define N 2),而在定义第二个宏名时,引用了第一次的定义,即M=2+1,而在定义第二个宏名时,引用了第二次的定义,即NUM=2*(2+1)+1,在这个例子中,进行了两次宏置换。这一点要在做题时注意。

明白了这一点后,这道题目就迎刃而解了。通过分析主函数中的简单程序就可以得出结论。

正确答案

B

难度提示

中级。这道题主要考查应试者关于宏定义的层层置换这样一个知识点,没有其他复杂的程序阅读和繁琐的语法分析,所以其难度不大。读者经过一段时间的学习后应该比较容易作答。

【例题10-6 带参数的宏定义】

(全国计算机等级考试二级笔试试卷19xx年9月)

题干

以下程序的输出结果是( )。

程序10-13:p10-13.c

#define f(x) x*x

main( )

{ int a=6,b=2,c;

c=f(a)/f(b);

printf("%d \n",c);

}

A)9 B)6

C)36 D)18

解题方法

本题的程序同样非常简单,问题仍然是分析带参数的宏定义。

知识点分析

分析程序,在宏定义#define f(x) x*x中,形参x分别与x*x中的相应字符对应,即宏定义表明 f(x)=x*x式子成立。这样就可以将程序中的 f(X)变成它所代表的式子。而在c=f(a)/f(b);语句中,f()的实参是a和b。则结果就是a=a*a/b*b。

正确答案

A

难度提示

263初级。这道题主要考查应试者关于带参数的宏定义这一知识点。

264【例题10-7 宏定义和文件包含】

(全国计算机等级考试二级笔试试卷19xx年4月)

题干

下面程序的输出结果是______。

程序10-14:p10-14.c

#include

#define PT 5.5

#difine s(x) PT*x*x

main()

{ int a=1,b=2;

print ("%4.lf\n",s(a+b));

}

解题方法

本题主要考查读者知识点有三:不带参数的宏定义、带参数的宏定义和文件包含处理。考查的能力是读程序的能力。知识点是读者必须掌握的,在掌握了考试所要求的知识点后并不意味着读者已经掌握了C语言。要掌握C语言,必须能读懂一些简单的程序,并且自己能够编写出一些简单的程序。只有这样,才有可能达到考试要求。

知识点分析

这道题目中的宏定义比较简单。有一点需要注意的是,定义的宏在程序中是可嵌套引用的。这在前面已讲过多次。在此不再赘述。

关于“文件包含”处理的知识:由于文件包含使用得非常广泛,所以读者必须对它十分清楚,而且从整个C语言体系来看,这个知识点比较简单,很容易被读者接受,所以它并不是难点。

掌握了这些以后这道题就比较好做了。只要再知道 printf函数的用法和浮点型数据的表示方法就可以了,而这两个知识点是非常普通的。

正确答案

9.5

难度提示

中级。知识点较多,所以难度有所上升。

本 章 练 习

【填空题】

265 1.C语言提供了三种预处理语句,它们是 、 和条件编译。

2.下面程序中for循环的执行次数是______,输出结果为______。

程序10-15:p10-15.c

#include"stdio.h"

#define N 2

#define M N+1

#define NUM (M+1)*M/2

void main()

{ int i;

for(i=1;i<=NUM;i++);

printf("%d\n",i);

}

3.下面程序的输出是______。

程序10-16:p10-16.c

#define PR(ar) printf("%d", ar)

main()

{ int j, a[]={ 1,3,5,7,9,11,13,15},*p=a+5;

for(j=3; j; j--)

{ switch(j)

{case 1:

case 2: PR(*p++); break;

case 3: PR(*(--p));

}

}

}

4.输入两个整数,并利用带参数的宏定义,求其相除的商。

5.利用带参数的宏定义,实现功能:求给定一个数的绝对值。

6.文件包含和程序文件的连接有什么不同?

7.判断对错:C语言编译预处理是在编译之前完成的。

8.判断对错:宏命令行是一行C语句。

9.从键盘输入三个整数,利用宏定义求出其中的最小值。

10.编写一程序,从键盘输入三角形的三条边的长度,利用带参数的宏定义,求三角形的面积。(提示:三角形的面积area=sqrt(s*(s-a)*(s-b)*(s-c),其中,a、b、c是三角形的三条边长,s=(a+b+c)/2。)

11.文件包含和程序文件的连接有什么不同?

12.分析下面的程序,给出结果。

var.h文件内容如下:

int M1,M2;

int a[5][5];

disp.c文件内容如下所示。

程序10-17:p10-17.c

266 #include"stdio.h"

#include var.h

main()

{void solve();

int i,j;

for(i=0;i<5;i++)

for(j=0;j<5;j++)

scanf("%d",&a[i][j]);

solve();

printf("M1=%d,M2=%d",M1,M2);}

void solve()

{ int i,j;

M1=a[0];

M2=a[0];

for(i=0;i<5;i++)

for(j=0;j<5;j++)

{ if(M1<a[i][j])

M1=a[i][j];

if(M2<a[i][j])

M2=a[i][j];}

}

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

267 第 11 章 指 针

考纲要求:

1.指针与指针变量的概念,指针与地址运算符。

2.变量、数组、字符串、函数、结构体的指针以及指向变量、数组、字符串、

函数、结构体的指针变量。通过指针引用以上各类型数据。

3.用指针作函数参数。

4.返回指针值的指针函数。

5.指针数组,指向指针的指针,main函数的命令行参数。

知识点讲析

【指针】

指针就是变量的地址,它是一个特殊的变量。在它里面存储的数值被解释成为内存里的一个地址。为了掌握指针的概念,必须知道地址、指针和间接访问之间的关系。并且需要搞清指针四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存单元,还有指针本身所占据的内存单元。

学习C语言, 如果不能用指针编写有效,正确和灵活的程序,可以认为你没有

学好C语言。指针是C语言的灵魂。指针、地址、数组及其相互关系是C语言中

最有特色的部分。规范地使用指针,可以使程序达到简单明了,因此,我们不但要

学会如何正确地使用指针,而且要学会在各种情况下正确地使用指针变量。

【指针变量】

如果一个变量中存放的是指针,那么这个变量就是指针变量。即专门用来存放某种类型变量的首地址(指针值)的变量被称为该种类型的指针变量。指针变量的一般定义形式如下:

类型说明符 *指针变量名

这里,类型说明符是任意有效的C语言类型;指针变量名是合法的C语言变量名即可。

268*是指针运算符,又称“间接访问”运算符,它表明该变量是指针变量。

269【指针与地址运算符】

指针变量有两个相关运算符:&和*。

&:取地址运算符。它的作用是取得变量所占用的存储单元的首地址,在利用指针变量进行间接访问之前,一般都必须使用该运算符将某变量的地址赋给相应的指针变量。

*:指针运算符,又称“间接访问”运算符。它的作用是通过指针变量来间接访问它所指向的变量。

【变量的指针和指向变量的指针变量】

变量的指针:实际上就是指变量的地址,变量的地址虽然在形式上好像类似于整型变量,但在概念上不同于以前介绍过的整型变量,它是一种新的数据类型,即指针类型。

指向变量的指针变量:指针变量是用来存放变量地址的指针类型的变量。指针变量的类型是“指针类型”,这是不同于整形或者字符型等其他类型的。指针变量是专门用来存储地址的。

【数组的指针和指向数组的指针变量】

数组的指针:是指数组的起始地址,而数组中某个元素的指针就是这个数组元素的地址。

指向数组的指针变量:其定义与指向变量的指针变量的定义相同。即指针变量内存放的是数组的首地址。

【字符串的指针和指向字符串的指针变量】

字符串的指针:即字符串常量的首地址。

指向字符串的指针变量:变量的类型仍然是指针类型,不过它保存的是字符串的首地址,或者是字符数组的首地址。

【函数的指针和指向函数的指针变量】

函数的指针:指针变量不仅可以指向整型、实型变量、字符串、数组,还可以指向一个函数。每一个函数都占用一段内存,在编译时被分配一个入口地址,这个入口地址就是函数的指针。可以让一个指针变量指向函数,然后就可以通过调用这个指针变量来调用函数。

270 指向函数的指针变量:它的一般定义形式是:

类型说明符 (*指针变量名)()

在这种指针变量中保存的是函数的入口地址,定义中的类型说明符所说明的类型即为所指向的函数的返回值的类型。

【结构体的指针和指向结构体的指针变量】

结构体的指针:结构体所占用的内存单元的起始地址就是该结构体的指针。要想获得一个结构体的指针,必须使用取地址运算符&。

指向结构体的指针变量:一个结构体变量的指针就是该变量所占据的内存段的起始地址。我们可以用一个指针变量来指向结构体变量,这时指针变量的值就是结构体的起始地址。

【用指针作函数参数】

函数指针可以作为参数传递到其他函数。可以把指针作为函数的形参。在函数调用语句中,也可以用指针表达式来作为实参。

【返回指针值的指针函数】

前面已经说明,一个函数可以返回整型、字符型、实型和结构类型等数据,当然还可以返回指针类型的数据。对于返回指针类型数据的函数,在函数定义时,也应该进行相应的返回值类型说明。

【指针数组】

一个数组,其元素均为指针类型的数据,则称该数组为指针数组。指针是变量,因此可设想用指向同一数据类型的指针来构成一个数组,这就是指针数组。数组中的每个元素都是指针变量,根据数组的定义,指针数组中每个元素都为指向同一数据类型的指针。指针数组的一般定义形式为:

类型说明符 *数组名[整型常量表达式]

【指向指针的指针】

指向指针的指针的一般定义形式为:

类型说明符 **指针变量名

271清楚了指针数组后,就较容易理解指向指针的指针了。

272【main函数的命令行参数】

运行一个命令时,命令行的一般形式为:

命令名 参数1 参数2 ??参数n

例如用copy命令将C盘Windows目录下的文件tc.txt复制到dos目录下,为:

copy C:\Windows\tc.txt C:\dos

在编程时,如何处理这些参数呢?在以往编写的C程序中,main函数一般写为以下形式:

main()

实际上,main函数是可以带参数的,比如:

main(参数1,参数2)

其中,参数1和参数2就是main函数的形参。其中参数1是命令行参数的个数,参数2是一个指向字符串的指针数组。即带参数的main函数形式应当为:

main(argc,argv)

int argc;

char *argv[ ];

{?

}

补充与扩展

指针是C中的难点和重点。要想把它学好,就必须多看一些实例,多上机练习。【关于指针和指针变量的补充】

我们知道在计算机内系统为每一个变量分配了一块存储区域,变量的值就存放在这块区域之中。即程序中的变量经过编译系统处理后都对应着内存中的一个地址,也就是说,编译系统根据变量的类型,为其分配相应的内存单元以便存放变量的内容。不同类型的变量所分配的内存单元的长度是不一样的。通常字符型变量占用一个字节的内存空间,整型变量占用两个字节的存储空间,等等。对一般变量的存取是通过变量的地址进行的。这种按照变量地址来存储变量值的方式称为“直接访问”方式。

在计算机内部,通过访问或修改这块区域的内容来访问或修改相应的变量。C语言中,对于变量的访问形式之一,就是先求出变量的地址,然后再通过地址对它进行访问,这就是形成指针及其指针变量的概念,这种存取变量的方式就是“间接访问”方式。

如前所述,可以用下述方法来定义一个指针类型的变量。

273 float *ip

首先说明了这是一指针类型的变量,注意在定义中不要漏写符号“*”,否则就是一般的浮点型变量。另外,在定义中的float表示该指针变量为指向浮点型数据的指针变量,有时也可称ip为指向浮点型数据的指针。ip是一个变量,它专门存放浮点型变量的地址。

指针变量在使用时需要注意很多事项,下面通过一个程序说明指针变量的使用方法。见程序11-1。

程序11-1:P11-1.c

#include"stdio.h"

main()

{int i;

int *ip;

ip=&i;

i=2;

printf("%d,%d\n",i, *ip);

*ip=100;

printf("%d,%d\n",i, *ip);

}

程序执行结果如下:

2,2

100,100

这是一个非常简单的用指针写成的程序,目的就是让读者从中了解指针的概念,学会指针的使用。下面对它作如下解释说明。

为了将整型变量i的地址赋给整型指针变量ip,我们进行了如下操作:

int i;

int *ip;

ip=&i;

这就是关于指针变量如何引用的问题。下面的赋值是不合法的:

int *ip;

ip=100;

指针变量中只能存放地址,因此,在使用中不要将一个整数赋给一指针变量。

在定义指针变量的同时也可以对其进行初始化。比如,可以将程序中的两条语句:

int *ip;

ip=&i;

改为:

int *ip=&i;

这是允许的。将变量i的地址赋给指针变量ip之后,*ip就与i等价,除非对其另行赋值。

指针变量的引用。由于在指针变量中存放的是地址,因此在使用中不能将一个整数赋给一指针变量。比如下面的赋值是不合法的:

274 int *ip;

ip=100;

现在我们假设:

int i=200, x;

int *ip;

我们定义了两个整型变量I,x,同时还定义了一个指向整型数据的指针变量ip。I,x中可存放整数,而ip中只能存放整型变量的地址。我们可以把i的地址赋给ip:

ip=&i;

此时指针变量ip指向整型变量i。以后我们便可以通过指针变量ip间接访问变量i。

通过指针访问它所指向的变量是一种间接访问的形式,这比直接访问一个变量要费时间,,而且不直观,因为通过指针要访问哪一个变量,取决于指针的值(即指向),但由于指针是变量,我们可以通过改变它们的指向,以间接访问不同的变量,这给程序员带来灵活性,也使程序代码编写得更为简洁和有效。

为理解上述指针使用原则,我们再来看一个程序。程序的要求是:用指针变量来编写一程序。程序实现的功能是:从键盘输入两个整数分别存入变量a和b中,按照由小到大的顺序显示出来。程序如下所示。

程序11-2:P11-2.c

#include"stdio.h"

main()

{

int a,b;

int *ipa, *ipb;

ipa=&a;

ipb=&b;

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

if(*ipa<=*ipb)

printf("%d,%d",*ipa, *ipb);

printf("%d,%d", *ipb, *ipa);

}

程序分析

在指针变量ipa中存放的是a变量的地址,ipb中存放的是b变量的地址,所以程序中使用的scanf函数可以改写如下:

scanf("%d%d",ipa,ipb);

需要注意的是,这里的ipa和ipb前面不再使用&运算符。

当然,我们可以参考前面的做法,设置一个中间变量ip作为交换变量,而ipa总是指向最小值,ipb则总是指向最大值。改变后的程序如下所示。

程序11-3:P11-3.c

#include"stdio.h"

275 main()

{

int a,b;

int *ipa, *ipb,*ip;

ipa=&a;

ipb=&b;

scanf("%d%d",ipa,ipb);

if(*ipa>*ipb)

{

ip=ipa;

ipa=ipb;

ipb=ip;

}

printf("%d,%d",*ipa, *ipb);

}

程序分析

如上面所述,当*ipa>*ipb时,交换的是指针变量ipa和ipb中指针值的方法。ipa总是指向最小值,ipb则总是指向最大值,从而达到按顺序输出的目的。再有一种方法就是使得ipa和ipb的指针值不变,采用交换整型变量a和b的值的方法,程序如下所示。

程序11-4:P11-4.c

#include"stdio.h"

main()

{

int a,b,temp;

int *ipa, *ipb;

ipa=&a;

ipb=&b;

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

if(*ipa>*ipb)

{

temp=a;

a=b;

b=temp;

}

printf("%d,%d",*ipa, *ipb);

}

当*ipa>*ipb时,程序中交换a与b的值,而ipa和ipb的值不变,但它们所指向的变量中的数值发生变化,这样也可以达到按顺序输出的目的。

从上面三个例子可以看出,采用指针编写程序将使得编写过程非常灵活,往往一个程序有好多种实现方法。这就显示出指针的灵活性,这也是C语言较其它语言灵活且复杂的一面。但从另一个角度看,使用指针在使程序变得简单的同时也降低了程序的可读性和可维护性。指针给了程序员非常大的创作空间,但也给维护人员带来致命的麻烦。由于指针的灵活性,所以其他人不得不花费很多的时间和精力来阅读程序找出某一指针的指向,这显然比阅读不带指针的程序要费力得多。

276【指针和地址运算符】

取地址运算符&和指针运算符*在前面已经做了定义,如&a得到的将是变量a的地址,而*ip则是ip所指向的变量,即其值为指针变量ip所指向的地址中所存放的内容。取地址运算符&和指针运算符*的优先级别相同,但按照从右至左的方向结合。因此如果已经执行了“ip=&a”语句,并且有下面的语句:

&*ip;

则其含意是:先进行*ip运算,得到变量a,再执行&运算,即相当于&a。

同样道理,*&a就是先进行&a运算,得到a的地址,再进行*运算,即&a所指向的变量。(*ip)++相当于 a++。这里的括弧是必要的,不能省略。因为如果没有括号,就变成了*(ip++),这就不再是指向a了。

【指针变量作为函数参数】

函数的参数并不局限于整型、实型、字符型等数据,还可以是指针类型。指针类型的函数参数的作用是将一个变量的地址传送到另一个函数中。而其他类型数据的作用都是传送简单的非地址形式的数据。现在用指针类型的数据作函数参数处理下面的问题。

在上面的程序11-2中我们用指针为下面的问题编写了程序,下面我们就用指针变量作为函数参数来改写这个问题的程序。问题如下:从键盘输入两个整数分别存入变量a和b中,按照由小到大的顺序显示出来。程序编写如下所示。

程序11-5:P11-5.c

#include"stdio.h"

swap(ipa,ipb)

int *ipa,*ipb;

{

int p;

p=*ipa;

*ipa=*ipb;

*ipb=p;

}

main()

{

int a,b;

int *pointera,*pointerb;

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

pointera=&a;

pointerb=&b;

if(a<b)

swap(pointera,pointerb);

printf("%d,%d\n",a,b);

}

277 程序运行结果

4, 7

7, 4

程序分析

swap()是用户自己定义的函数,其功能就是交换两个变量(a和b)的值。可以看到swap()函数的形参均是指针变量。程序开始执行后,用户输入a和b的值,如4和7。后将a的地址赋给指针变量pointera,而b的地址赋给指针变量pointerb,即使得pointera指向变量a,pointerb指向变量b。执行判断语句,因为a<b,所以执行函数swap()。在这里,由于实参pointera和pointerb是指针变量,函数调用开始时,实参变量将其值传送给形参变量,采取的是“值传递”方式。

执行函数swap(),使*pointera和*pointerb的值互换,也就是使a和b的值互换。函数调用结束后,pointera和pointerb不复存在,最后在主函数中输出的是已经经过交换后的值。

请读者一定要注意*pointera和*pointerb的值是如何交换的。在本例中我们采取的方法是交换a和b的值,但pointera和pointerb的值不变,这和程序9的方法是相同的。而如果读者要像程序11-4.c那么写成下面的样子就有问题了:

swap(ipa, ipb)

int *ipa,* ipb;

{ int *ip;

*ip=*ipa;

*ipa=*ipb;

*ipb=*ip;

}

这是因为*ipa就是a,它是整型变量。而*ip则是指针变量ip所指向的变量。但ip中并无确定的地址,用*ip就可能会破坏系统的正常工作状态。而用整型变量p作为过渡变量实现*ipa和*ipb的交换就不会出现上述问题。

【指针与数组】

指针和数组有着密切的关系,任何能由数组及其下标完成的操作也都可用指针来实现,但程序中使用指针可使代码更紧凑、更灵活。正确地使用数组的指针来处理数组元素,能够产生高质量的目标代码。

例如,定义下面两个变量:

int a[];

int *ip;

通过语句“ip=&a[0]”就把a[0]元素的地址赋给指针变量ip,即ip指向a数组的首地址。由于数组名即代表数组的首地址,所以这个语句也可以写成“ip=a”。

下面我们通过实例来介绍数组和指针之间的关系,这包括两者之间的联系和区别。具体看下面的程序11-6.c,然后加以分析。

278 程序11-6:P11-6.c

#include"stdio.h"

main()

{ int a[10],i,minv;

int *ip;

ip=a;

for(i=0;i<10;i++)

scanf("%d",ip+i);

minv=*ip;

for(i=1;i<10;i++)

if(minv>*(ip+i))

minv=*(ip+i);

printf("MIN=%d",minv);

}

这个程序的目的就是从键盘上输入10个整型数,找出其中的最小值并打印出来。

如果ip的初值为&a[0],则ip+i和a+i是等价的,都是a[i]的地址,即两者都指向a数组的第i个元素。

指向数组元素的指针变量也可以带下标,例如ip[i]与*(ip+I)。

数组元素的引用

下标法。例如a[i]形式。

指针法。例如*(a+i)或者*(ip+i)形式。

C编译系统在处理两种引用方法时是不同的,因而其执行效率不同。对于下

标法,编译系统是先计算元素地址,将其转换为指针后再进行处理,因而这种方

法费时较多。而指针变量则直接指向元素,不必重新计算地址。同时像ip++这样

的自加操作是比较迅速的,这样就大大提高了程序的执行效率。

用下标法的优点是比较直观,能直接知道是第几个元素。用地址法或者指针

变量的方法却不直观,让人很难快速判断当前处理的是哪一个元素。所以对程序

员来说,比较喜欢用下标法。在要求程序简明的时候而又不必过多顾及工作效率

时优先选用下标法。而在要求程序的运行效率时,最好选用指针法。

在定义指针变量时可以赋初值,比如上面程序中,如果有语句:

int *ip=&a[0];

它就等价于:

int *ip;

ip=&a[0];

(注意,这里不是*ip=&a[0];)

根据前面的介绍,如果写成

int *ip=a;

也是可以的。这样的意思是将a数组的首地址(也就是a[0]的地址)赋给指针变量ip。

279 既然我们已经定义了ip为指针变量并且已经为其赋了初值,即a的首地址。它就指向a这个数组元素。如果有下面的赋值语句;

*ip=6;

这就表示对ip当前所指向的数组元素赋一个初值6。

【用数组和指针作函数参数】

数组名可以用作函数的形参和实参。如果实参中有一段连续内存中的数据,想在函数调用中改变这段内存中的数据,然后在主调函数中使用这些改变了的数据。可以有四种方法完成这个任务。

下面通过一个实例来阐述这四种方法。

例:编写一个函数能够将两个字符串合并。

这四种方法采用同样的主函数,如下所示。

程序11-7:P11-7.c

#include"stdio.h"

main()

{ static char stra[]="Happy";

static char strb[]="New Year!";

char strc[20];

constr(stra,strb,strc);

printf("%s",strc);

}

这样,我们只要编写子函数constr()就可以了。

方法1:形参和实参都用数组名。

程序11-8:P11-8.c

void constr(stra,strb,strc)

char stra[],strb[],strc[];

{ int i=0,j=0;

while(stra[i]!='\0')

{ strc[i]=stra[i];

i++;

}

while(strb[j]!='\0')

{ strc[i+j]=strb[j];

j++;

}

strc[i+j]='\0';

}

方法2:形参为指针变量,实参用数组名。

程序11-9: P11-9.c

void constr(stra,strb,strc)

280 char *stra, *strb, *strc;

{ while(*stra!='\0')

{ *strc=*stra;

++stra;

++strc;

}

while(*strb!='\0')

{ *strc=*strb;

++strb;

++strc;

}

*strc='\0';

}

利用前面讲过的知识,上述函数也可以简写如下:

程序11-10:P11-10.c

void constr(stra,strb,strc)

char *stra, *strb, *strc;

{ while(*stra!='\0')

*strc++=*stra++;

while(*strb!='\0')

*strc++=*strb++;

*strc='\0';

}

方法3:形参和实参均为指针变量。

程序11-11:P11-11.c

void constr(stra,strb,strc)

char *stra, *strb, *strc;

{ int i=0,j=0;

while(stra[i]!='\0')

{ strc[i]=stra[i];

i++;

}

while(strb[j]!='\0')

{ strc[i+j]=strb[j];

j++;

}

strc[i+j]='\0';

}

注意这种方法与方法1的区别,在方法1中,我们是将形参说明为数组,而在方法3中我们则将形参说明为指针,这两者是不同的,虽然它们的效果都是一样的。

方法4:实参为指针变量,形参为数组名。

程序11-12:P11-12.c

void constr(stra,strb,strc)

char stra[],strb[],strc[];

{ while(*stra!='\0')

281 { *strc=*stra;

++stra;

++strc;

}

while(*strb!='\0')

{ *strc=*strb;

++strb;

++strc;

}

*strc='\0';

}

这四种方法的实质都是在于通过形参与实参共同占用一段内存,从而在函数调用后主调函数的实参元素值为改变后的值。

【指向多维数组的指针和指针变量】

用指针变量可以指向一维数组,当然也可以指向多维数组。但在概念上和使用上,多维数组的指针要比一维数组的指针复杂得多。对于报考计算机等级考试的考生来说,只要明确概念并且能判断二维数组的正确与否就可以了。如果读者要深入一步探究二维数组及多维数组,可以参考其他相关的参考书。

下面以二维数组为例进行具体讲述,先定义一个三行四列的二维数组a:

static int a[3][4]={{0,1,2,3}, {4,5,6,7}, {8,9,10,11}};

a为二维数组名,此数组有3行4列,共12个元素。也可这样来理解,数组a由三个元素组成:a[0], a[1], a[2]。而每个元素又是一个包含有四个整型变量的一维数组,例如a[0]所代表的一维数组所包含的 4 个元素为 a[0][0],a[0][1],a[0][2],a[0][3]。

但从二维数组的角度来看,a代表二维数组的首地址,当然也可看成是二维数组第 0行的首地址。a+1就代表第1行的首地址,a+2就代表第2行的首地址。既然我们把a[0],a[1],a[2]看成是一维数组名,可以认为它们分别代表它们所对应的数组的首地址,也就是说,a[0]代表第0行中第0列元素的地址,即&a[0][0], a[1]是第1行中第0列元素的地址,即&a[1][0],根据地址运算规则,a[0]+1即代表第0行第1列元素的地址,即&a[0][1],一般而言,a[i]+j即代表第i行第j列元素的地址,即&a[i][j]。

另外,在二维数组中,我们还可用指针的形式来表示各元素的地址。如前所述,a[0]与*(a+0)等价,a[1]与*(a+1)等价,因此a[i]+j就与*(a+i)+j等价,它表示数组元素a[i][j]的地址。

因此,二维数组元素a[i][j]可表示成*(a[i]+j)或*(*(a+i)+j),它们都与a[i][j]等价,或者还可写成(*(a+i))[j]。

清楚了多维数组的地址及其表示之后,就可以用指针变量来处理数组的相关运算了。看下面一个关于二维数组指针运用的例子。

程序11-13: P11-13.c

main()

282 { int a[3][4]={11,12,13,14,21,22,23,24,31,32,33,34};

int *ip;

for(ip=a[0];ip<a[0]+12;ip++)

{ if((ip-a[0])%4==0)

printf("\n");

printf("%4d",*ip);

}

}

运行结果如下:

11 12 13 14

21 22 23 24

31 32 33 34

在这个程序中,a[0]是数组第一行的首地址,a是数组的首地址,二者的值是相同的。这样,可以将源程序改成下面的程序11-14,结果不变。

程序11-14:P11-14.c

main()

{ int a[3][4]={11,12,13,14,21,22,23,24,31,32,33,34};

int *ip;

for(ip=a;ip<a[0]+12;ip++)

{ if((ip-a)%4==0)

printf("\n");

printf("%4d",*ip);

}

}

在上述的两个例子中,我们利用了数组的特性:第一行的首地址等于数组的首地址。这样的变换在有些情况下会使编写的程序简洁易懂,为编程提供了方便。

下面给出一个“指向由m个整数组成的一维数组的指针变量”的例子。

在上例中,指针变量ip是指向整型变量的,ip的值加1,所指的元素是原来ip所指向的元素的下一个元素。现在,我们让ip不是指向整型变量,而是指向一个包含m个元素的一维数组,见程序11-15。

程序11-15:P11-15.c

main()

{ int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};

int i,(*b)[4];

b=a+1;

for(i=1;i<=4;b=b[0]+2,i++)

printf("%d\t",*b[0]);

printf("\n");

for (i=0; i<2; i++)

{b=a+i;

printf("%d\t",*(b[i]+1));

}

printf ("\n");

}

283 程序运行结果如下:

9 13 17 21

3 11 19

在定义指向由4个整数组成的一维数组的指针变量中圆括号是不能少的,否则它是指针数组。这种数组的指针不同于前面介绍的整型指针,当整型指针指向一个整型数组的元素时,进行指针(地址)加1运算,表示指向数组的下一个元素。而对于这里讨论的指针变量,当进行指针(地址)加1运算时,指向的将是下一个一维数组,而不是元素。所以它的地址变化将不是简单地加 1,而是根据一维数组而定的。这样的指针在处理二维数组时是很方便的。

【字符串和指针】

前面已经讨论过,字符串常量是由双引号括起来的字符序列,例如:

"How do you do?"

就是一个字符串常量。该字符串中因为字符有空格字符,所以它由13个字符序列组成。在程序中如出现字符串常量,C编译程序就给字符串常量安排一存储区域,这个区域是静态的,在整个程序运行的过程中始终占用,平时所讲的字符串常量的长度是指该字符串的字符个数,但在安排存储区域时,C编译程序还自动给该字符串序列的末尾加上一个空字符'\0',用来标志字符串的结束,因此一个字符串常量所占的存储区域的字节数总比它的

字符个数多一个字节。

在C语言中,实现一个字符串的方法有两种:用字符数组实现和用字符指针实现。

先看用字符数组如何实现。我们讨论下面的程序,程序的目的是实现字符串的转移,即将字符串a的内容复制到b中去,程序如下所示。

程序11-16: P11-16.c

main()

{ char a[]="How do you do?";

char b[20];

int i;

for(i=0; *(a+i)!=’\0’;i++)

*(b+i)=*(a+i);

*(b+i)='\0';

printf("a is:%s\n",a);

printf("b is:");

for(i=0;b[i]!='\0';i++)

printf("%c",b[i]);

printf("\n");

}

程序运行结果

a is: How do you do?

b is: How do you do?

284 程序分析

在这个程序中,我们采用字符数组来存储字符串。首先定义两个字符数组a和b,其中数组元素的表示方法

可以有两种。

一是用地址方法表示,比如*(a+i),这在第一个for循环语句中有所体现。在这个 for语句中,先检查a[i]是否为空字符 '\0'。如果不等于'\0',表示字符串尚未全部复制到b中去,那么就执行复制工作。当遇到 '\0' 字符时,就表明a字符串中的所有字符均复制到b中了,那么只要再将 '\0' 复制到b中程序就执行完成了。

另一种是用下标法表示,例如a[i],这在第二个for循环语句中体现出来。

用字符数组来表示字符串的优点是逻辑简单,形式简明。

另外,除了用字符数组的方法来实现字符串的存储外还有一种方法就是用字符指针来实现。例如:

char *string;

*string="How do you do?";

当然也可以写成:

char *string="How do you do?";

在对字符指针初始化时,不必说明为静态存储类型,因为这种初始化方式所做的工作仅仅是将字符串的首地址赋给指针变量,而不是为字符数组的每个元素赋值。

为明确这种用法,我们来看一个简单的程序。

程序11-17: P11-17.c

main()

{ char *string;

string="How do you do?";

printf("%s\n",string);

}

这里虽然没有定义字符数组,但C语言对字符串常量是按照字符数组来处理的,实际上在内存开辟了一个字符数组来存储字符串常量。程序中定义了一个字符串变量string,但并没有把字符串常量"How do you do?"赋给它,这是因为赋给它的是字符串的首地址,即存放字符串的字符数组的首地址。

下面我们就用字符指针来改写上面的程序11-17。从中也可以看出这两种方法的相同点和不同点。

程序11-18: P11-18.c

main()

{ char a[]="How do you do?";

char b[20],*ipa,*ipb;

int i;

ipa=a;

ipb=b;

for(;*ipa!='\0'; ipa++,ipb++)

*ipb=*ipa;

*ipb='\0';

285 printf("a is:%s\n",a);

printf("b is:");

for(i=0;b[i]!='\0';i++)

printf("%c",b[i]);

printf("\n");

}

程序运行结果

a is: How do you do?

b is: How do you do?

程序分析

这里我们用指针变量的值的改变来指向字符串中的不同的字符。ipa、ipb是指针变量,指向字符型数据。程序中先使ipa和ipb的值分别为a和b的首地址。赋值语句

*ipb=*ipa;

的作用是将a中的字符复制给b中的对应元素。这里我们应用了指针的方法。

尽管字符数组和字符型的指针变量都能够处理字符串,有时候甚至于可以混用,但两者不是完全相同的,而是有很大区别。

在初始化方面,对函数内定义的数组初始化时,需要使用关键字static,从而使其成为静态存储类型。例如:

static char a[]="How do you do?";

而对指针变量初始化时,无论该指针变量的作用域是全局的还是局部的,都可以不写static关键字。例如:

char *ip="How do you do?";

在赋值方面,字符数组只能是一个元素一个元素的依次赋值,而不能一次为整个数组赋值。比如,像下面这种赋值方式是错误的。

char a[20];

...

a="How do you do?";

但对于字符型指针变量来说,这样做是可以的,比如:

char *ip;

...

ip="How do you do?";

这是因为对语句:

ip="How do you do?";

来说,它的意义是将字符串"How do you do?"的首地址赋给指针变量ip。

对于字符数组,在定义完成之后,系统就为其分配了一段内存区域,而数组名就表示这段内存区域的首地址,在以后的程序中就可以用这段内存单元来存取字符数组中的每一个元素。而对于指针变量,在定义了之后,系统也为它分配了一个内存单元,但这段内存

286单元是用来存放指针(即地址)的,而它具体指向的内存单元并没有随着定义的完成而确定。要想利用它来间接访问某一个字符,必须首先告诉这个指针变量将要访问的字符变量的地址。也就是说,指针变量只是起到连接的作用。比如,下面的输入语句是允许的:

char a[100];

scanf("%s",a);

但如下的语句则是不合法的:

char *ip;

...

scanf("%s",ip);

这是因为,尽管ip被定义为指针变量,但它具体指向的内存单元并没有确定,也就是说,ip的值是不定的,它并没有指向某一确定的内存区域。这样一来,当我们把一些数据硬性地存入ip所指向的单元中就有可能破坏程序的其他数据,造成系统瘫痪。如果要用ip,可以按照下面的方法实现:

char a[100];

char *ip;

ip=a;

...

scanf("%s",ip);

这样就表明ip指向的是数组a,那么执行语句:

scanf("%s",ip);

就可以将数据送入数组a。

数组名可以代表数组的首地址,而且一旦定义了数组,那么数组名所代表的首地址的值就是确定的,不会随着外界的改变而改变,当然也就不能对它进行赋值运算。也就是说,数组名不能够作为赋值对象,不能对其进行赋值。指针变量的值却是可以改变的,可以像整型变量、实型变量那样对其进行赋值。指针变量可以指向字符串中任何的位置上,当然也可以作为赋值对象来进行赋值运算。

【字符串指针作函数参数】

字符串指针作函数参数的实质就是通过实参和形参共占一段内存来实现。这样一来,在被调函数中改变此段内存的内容,那么在主调函数中就可以得到改变了的数据。

将一个字符串从一个函数传递到另外一个函数可以用地址传递的方法,即用字符数组名作参数或用指向字符

串的指针变量作参数。下面通过实例来熟悉这两种方法。

程序11-19:P11-19.c

void stringcopy(froma,tob)

char from[],tob[];

{

int i=0;

while(froma[i]!='\0')

287 {

tob[i]=froma[i];

i++;

}

tob[i]='\0';

}

main()

{

char a[]="Happy New year.";

char b[]="Happy New year,too.";

printf("stringa=%s\nstringb=%s\n",a,b);

stringcopy(a,b);

printf("stringa=%s\nstringb=%s\n",a,b);

}

程序运行结果

stringa=Happy New year.

stringb=Happy New year,too.

stringa=Happy New year.

stringb=Happy New year.

程序分析

这个程序实现的是字符串的复制,即将字符串a的内容复制到字符串b中。a和b是字符数组。stringcopy的作用是将froma[i]赋给tob[i],直到遇到 '\0' 为止。在调用这个函数时,将a和b的首地址分别传递给形参数组froma和tob。

当然,在main()函数中可以不定义字符数组而用字符型指针变量。将main()函数改写如下:

程序11-20:P11-20.c

main()

{

char *a="Happy New year.";

char *b="Happy New year,too.";

printf("stringa=%s\nstringb=%s\n",a,b);

stringcopy(a,b);

printf("stringa=%s\nstringb=%s\n",a,b);

}

程序运行结果与上面的相同。不再赘述。

【动态存储分配】

对于指针变量的应用方式可以有两种:

定义一个基本数据类型和一个同类型的指针变量,比如可以定义一个整型量:

int a;

定义一个同类型的指针变量:

288 int ip;

这样就可以将指针变量ip指向存放基本数据变量的地址:

ip=&a;

从而,我们可以在以后的程序中使用该指针变量了。例如:

*ip=100;

定义一个数组,同时定义一个同类型的指针变量,例如:

int a[10];

int ip;

这样我们同样就可以让指针变量指向该数组的首地址:

ip=a;

另外,我们在遇到字符指针变量时,可以这样来使用:

char *ip;

ip="abcde";

在第一个语句中,我们定义了一个字符指针变量;第二句中我们将指针ip指向了字符串的首地址。这样做的依据是系统会为字符串常量分配适当的内存空间。

如果不用这些方式,而是在定义一个指针变量后直接引用,那么就有可能因指针指向的不确定性而造成难以预料的错误。在C语言中有一条重要的原则就是:对变量必须先定义后引用。C的编译程序在工作时也是先通过定义语句知道它们所需存储空间的大小,然后预先在内存区为其分配一段存储区域。而这些空间一旦分配,在变量或者数组的生存期内是固定不变的,这种分配方式称为“静态存储分配”。

存储方法一共有两大类:动态存储类和静态存储类

相对于“静态存储分配”,在C语言中还有另外一种分配内存空间的方式,这就是“动态存储分配”。在程序执行期间,可以随时将其分配给申请者。当需要空间来存放数据时,程序可以申请内存空间,而当空间使用完毕后可以将其释放而另作他用。

C语言自带有动态开辟和释放内存单元的库函数。主要有四个:malloc()、calloc()、free()和realloc()。

【malloc函数】

功能

在内存的动态存储区中分配一个长度为size的连续空间。如果分配成功就返回一个指针,这个指针指向分配区域的起始地址,类型为void;如果没有足够的内存单元用来分配,就将返回空指针(NULL)。

语法

malloc(size)

假如整型数据占用2个字节的存储单元,浮点型数据占用4个字节的存储单元,例如:

289 int *a;

float *b;

a=(int*)malloc(2);

b=(float*)malloc(4);

这样就使a指向一个int类型的存储单元,使b指向一个float类型的存储单元。如果不能确定所用的数据类型所占用的存储空间,可以通过sizeof运算符来求取。例如:

a=(int*)malloc(sizeof(int));

b=(float*)malloc(sizeof(float));

利用sizeof这种运算,就可以由系统来计算指定类型的字节数。这种方法有利于程序的移植。由于ANSI C标准规定malloc函数返回的指针为void*(无值型),所以在调用函数时,必须用强调类型转换将其转换成所需的数据类型。

【calloc函数】

功能

分配n个数据项的内存连续空间,每个数据项的大小为size。如果分配成功,就返回一个指针,这个指针指向分配区域起始地址,类型为void;如果不成功就返回空指针。

语法

calloc(n,size)

使用该函数相当于动态开辟了一个一维数组存储区域。该函数的第一个参数n决定了一维数组的大小,第二

个参数则决定了数组元素的类型,函数的返回值则是函数的首地址。【strcpy函数】

功能

把str2指向的字符串复制到str1中去。

语法

char *strcpy(str1,str2)

char *str1, *str2;

函数的返回值是str1。

【strlen函数】

功能

统计字符串str中的字符个数,不包括终止符 '\0'。

语法

290 unsigned int strlen(str)

char *str1;

函数的返回值是str的字符个数。

【free函数】

功能

释放指针变量p所指向的内存区,这个函数没有返回值。需要注意的是,这里的指针变量p必须指向由动态分配函数分配的地址。

语法

free(p)

【realloc函数】

功能

将p所指出的已分配内存区的大小改为size。size可以比原来的分配空间大或小。这个函数的返回值是指

向该内存区的指针。

语法

realloc(p, size)

ANSI标准建议设四个有关的动态存储分配的函数,但实际上,许多C编译系统实现时,往往增加了一些其他函数。读者在使用时可以查阅有关手册。

ANSI标准要求动态分配系统返回void指针。void指针具有一般性,可以指向任何类型的数据。但目前的大多数C编译系统所提供的这类函数都返回char指针。无论哪种情况,都需要用强制类型转换的方法把char指针转换成所需的类型。

【函数指针变量】

程序中的函数是以过程的形式存在的。调用函数是通过函数过程的入口地址实现的。对函数的调用就是相当于从函数的入口地址开始执行函数的处理过程。因此,我们既可以通过指定函数的名称实现对函数的调用,也可以通过指定函数的入口地址来实现对函数的调用。使用名称指定函数时,只能实现对固定函数的调用,这是因为在系统中函数的名称通常是不会重复的,但是如果通过函数的入口地址调用函数,情况就有所不同了。如果使用一个指针变量来记录函数的入口地址,则这个指针变量中记录的是那个函数的入口地址,可以通过这个指针变量实现对那个函数的调用。

函数指针变量的一般定义形式为:

类型说明符 (*标识符)(参数类型列表)

291 需要注意的是,类型说明符定义了函数指针的数据类型,该类型与该指针所指向的函数的返回值的类型必须一致。

为说明如何用函数指针变量来调用函数,我们给出下面的实例,实例的要求是:输入两个整型数据,然后将其中较大者输出。

程序11-21: P11-21.c

main()

{

int max();

int (*ip)();

int a,b,c;

ip=max;

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

c=(*ip)(a,b);

printf("a=%d,b=%d,max=%d",a,b,c);

}

max(x,y)

int x,y;

{

int z;

if(x>y)

z=x;

else

z=y;

return(z);

}

程序分析

语句

int (*ip)();

说明ip是一个指向函数的指针变量。这里*ip两侧的括弧不能省略,这表示ip先与*结合,是一指针变量,然后再与后面的()结合,表示此指针变量指向函数。如果写成:

int *ip();

则变成了返回指针值的函数。

赋值语句:

ip=max;

的作用是将函数max的入口地址赋给指针变量ip。为避免与函数调用格式发生冲突,不能写成下面的形式:

ip=max(a,b);

(*ip)()表示定义了一个指向函数的指针变量,它不是固定指向哪一个函数的,而只是表示定义了这样的一个变量,专门用来存放函数的入口地址的。在程序中把哪一个函数的入口地址赋给它,它就指向哪一个函数。在一个程序中,一个指针变量可以先后指向不同的函数。

292 在同一个程序中,特定的函数的入口地址是一定的,也就是说对指向函数的指针变量来说,像ip+n、p++等的运算是毫无意义的。

293【返回值为指针的函数】

一个函数的返回值可以是整型值、字符型值、实型值等,我们在前面的论述中均有所涉及。当然,函数的返回值可以是指针类型的数据,即地址。其一般的定义形式为:

类型标识符 *函数名(参数表)

例如下面的定义:

int *a(x,y);

a是函数名,调用它以后就可以得到一个指向整型数据的指针。x、y是函数的形参。*a的两侧没有括号,如果加了括号就会变成指向函数的指针变量,而不再是一个函数。【指向指针的指针】

指向指针数据的指针变量简称为指向指针的指针。其一般的定义形式为;

类型说明符 **指针变量名

由于这一知识点不是考试的重点,而且是非常难理解的。对它的论述我们将留在例题中,结合实例对其作一简单介绍。

【指针小结】

1.指针是C语言中一个重要的组成部分,使用指针编程有以下优点:

(1)提高程序的编译效率和执行速度。

(2)通过指针可使用主调函数和被调函数之间共享变量或数据结构,便于实现双向数据通信。

(3)可以实现动态的存储分配。

(4)便于表示各种数据结构,编写高质量的程序。

2.指针的运算

(1)取地址运算符&:求变量的地址

(2)取内容运算符*:表示指针所指的变量

(3)赋值运算:

① 把变量地址赋予指针变量;②同类型指针变量相互赋值;③把数组、字符串的首地址赋予指针变量;④把函数入口地址赋予指针变量

(4)加减运算

对指向数组,字符串的指针变量可以进行加减运算,如 p+n,p-n,p++,p--等。对指向同一数组的两个指针变量可以相减。对指向其他类型的指针变量作加减运算是无意义的。

(5)关系运算

指向同一数组的两个指针变量之间可以进行大于、小于、等于比较运算。指针可与 0

294比较,p==0表示p为空指针。

3.有关指针的说明很多是由指针,数组,函数说明组合而成的。但并不是可以任意组合,例如数组不能由函数组成,即数组元素不能是一个函数;函数也不能返回一个数组或返回另一个函数。例如int a[5]();就是错误的。

4.关于括号

在解释组合说明符时,标志符右边的方括号和圆括号优先于标志符左边的“*”号,而方括号和圆括号以相同的优先级从左到右结合。但可以用圆括号改变约定的结合顺序。

5.阅读组合说明符的规则是“从里向外”。从标志符开始,先看它右边有无方括号或园括号,如有则先作出解释,再看左边有无*号。如果在任何时候遇到了闭括号,则在继续之前必须用相同的规则处理括号内的内容。

典 型 例 题

【例题11-1 指针和地址运算符】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若有说明语句:int a,b,c,*d=&c;,则能正确从键盘读入三个整数分别赋给变量a、b、c的语句是( )。

A)scanf("%d%d%d", &a, &b, d);

B)scanf("%d%d%d", &a, &b, &d);

C)scanf("%d%d%d", a, b, d);

D)scanf("%d%d%d", a, b, *d);

解题方法

本题是关于指针变量的一道题。指针变量这一部分内容是C语言中的重点,也是C语言中令初学者最感到头痛的一部分。对指针掌握的好坏直接影响到编程者的编程素质。由于这一部分比较抽象,相对其他知识点来说又是最不容易理解的,所以这一部分往往又让许多人忽视。

解这种题的基本方法就是必须掌握指针的基本知识。这包括计算机等级考试中列出的所有知识点。现在计算机等级考试的一个明显趋势就是考的内容宽而广,从而就隐性地降低了题目难度。也就是说,对各个知识点的掌握程度并不是很高。

知识点分析

对于本题而言,只要清楚指针的概念和指针运算符*、取地址运算符&的意义就可以对其做出解答。在定义中,指定d是一指针变量,而且对其赋初值&c。所以只要将输入的数据送入d所代表的内存中就相当于给c赋初值。

正确答案

295 A

难度提示

初级。只要理解了指针的概念就可作答。

【例题11-2 指针的概念】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若定义:int a=511,*b=&a;,则printf("%d\n",*b);的输出结果为( )。

A)无确定值 B)a的地址 C)512 D)511

解题方法

本题同上题一样,也是关于指针变量的一道题。考查的仍然是指针的基本概念。

知识点分析

在语句int a=511,*b=&a;中,定义b为一指向整型的指针变量,并且对它赋初值&a。即将a的地址存入b这个指针变量中。在随后的输出语句printf("%d\n",*b);中,输出的是b所指向的内存单元*b中的数据,也就是a的数值511。

正确答案

D

难度提示

初级。只要理解了指针的概念就可作答。

【例题11-3 字符串指针作函数参数】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序的输出结果是( )。

程序11-22: P11-22.c

char cchar(char ch)

{

if(ch>='A'&&ch<='Z')

ch=ch-'A'+'a';

return ch;

}

main()

{

char s[]="ABC+abc=defDEF",*p=s;

while(*p)

296 {

*p=cchar(*p);

p++;

}

printf("%s\n",s);

}

A)abc+ABC=DEFdef B)abc+abc=defdef

C)abcaABCDEFdef D)abcabcdefdef

解题方法

本题是关于指针和函数调用的一道题。考生不但需要掌握函数调用的知识,而且还需要对指针的知识了如指掌。这就增加了试题的难度。做这种题,首先要从宏观上把握程序的逻辑关系,然后再从细节上考虑程序的运行过程,将整个程序做一解析。按照结构化程序设计的步骤,一步步的分析程序,直到得到最终结果。

知识点分析

首先,考生可以看到本题在主函数中调用一子函数cchar(),这个子函数已经在主函数之前做了定义。也就是相当于把子函数写在这个位置后得到一个完整的主函数。其次,从主函数开始分析逻辑关系。先定义一字符串s和字符串指针p,同时将字符串的首地址赋给字符串指针p。接着将字符串指针p做函数实参,运行函数cchar()。而函数的作用就是将字符串中的大写字母变成小写。得到的结果返回到原来的指针变量 p,这样就把字符串 s中的大写字母均变成小写字母。

正确答案

B

难度提示

高级。考生必须对C语言的两大难点(重点)函数和指针做彻底了解并且能灵活运用才能对本题作答。同时这道题目还考查考生阅读程序的能力。

【例题11-4 指针变量和数组名作函数参数】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序调用findmax函数返回数组中的最大值。

程序11-23:P11-23.c

findmax(int *a,int n)

{

int *p,*s;

for(p=a, s=a; p-a<n; p++)

if ( )

s=p;

297 return(*s);

}

main()

{

int x[5]={12,21,13,6,18};

printf("%d\n",findmax(x,5));

}

在下划线处应填入的是( )。

A)p>s B)*p>*s C)a[p]>a[s] D)p-a>p-s

解题方法

本题除了涉及到函数调用的知识外,主要还在于考查读者对指针的掌握程度。

知识点分析

讨论四个选项。

对A来说,p>s是对于地址作算术运算。在语法上这当然是允许的,但在本题中,对地址作大小判断是不对的,这将是无意义的。

对B来说,这是对p和s这两个指针变量所指向的两个整型数据作大小判断,这在逻辑上说是有意义的。从整个题目的逻辑关系上说,这也是讲得通的。

对C来说,由于p和s是两个指针变量,它们所存储的是两个地址,所以再将它们作为数组a的下标,这在语法上就是错误的。

对D来说,它的问题和选项A一样,在此不再赘述。

正确答案

B

难度提示

中级。需要读者理解指针的运算,特别是指针和其他数据类型混在一起时,需要分辨孰是孰非。

地址运算简述

1.指针在一定条件下可以进行比较,这里所说的一定条件是指两个指针指向

同一个对象才有意义。例如两个指针变量p,q指向同一数组时<、>、>=、<=、

==等关系运算符都能正常进行。若p==q为真,则表示p,q指向数组的同一元素;

若p<q为真,则表示p所指向的数组元素在q所指向的数组元素之前。

2.指针和整数可进行加减运算。设p是指向某一数组元素的指针,开始时指

向数组的第0号元素,设n为一整数,则

p+n

就表示指向数组的第n号元素(下标为n的元素)。不论指针变量指向何种数

据类型,指针和整数进行加、减运算时,编译程序总根据所指对象的数据长度对

n放大。

3.两个指针变量在一定条件下可进行减法运算。设p,q指向同一数组,则

298p-q的绝对值表示p所指对象与q所指对象之间的元素个数。其相减的结果遵守对象类型的字节长度进行缩小的规则。

299【例题11-5 动态存储分配】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若指针p已正确定义,要使p指向两个连续的整型动态存储单元,不正确的语句是( )。

A)p=2*(int*)malloc(sizeof(int));

B)p=(int*)malloc(2*sizeof(int));

C)p=(int*)malloc(2*2);

D)p=(int*)calloc(2,sizeof(int));

解题方法

对于这种题型,读者要对于C语言的动态存储分配有必要的了解。对于这一点,可以参见补充和扩展中的关于“动态存储分配”的知识点。

知识点分析

对于动态存储分配的理解只要读者掌握四个函数的使用就可以了。在本题中需要申请为变量 p分配空间,其大小是两个连续的整型动态存储单元。我们先通过函数 sizeof(int)来得到整型数据的存储单元的大小。这在不能确定数据类型所占的字节数时是非常有用的。同时利用这个函数还有利于程序的移植。

在通常情况下,int类型数据占据2个字节的存储单元,float型的数据则占据4个字节的存储单元。

下面看各个选项:

对于A,(int*)malloc(sizeof(int))是分配一个整型动态存储单元,此时在其前乘2并不能表明是为变量分配了两个连续的动态存储分配单元。选项A的语法错误。

对于B,sizeof(int)是指定了int型数据的字节数。再将其乘2则表明是两个连续的整型动态存储单元。然后再用语句(int*)malloc(2*sizeof(int))将其分配给变量p。

对于 C,则是假定了整型数据的字节数是 2,这在大部分微机系统中是正确的,当然在有些系统中是错误的。但这不是问题的关键所在。从语法上说,这种写法并无不妥。

对于D,则是使用了函数calloc()。关于函数calloc()的用法可以参见知识点分析中对应专题,在此不再赘述。需要注意的是malloc()函数和calloc()函数的区别,malloc()函数分配的是一个长度为size的存储单元,而calloc()函数则是可以分配n个size长度的存储单元。对于两者的返回值并无区别。

正确答案

A

难度提示

中级。这道题需要考生掌握动态存储分配函数的用法,而因为动态存储分配不是重点,对它的考查也是偶尔出现,所以对大部分考生来说这一知识点比较陌生。再加之这四个函

300数比较生僻,平时写程序时很少用到,这也无形中增加了本题的难度。

301【例题11-6 取地址运算符与数组形式】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若有定义:

int aa[8];

则以下表达式中不能代表数组元aa[1]的地址的是( )。

A)&aa[0]+1 B)&aa[1] C)&aa[0]++ D)aa+1

解题方法

本题主要考查数组首地址和其他元素的地址的关系,包括取地址运算符&的使用。

知识点分析

对于这种关于数组地址的问题,只要记住数组的首地址是可以用数组名来表示就可以了。在这道题中,int aa[8];定义了一个数组,数组名是aa。对于A、B、D选项,&aa[0]+1、&aa[1]和aa+1均可以表示aa[1]的地址。但对于C选项,&aa[0]++并不能表示aa[1]的地址。它表示先取aa[0]的地址,然后再使其加1,如果修改为&++aa[0]就正确了。

正确答案

C

难度提示

中级。本题考查的知识点不多,主要集中在数组和取地址运算符上,所以其难度不是很高,读者只要平时留心注意这些用法就不难作出解答。

【例题11-7 字符串标准函数的应用及指针的应用】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序的输出结果是( )。

程序11-24:P11-24.c

#include<stdio.h>

#include<string.h>

main()

{ char b1[8]="abcdefg",b2[8],*pb=b1+3;

while(--pb>=b1)

strcpy(b2,pb);

printf("%d\n",strlen(b2));

}

302 A)8 B)3 C)1 D)7

解题方法

本题主要考查指针和数组名作函数参数的知识以及函数strcpy()和strlen()的用法。对于这种题型来说,关键是要理解指针和数组名作函数的参数的区别,还有就是要了解一些常用标准函数的用法。

知识点分析

首先可以看到,指针的起始位置是b1的第三个字符,然后进行自减运算。注意,算术运算符的优先级是要高于关系运算符的,而关系运算符的优先级又高于赋值运算符。b1是数组的首地址。

其次,读者要清楚的是while语句的使用方法。在实际的工作中,while语句完全可以用for语句代替,而while语句作为一种循环结构是有掌握它的必要的。它不像goto语句在程序中必须尽量限制其使用,因为goto这种语句会使得程序变得难读,增加了程序维护的难度。while语句的特点是先判断表达式然后执行语句,是一种“当型”的循环结构。

对于一些常用的库函数是有其记忆的必要的。这是因为这类函数几乎在任何一个C编译系统中都能用到,而且其用法和结构都是固定的。特别是一些用于字符串操作的函数就更有记忆的必要。因为字符串的操作是较其他整型、实型数据更为复杂的,如果能够熟练使用一些标准库函数就会使得编程简单高效,而且程序更加易读。

本题涉计到两个字符串处理函数strcpy()和strlen(),请参阅“补充和扩展”中说明,此不赘述。

正确答案

D

难度提示

高级。这道题目包含的知识点较多,这就要求考生必须了解每一个知识点,特别是一些知识点的特殊用法。对于这种类型的题,考生除了平时把基础打好之外,还要提高自己阅读程序的能力。多上机练习,多读一些比较好的程序,这样才能在考试时做到得心应手。【例题11-8 返回指针值的函数】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

在说明语句:int *f();中,标志符f代表的是( )。

A)一个用于指向整型数据的指针变量

B)一个用于指向一维数组的行指针

C)一个用于指向函数的指针变量

D)一个返回值为指针型的函数名

解题方法

303 这道题目所用的知识点不多,但要求考生对这一知识点有较为深入的了解。要作对这种题目,关键在于平时对这类知识点要多加注意,仔细研读教材中的论述。同时也可以参考本书中关于知识点的分析。对那些重要的知识点或者常考的知识点,我们在书中均做了标记。读者只要按照书中的记录仔细研读即可,省去了自己

总结和经验不足的缺点。

知识点分析

函数可以返回整型、字符型、实型等类的数据,当然也可以返回指针类型的数据,这就是地址。注意,在返回指针值的函数的定义中,函数名前面必须带有*。这样才能表示此函数是指针型函数。对于这种函数,它的返回值的类型是单一的,就是指针型。

正确答案

D

难度提示

中级。虽然题目的知识点比较单一,但知识点本身是有一定难度的。特别是对于刚接触C语言的初学者来说,这个知识点就更加抽象。

【例题11-9 带参数的main函数】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

不合法的main函数命令行参数表示形式是( )。

A)main(int a,char *c[]) B)main(int arc,char **arv)

C)main(int argc,char *argv) D)main(int argv,char *argc[])

解题方法

同上题一样,本题所要考查的知识点也只有一条,就是main函数的形参的用法。但难点在于这一知识点本身就是C语言中的难点所在,掌握这一知识点本身就有一定的难度。而且从前几年计算机等级考试二级C语言的考题看来,考到这一知识点的机会微乎其微。也就是说,这一知识点不是常考点,这就使得这个知识点更加冷僻。对于这种题型,读者只能在全面掌握知识点的基础上有目的地涉及一些难点。关于难点,在本书中已有所涉及。读者可以作为参考。

知识点分析

在指针数组这一知识点分析中,我们已经讲过,指针数组的一个重要作用就是作为main函数的形参。在一般的程序中,main函数一般都写为下面的形式:

main()

括弧中是空的。实际上,main函数是可以带有它自己的参数的,比如:

main(argc,argv)

argc和argv是main函数的形参。main函数是由系统调用的,当处于操作命令状态下,

304输入main函数所在的可执行文件名,系统就调用main函数。在这里,main函数的形参值显然不可能从程序中得到。而实际上,实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传给main函数的参数。

带参数的main函数的形式应该是:

main(argc,argv)

int argc;

char *argv[];

{

...

}

这里main函数的第一个参数argc是指命令行中参数的个数,第二个参数必须是指针数组。

到这里就可以做出判断。

正确答案

C

难度提示

高级。虽然知识点只有一个,但因为知识点的特殊性,所以本题的难度并不小。而且这种知识点又会被很多考生所忽视,所以其难度就显得更大。

【例题11-10 指针类型转换】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序的输出结果是( )。

程序11-25: P11-25.c

main()

{ char *p="abcdefgh",*r;

long *q;

q=(long*)p;

q++;

r=(char*)q;

printf("%s\n",r);

}

解题方法

对于这种题型,关键就是清楚指针的概念和一些特殊的用法。

知识点分析

本题的知识点有二,其一是指针变量的使用,其二是指针类型的转换。

关于指针变量的使用这一知识点,在前面做了较多的叙述,在此不再赘述。关于指针

305类型的转换简述如下。

ANSI增加了一种“void *”指针类型,可以用它来定义一个指针变量但不指定它指向哪一种类型的数据。“void *”可以用来指向一个抽象的类型的数据,在将它的值赋给另一指针变量时要进行强制类型转换以使之适合于被赋值的变量的类型,例如:

char *ipa;

void *ipb;

...

ipa=(char *)ipb;

同样可以用(void *)ipa将ipa的地址转换成void *类型。比如:

ipb=(void *)ipa;

当然也可以将一个函数定义为void *类型,如:

void *f(cha,chb)

表示函数f()返回的是一个指向“空类型”的地址,如果要引用此地址,就必须根据情况对其进行类型转换,比如:

ipa=(char *)f(cha,chb);

清楚了上面的知识以后就可以准确地对本题作出回答。

正确答案

efgh

难度提示

高级。本题的知识点较多,而且其中有些知识点有一定的难度。这就使得整个题目的难度加大。

【例题11-11 字符串指针的使用】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

mystrlen函数的功能是计算str所指字符串的长度,并作为函数值返回,请填空。

int mystrlen(char *str)

{

int i;

for(i=0; !='\0';i++);

return( );

}

解题方法

这种题型仍然是关于指针的应用问题。问题的核心就是字符串指针作为函数的形参时,如何使用它来传递字符串。

知识点分析

306 本题的知识点较为简单,关键仍是指针的使用。需要注意的一点就是在处理字符串时,系统会在字符串的结束加一 '\0' 作为结束的标志。通常我们在编写程序时也是通过判断字符是否为 '\0' 来确定字符串的结束与否。

正确答案

*(str+i)str[i]

i

难度提示

中级。通过上面这些题,我们可以得到这样一个印象,指针在历年的计算机等级考试中都是重点。由于指针的特殊性和复杂性,使得这一部分在试题中所占的比重非常大。又由于指针的灵活性和使用的广泛性,使得对指针的考查非常灵活,题目类型也是千变万化,这也增加了指针这一部分的难度。

不管试题如何变,考生只要扎扎实实掌握好基本的知识,应对指针这一部分并不是难事。

【例题11-12 函数参数的类型】

(全国计算机等级考试二级笔试试卷19xx年4月)

题干

以下程序中的trap函数是一个用梯形法求定积分的通用函数。梯形法求定积分的公式为:

s=((f(a)+f(b))/2+∑f(a+i*h))*h

h=|(a-b)/(n-1)|

i=1--n

其中,n为积分小区间数。 以下程序调用trap函数求定积分,被积函数是:f(x)=x*x+3*x+2,且n=1000,a=0,b=4。

程序11-26:P11-26.c

#include<stdio.h>

#include<math.h>

double trap(fun,a,b)

double (*fun)()

double a,b;

{

double t,h;

int i,n=1000;

t=0.5*((*fun)(a)+(*fun)(b));

h=fabs(a-b)/(double)(n);

for(i=1;i<=n-1;i++)

t=t+______;

t=t*h;

return(t);

307 }

double mypoly(x)

double x;

{

return(x*x+3.0*x+2.0);

}

main()

{

double y,( *pf)();

pf=______;

y=trap(pf,0.0,4.0);

printf("%f\n",y);

}

解题方法

这种类型的题目有一定的综合性。要做好这类题目关键就在于考生读程序的能力。而这种能力考查的是考生综合的应用C语言的能力。所以对考生的自身素质的要求很高。不仅要求考生扎实掌握基础知识,更要求考生掌握如何应用这些基础知识编写综合性的程序。

知识点分析

本题考查的知识点较多。归纳起来有下面这些:

“文件包含”处理。这一知识点的难度并不大,大部分考生能较好地掌握。要注意的就是在考生自己编写C程序时或者在做改错练习时,注意所包含的文件名以及它的写法。这一点在上面第8章我们做过详细论述,不清楚的地方读者可以参阅一下。

函数的定义、函数参数的类别、函数调用。函数这一知识点是C语言的重点,也是出题的重点和难点。在本题中几乎涉及到了函数的所有特殊用法,值得好好研究。首先涉及到了函数的定义,本题定义了三个函数,例如:

double trap(fun,a,b)

double mypoly(x)

double (*fun)()

同时可以看到它们涉及了函数形参的各种类型,指针类型、实型,等等。在整个题目中涉及到函数的调用时,又需要注意实参和形参的问题。

本题还涉及到函数的返回值、类型的转换等一系列的问题。关于这些问题在本书中均有详细论述,在此不再赘述。

程序从主函数开始,首先定义double (*pf)();且使得pf=mypoly,这可以从下面语句y=trap(pf,0.0,4.0);的用意看出。这里使用了指向函数的指针变量,它的值就是函数的入口地址。也就是说,pf的作用就是将函数mypoly的入口地址告诉函数trap,从而实现函数的调用。

在函数trap中有一函数指针变量fun,它是函数的形参,其目的就是实现函数mypoly的调用。并且通过语句double (*fun)();将指针的类型转换为double型,同mypoly返回值的类型相同。

在清楚了整个程序的逻辑关系之后就可以从整体上把握程序的结构,为解题打下良好

308的基础。

至此,本题的答案就可以得出。

正确答案

(*fun)(a+i*h)

mypoly

难度提示

高级。这道题的逻辑严密,由于有几重函数调用,使得整个题目的逻辑关系变得非常隐蔽。这就需要读者仔细研读才能把握住题目的结构。

【例题11-13 指针变量作函数参数】

(全国计算机等级考试二级笔试试卷19xx年4月)

题干

以下程序求a数组中的所有素数的和,函数isprime用来判断自变量是否为素数。素数是只能被1和本身整除且大于1的自然数。

程序11-27:P11-27.c

#include<stdio.h>

main()

{

int i,a[10],*p=a,sum=0;

printf("Enter 10 num:\n");

for(i=0;i<10;i++)

scanf("%d",&a[i]);

for(i=0;i<10;i++)

if(isprime(*(p+______))==1)

{

printf("%d",*(a+i));

sum+=*(a+i);

}

printf("\nThe sum=%d\n",sum);

}

isprime(x)

int x;

{

int i;

for(i=2;i<=x/2;i++)

if(x%i==0)

return (0);

______;

}

解题方法

这道题中出现了 C 语言运算符的一些特别的用法,比如在语句 sum+=*(a+i);和

309if(x%i==0)中的用法。为了做出本题,读者仍旧需要的是一种读程序的能力。

知识点分析

题目中已经说明函数isprime是用来判断自变量是否为素数的。先阅读函数isprime,它将判断自变量的属性。如果是素数将返回1,不是就会返回0值(return (0);)。这样就会告诉主函数哪个自变量是素数,哪个不是素数。

主函数遇到素数后就将其加到变量sum中,即sum是所有素数的和。最终的结果就满足了题目要求:求出了a数组中的所有素数的和。

在主函数中,把数组a的首地址赋给指针变量p,也就是说在参数传递的过程中是用指针变量进行的。

正确答案

i;return 1 或 return (1)

难度提示

中级。整体说来,本题所涉及到的知识点并不是十分复杂,解答本题关键的一点就是考生阅读程序的能力。

【例题11-14 指针、数组、函数的综合应用】

(全国计算机等级考试二级笔试试卷19xx年4月)

题干

以下四个程序中,_______不能对两个整型变量的值进行交换。

A)

程序11-28: P11-28.c

#include<stdio.h>

main()

{

int a=10,b=20;

swap(&a,&b);

printf("%d %d\n",a,b);

}

swap(p,q)

int *p,*q;

{

int *t;

t=(int*)malloc(sizeof(int));

t=p;

*p=*q;

*q=*t;

}

B)

310程序11-29: P11-29.c

#include<stdio.h>

main()

{

int a=10,b=20;

swap(&a,&b);

printf("%d %d\n",a,b);

}

swap(p,q)

int p,q;

{

int *t;

t=*p;

*p=*q;

*q=t;

}

C)

程序11-30: P11-30.c

#include<stdio.h>

main()

{

int *a,*b;

*a=10,*b=20;

swap(a,b);

printf("%d %d\n",*a,*b);

}

swap(p,q)

int *p,*q;

{

int t;

t=*p;

*p=*q;

*q=t;

}

D)

程序11-31: P11-31.c

#include<stdio.h>

main()

{

int a=10,b=20;

int x=&a,y=&b;

swap(x,y);

printf("%d %d\n",a,b);

}

swap(p,q)

int *p,*q;

311 {

int t;

t=*p;*p=*q;*q=t;

}

解题方法

本题考查的是考生对指针变量、数组和函数的知识的简单理解。题目的选项中涉及的知识点较多,是一道较好的考题。对这类题目的解答,需要考生在掌握了题目中所涉及的知识点之后,能对各个知识点有较深入的理解。特别是对一些非常相近的或者相关联的知识点能够知道它们的区别在什么地方。这就需要考生在平时的练习当中自己总结,经常对这类知识点作一下回顾。

知识点分析

题目要求的是对两个整型变量的值进行交换,而且四个选项中的程序在结构上是相同的,都是由主函数和一个子函数构成。问题的关键在于函数中参数的选取。

先看A选项。在swap函数中,使用p、q指针变量作为形参,而实参是来自主函数中的整型变量a和b。关于指针变量作函数形参的问题可以参考知识点分析中的论述。

对于B选项,在swap函数中p和q虽然定义为整型参数,但在程序中又定义了一个指针变量作为中间变量,这样一来就解决了参数的转换问题。

对于C选项,在swap函数中p和q定义为指针变量作为形参,这同选项A是相同的。问题出在主函数中。在主函数中定义a和b为指针变量,但却为其赋值为10和20,这是不可以的。具体的讨论可以参见前面的叙述,在此不再赘述。

对于D选项,swap函数同选项A相同。在主函数中,定义a和b为整型变量,同时定义整型变量x和y,它们用来存放a和b的地址,这是可以的。

通过上面的叙述,本题不难解答。

正确答案

C

难度提示

中级。本题涉及到的知识点并不是很多,难度也不是很大。主要考查的仍然是基础知识和程序阅读能力。

【例题11-15 动态存储分配在编程中的应用】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

下面的程序调用getone函数开辟一个动态存储单元,调用assone函数把数据输入此动态存储单元,调用outone函数输出此动态存储单元中的数据,请填空。

程序11-32: P11-32.c

#include<stdio.h>

312 getone(s)

int **s;

{

*s=(int *)malloc(sizeof(int));

}

assone(a)

int *a;

{

scanf("%d",__________);

}

outone(b)

int *b;

{

printf("%d\n",_________);

}

main( )

{

int *p;

getone(&p);

assone(p);

outone(p);

}

解题方法

本题的题干已经表明了该程序的逻辑结构:用 getone 函数开辟一动态存储单元,用assone输入数据,再用outone输出数据。而且函数均已经基本给出,余下的问题就是读函数,找出语句之间的逻辑关系,把剩下的语句补全。

知识点分析

首先看getone函数。注意,getone函数的形参是一指向指针数据的指针变量s。指向指针的指针是C语言的难点,也是C语言中最难理解的一个知识点。现对其做如下补充介绍。

我们已经讲过,*运算符的结合性是从右到左,因此,**p相当于*(*p)。显然*p 是指针变量的定义形式,如果其前没有*,那就是定义了一个指向字符数据的指针变量。那么再在其前面加一*就表示指针变量p是指向字

符指针变量的。利用指针变量访问另一个变量就是“间接访问”。如果在一个指针变量中存放一个目标变量的地址这就是“单级间址”,而指向指针的指针变量用的是“二级间址”的方法。从理论上说,这种间址方法可以延伸到更多的级,但实际上在程序中很少出现超过二级间址的。级数愈多,就愈难理解,这样就容易产生混淆,使出错的机会增多。这也是为什么指向指针的指针比单级指针更难理解的原因。

通过上面的论述我们看到s就是这样的一个变量。它是一指针变量,它还指向指针变量。通过函数malloc为其分配了一个整型数据的空间。

当然,读者需要清楚malloc函数的意义。这在前面的动态存储分配中做过介绍,在此不再赘述。

313 再来看assone函数。这一函数的作用就是将数据输入由getone函数得到的动态存储单元中。它的形参是一指针变量,即它是用指针变量作为形参的。

对于outone函数,它的形参仍然是一指针变量。

主函数中依次调用这三个函数,当然,实参是一指针变量p。

清楚了程序的逻辑之后就可以做题了。

正确答案

a 或&*a

*b

难度提示

高级。主要是关于指向指针的指针这一知识点是非常难的,而且由于这一知识点的难度太大,对一般的考生来说掌握它确实是有一定的难度,从而很多考生就忽视了对这一知识点的掌握。

本 章 练 习

【填空题】

1.专门用来存放某种类型变量的首地址的变量被称为该种类型的______,它的类型是“______”。

2.数组的指针是指数组的______,而数组中某个元素的指针就是______。指针变量内存放的是数组的首地址,则它被称为______。

3.指向字符串的指针变量的类型仍然是______,不过它保存的是字符串的______,或者是______。

4.每一个函数都占用一段内存,在编译时被分配一个______,这个就是函数的指针。可以让一个指针变量指向函数,然后就可以通过调用这个指针变量来调用函数。

5.在带参数的main函数中

main(参数1,参数2)

参数1和参数2就是main函数的形参。其中参数1是______,参数2是一个指向字符串的______。

6.如果要引用数组元素,可以有两种方法:______和______。

7.在C语言中,实现一个字符串的方法有两种:用______实现和用______实现。

8.以下程序用指针指向三个整型存储单元,输入三个整数,并保持这三个存储单元中的值不变。选出其中最小值并输出。

程序11-33: P11-33.c

#include"stdio.h"

main()

{

314int ______;

a=(int *)malloc(sizeof(int));

b=(int *)malloc(sizeof(int));

c=(int *)malloc(sizeof(int));

min=(int *)malloc(sizeof(int));

printf("输入三个整数:");

scanf("%d%d%d",______);

printf("输出以上整数:%d%d%d\n",______);

*min=*a;

if(*a>*b)

______;

if(______>*c)

______;

printf("输出最小的整数:%d\n",______);

}

9.阅读以下程序。

程序11-34: P11-34.c

main()

{

char str1[]="how do you do",str2[10];

char *ip1=str1,*ip2=str2;

scanf("%s",ip2);

printf("%s",ip2);

printf("%s\n",ip1);

}

运行上面的程序,输入字符串HOW DO YOU DO 则程序的输出结果是______。10.阅读下面的程序。

程序11-35: P11-35.c

main()

{

static char *name[]={"Follow me","Basic", "Fortran",

"Great Wall","Computer design"};

char **ip;

int i;

for(i=0;i<5;i++)

{

______;

printf("%s\n",*ip);

}

}

要得到如下的运行结果:

Follow me

Basic

Fortran

Great Wall

Computer design

315 请将程序补充完整。

11.阅读并运行上面的程序,如果从键盘上输入字符串qwerty和字符串abcd则程序的输出结果是______。

程序11-36: P11-36.c

#include"string.h"

#include"stdio.h"

strlen(char a[],char b[])

{

int num=0,n=0;

while(*(a+num)!='\0')

num++;

while(b[n])

{

*(a+num)=b[n];

num++;

n++;

}

return (num);

}

main()

{

char str1[81],str2[81],*p1=str1,*p2=str2;

gets(p1);

gets(p2);

printf("%d\n",strlen(p1,p2));

}

【上机题】

1.编写程序完成如下功能:输入10个整型数据,按照由大到小的顺序输出。

2.输入三个整数,要求用指针变量作为函数的参数编写程序完成按照由小到大排序的功能。

3.要求用选择法对10个整型数据排序。使用数组和指针数组两种方法分别实现。

4.有三个学生每个学生学习四门课,计算他们总的平均成绩以及第n个学生的成绩。要求用函数ave求总的平均成绩,用函数search找出并输出第n个学生的成绩。在编程时要使用多维数组指针作函数的参数。

5.用指针函数实现下面的程序:如果每个学生有四门课,现有若干个学生的成绩,要求在用户输入学生序号以后能输出该学生的全部成绩。

6.在上例的基础上,找出其中有不及格课程的学生及其学号。

7.用指向指针的指针的方法对5个字符串排序并输出。

8.要求用本章所讲的知识设计两个函数,实现下述操作。

(1)将一个字符串中的字母全部变成大写,函数形式为:strlwr(字符串)。

(2)将一个符串中的字母全部变成小写,函数形式为:strupr(字符串)。

316 (3)将字符数组a中下标为单数的元素值赋给另外一个字符数组b,然后输出a和b的内容。

317【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

318 第 12 章 结构体与共用体

考纲要求:

1.结构体和共用体类型数据的定义方法和引用方法。

2.用指针和结构体构成链表,单向链表的建立、输出、删除与插入。

知识点讲析

【结构体】

结构体又称为“结构”,是由具有不同数据类型的多个变量组合而成的数据存储形式。定义一个结构体类型的一般形式为:

struct 结构体名{成员列表};

【结构体类型数据的定义方法】

在实际问题中,一组数据往往具有不同的数据类型。例如,在学生登记表中,姓名应为字符型;学号可为整型或字符型;年龄应为整型;性别应为字符型;成绩可为整型或实型。显然不能用一个数组来存放这一组数据。因为数组中各元素的类型和长度都必须一致,这样就便于编译系统处理。

为了解决这个问题,C语言中给出了另一种构造数据类型:“结构”。它相当于其他高级语言中的“记录”。“结构”是一种构造类型,它是由若干“成员”组成的,每一个成员可以是一个基本数据类型或者又是一个构造类型。既然结构是一种“构造”而成的数据类型,那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义函数一样。

前面已经给出结构体的定义形式,如下:

struct 结构体名

{

类型标识符 成员名;

类型标识符 成员名;

...

} 结构变量;

例如:

319 struct student

{

int num;

char name[20];

char sex;

float score;

};

在上面的定义中,struct是关键字,不能省略。struct student表示这是一个“结构体类型”。在这个结构体定义中,结构体名是student。该结构体由4个成员组成。第一个成员是num,整型变量;第二个成员是name,

字符数组;第三个成员是sex,字符变量;第四个成员是score,实型变量。

也可以将“成员表列”称为“域表”,每一个成员称为结构体中的一个域。成员名的定名规则与变量名相同。

定义一个结构体类型的变量有三种方法。

第一种方法是先定义结构体类型再定义变量名。

比如在上例中,先定义结构体类型:

struct student

{

int num;

char name[20];

char sex;

float score;

};

再定义变量:

struct student stu1,stu2;

这就说明了两个变量stu1和stu2是student结构类型。也可以用宏定义使一个符号常量来表示一个结构类型,例如:

#define STUDENT struct student

STUDENT

{

int num;

char name[20];

char sex;

float score;

};

然后就可以在程序中直接用STUDENT定义变量。例如:

STUDENT stu1,stu2;

第二种方法就是在定义类型的同时定义变量。

这种形式定义的一般形式为:

struct 结构体名

{

成员表列

320 }变量名表列;

这样前面的定义可以写成下面的形式:

struct student

{

int num;

char name[20];

char sex;

float score;

} stu1,stu2;

第三种形式是直接定义结构类型变量。

其一般的定义形式为:

struct

{

成员表列

}变量名表列;

前面的两个例子可以改写如下:

struct

{

int num;

char name[20];

char sex;

float score;

} stu1,stu2;

这里的定义形式中没有出现结构体名。这种省略结构体名的形式称为“无名结构”。这种情况常常出现在函数内部。第三种方法与第二种方法的区别在于第三种方法中省去了结构名,而直接给出结构变量。说明了stu1,stu2变量为student类型后,即可向这两个变量中的各个成员赋值。在上述stu结构定义中,所有的成员都是基本数据类型或数组类型。

在这一部分涉及到了两个重要的概念:类型和变量。对于它们之间的区别,读者一定要清楚,不要混淆。对结构体变量来说,在定义时一般是先定义一个结构体类型,然后定义变量为该类型。程序中只能对变量赋值、存取或者运算,但不能对类型赋值、存取或者运算。同样,在编译时,系统对类型是不分配空间的,只对变量分配空间。

结构体中的成员可以单独使用,其作用就相当于普通变量。当然,成员还可以是一结构体变量,这就涉及到结构体的嵌套的问题。见下面的例子:

struct date

{

int month;

int day;

int year;

}

struct

{ int num;

char name[20];

321 char sex;

struct date birthday;

float score;

}stu1,stu2;

先定义一个结构date,由month(月)、day(日)、year(年)三个成员组成。在定义并说明变量stu1和stu2时,其中的成员birthday被说明为data结构类型。

成员名可以与程序中的变量名相同,因为这两者并不代表同一对象。成员名只在原结构体中才有意义。

关于结构体的扩展

定义一个变量为标准类型与定义一个结构体类型的变量的不同之处在于,定

义结构体类型的变量不仅要求指定变量为结构体类型,而且要求指定为某一特定

的结构体类型,不能只指定为“struct型”而不指定结构体名。

在编写大规模的程序时,经常将对结构体类型的定义集中到一个文件里。当

源文件需要用到此结构类型时就可以用#include命令将该头文件包含到本文件中,

这样做的好处是可以便于装配、修改和使用。

【共用体】

共用体又称为“联合”。“共用体”类型的结构是使几个不同的变量共占同一段内存的结构。“共用体”类型变量的定义形式为:

union 共用体名

{成员表列

}变量表列;

【链表】

用链式存储结构存储的线性表称为链表。链表是一种常见的重要的数据结构,它的每个结点中含有一个指针域,用来指出其后续结点的位置。它是动态地进行存储分配的一种结构。

补充与扩展

【结构体变量成员的引用】

在程序中使用结构变量时,往往不把它作为一个整体来使用。即不能将一个结构体变量作为一个整体进行输入和输出。例如,在上面的例子中已经定义stu1和stu2为结构体变量,现假设已经对它们进行了赋值。下面的引用是错误的:

322 printf("%d,%s,%s,%f\n",stu1);

在ANSIC中除了允许具有相同类型的结构体变量相互赋值以外,一般对结构体变量的使用,包括赋值、存取、运算等都是通过结构体变量的成员来实现的。对结构体变量成员的一般引用形式是:

结构体变量名.成员名

例如:

stu1.num /*即第一个变量的学号*/

stu2.sex /*即第二个变量的性别*/

这里的“.”是成员(分量)运算符,它在所有的运算符中优先级最高,因此,可以将stu1.num作为一个整体来看待。

如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级地找到最低一级的成员。也就是说,只能对最低级的成员进行赋值或者存取运算。例如:

stu1.birthday.month

但下面的使用是不对的:

stu1.birthday

不能用这种方法来访问stu1变量中的成员birthday,这是因为birthday本身就是一个结构体变量。

为说明结构体变量的引用方法,下面以实例说明。见程序12-1。

程序12-1:P12-1.c

main()

{

struct stu

{

int num;

char *name;

char sex;

float score;

} stu1,stu2;

stu1.num=102;

stu1.name="Zhang ping";

printf("input sex and score\n");

scanf("%c %f",&stu1.sex,&stu1.score);

stu2=stu1;

printf("Number=%d\nName=%s\n",stu2.num,stu2.name);

printf("Sex=%c\nScore=%f\n",stu2.sex,stu2.score);

}

程序分析

在这个例子中使用语句:

323 stu1.num=102;

stu1.name="Zhang ping";

对num和name两个成员赋值,而且这里的name是一字符串指针变量,关于这一问题在下面还会有详细的论述。用scanf函数动态地输入sex和score成员值,然后把stu1的所有成员的值整体赋予stu2。最后分别输出stu2的各个成员值。本例表示了结构变量的赋值、输入和输出的方法。

成员变量也可以像普通变量一样进行各种运算,当然必须根据其类型来决定可以进行地运算。例如:

stu1.score=stu2.score;

sum=stu1.score+stu2.score;

stu1.num++;

结构体变量作为一个变量也有一个地址,即系统也为其分配了一定的存储空间。在程序中可以引用成员的地址,也可以引用结构体变量的地址。结构体变量的地址主要用于作函数的参数,传递结构体的地址。例如:

scanf("%d",&stu1.num);

printf("%o",&stu1);

语句1中是输入stu1.num的值,语句2则是输出stu1的首地址。

【结构体变量的初始化】

旧版本的C规定,如果结构体变量是全局变量或为静态变量,则可对它作初始化赋值。对局部变量或者自动结构体变量不能作初始化赋值,只能在函数执行时用赋值语句对各部分成员分别赋值。而新版本的C则取消了这一规定。

首先看对外部存储类型的结构体变量进行初始化。下面将通过实例来说明。见下面的程序。

程序12-2:P12-2.c

struct student /*定义结构*/

{

int num;

char *name;

char sex;

float score;

}stu2,stu1={102,"Zhang ping",'M',78.5};

main()

{

stu2=stu1;

printf("Number=%d\nName=%s\n",stu2.num,stu2.name);

printf("Sex=%c\nScore=%f\n",stu2.sex,stu2.score);

}

程序运行结果

Number=102

324 Name= Zhang ping

Sex=M

Score=78.5

程序分析

在本例中,stu2,stu1均被定义为外部结构体变量,并且对stu1作了初始化赋值。在main函数中,把stu1的值整体赋予stu2,然后用两个printf语句输出stu2各成员的值。

下面通过实例来说明如何对静态存储类型的结构体变量进行初始化。见下面的程序。

程序12-3:P12-3.c

main()

{

static struct student /*定义静态结构体变量*/

{

int num;

char *name;

char sex;

float score;

}stu2,stu1={102,"Zhang ping",'M',78.5};

stu2=stu1;

printf("Number=%d\nName=%s\n",stu2.num,stu2.name);

printf("Sex=%c\nScore=%f\n",stu2.sex,stu2.score);

}

程序运行结果与上相同。

程序分析

在本例中将stu1和stu2均定义为静态局部的结构体变量,同样可以对它进行初始化赋值。

【结构体数组】

数组元素可以是整型、实型、字符型,等等,也可以是指针类型,还可以是结构体类型的。这样就可以构成结构体数组。结构体数组的每一个元素都是具有相同结构体类型的结构体变量。在实际应用中,经常用结构体数组来表示具有相同数据结构的一个群体。如一个班的学生档案,一个车间职工的工资表,等等。结构体数组的定义方法和结构体变量相似,只需说明它为数组类型即可。例如:

struct

{

char name[8];

char sex[2];

int age;

char addr[40];

}student[40];

也可定义为:

325 struct string

{

char name[8];

char sex[2];

int age;

char addr[40];

};

struct string student[40];

上面定义了数组student为结构体数组,数组中的元素为结构体变量,即每一个变量可以存储一个学生的相应信息。

当然也可以对结构体数组进行初始化,这样,在定义数组时就可以不指定数组元素的个数。编译时,系统会根据给出初值的结构体常量的个数来确定数组元素的个数。对结构体数组的初始化,形式如下:

struct student

{

int num;

char *name;

char sex;

float score;

}stu[5]={

{101,"Li ping","M",95},

{102,"Zhang ping","M",73},

{103,"Xiao fang","F",98.5},

{104,"Cheng ling","F",76},

{105,"Wang ming","M",49};

}

当然也可以先定义结构体类型,然后再定义数组为该结构体类型,在定义数组时将其初始化。对结构体数组成员的访问是以数组元素为结构体变量的。形式是:

结构体数组元素.成员名

例如:

student[0].name

student[9].age

结构体数组相当于一个二维的构造。第一维是结构体数组元素,每个元素是一个结构体变量;第二维是结构体成员。另外,结构体数组的成员也可以是数组变量。例如:

struct aa

{

int m[3][5];

float f;

char s[20];

}y[4];

为了访问结构体aa中结构体变量y[2]的m[1][4]这个变量,可写成:

326y[2].m[1][4]

327【结构体与函数】

C语言中允许用结构体变量作为函数参数向所调用的函数传递整个结构体变量的内容。需要注意的是:结构体类型的实参和形参必须具有相同的结构体类型;结构体变量的形式与数组类似,含有多个成分,但是结构体与数组不同的是,结构体变量的名称并不表示结构的地址,所以在使用结构体变量名称作为参数时,函数之间传递的是结构体变量的内容,是值传递。

在程序设计时,有时希望函数返回一个结构体类型的值作为函数值。为达到此目的,应将函数的返回值类型定义为结构体类型,这类函数可以称之为结构体类型函数。其一般的定义形式如下:

struct 结构体名 函数名(参数表)

对于这一部分知识在此不再举例,读者可以参见其他的参考书。

【结构体与指针】

整型、字符型、数组和函数都有各自的指针,同样,结构体也有其自己的指针。结构体变量的指针就是该变量所占据的内存段的起始地址。这样就可以像定义指向其他类型数据的指针变量一样来定义指向结构体变量的指针变量。结构体指针变量的值就是该结构体变量的起始地址。指针变量可以指向单个的结构体变量,当然也可以指向结构体数组中的元素。定义结构体指针变量的一般形式是:

struct 结构体名 *结构体指针变量名

例如,在前面已经定义了结构体student,下面就可以定义结构体指针变量了:

struct student *ipstu;

在使用这个指针变量之前需要对其先进行赋值。赋值就是把结构体变量的起始地址赋予该指针变量,但我们不能把结构体名赋予该指针变量。这是因为结构体名和结构体变量名是两个不同的概念。结构体名它表示的是一种固定的结构体形式,在编译时系统并不给它分配存储空间。当某一变量被声明为这种结构体类型时,编译系统就为该变量分配相应的存储空间。这样就不可能得到一个结构体的起始地址,而只能得到结构体变量的起始地址。例如,前面已经说明stu1是一结构体变量,所以可以将它的值保存到结构体指针变量ipstu中,即:

ipstu=&stu1;

但如果写成下面的形式就是错误的:

ipstu=&student;

定义了结构体指针变量以后,就可以通过该变量来访问结构体变量。访问的一般形式如下:

328 (*结构体指针变量名).成员名

其实,前面定义了结构体指针变量后就将某一结构体变量赋给它,也就是说,*结构体指针变量名就等价于这一结构体变量。所以上面的访问形式其实就是下面的形式:

结构体变量名.成员名

在C语言中,为使用方便和直观其间,用下面这种形式代替上述的访问形式:

结构体指针变量名->成员名

其中,->称为指向运算符。这样就得到了三种等价的形式:

结构体变量名.成员名;

(*ipstu).成员名;

ipstu->成员名;

为使读者能对结构体指针变量的用法有一个清楚的了解,来看下面的例子,这个例子具体说明了结构体指针变量的使用方法:

程序12-4:P12-4.c

struct student

{

int num;

char *name;

char sex;

float score;

}stu1={102,"Zhang ping",'M',78.5},*ipstu;

main()

{

ipstu=&stu1;

printf("Number=%d\nName=%s\n",stu1.num,stu1.name);

printf("Sex=%c\nScore=%f\n\n",stu1.sex,stu1.score);

printf("Number=%d\nName=%s\n",(*ipstu).num,(*ipstu).name);

printf("Sex=%c\nScore=%f\n\n",(*ipstu).sex,(*ipstu).score);

printf("Number=%d\nName=%s\n",ipstu->num,ipstu->name);

printf("Sex=%c\nScore=%f\n\n",ipstu->sex,ipstu->score);

}

程序分析

程序首先定义了一个结构体student,接着定义了student类型的结构体变量stu1并对其作了初始化赋值,还定义了一个指向student类型的结构体指针变量ipstu。在main函数中,ipstu被赋予stu1的地址,因此ipstu指向stu1。然后在printf语句内用三种形式输出stu1的各个成员值。

【指向结构体数组的指针】

对于结构体数组及其元素,可以用指针或者指针变量来指向。即,结构体指针变量可以指向一个结构体数组,这时结构体指针变量的值是整个结构体数组的起始地址。结构体

329指针变量也可以指向结构体数组的一个元素,这时结构体指针变量的值是该结构体数组元素的起始地址。

下面通过一个例子来说明指向结构体数组的指针的应用。本例定义了一个含有4个元素的结构体数组。

程序12-5:P12-5.c

struct student

{

int num;

char *name;

char sex;

float score;

}stu[4]={

{101,"Zhou ying",'M',90},

{102,"Zhang ping",'M',83.5},

{103,"Yang fang",'F',96},

{104,"Cheng ling",'F',87},

};

main()

{

struct student *ipstu;

printf("No\tName\t\t\tSex\tScore\t\n");

for(ipstu=stu;ipstu<stu+4;ipstu++)

printf("%d\t%s\t\t%c\t%f\t\n",ipstu->num,ipstu->name,

ipstu->sex,ipstu->score);

}

程序分析

在本程序的for循环语句中有ipstu++语句,如果ipstu指向目前的元素,那么ipstu+1后就指向下一个元素的起始地址。

指针ipstu已经定义为指向结构体student类型的数据,那么它就只能指向一个结构体类型的数据,即ipstu的值是数组stu的一个元素的起始地址。不能将某一成员的地址赋给该指针,虽然ipstu是存放地址的。例如,下面的语句是错误的:

ipstu=&stu.name;

这是因为两者的地址类型是不同的,一个是结构体类型的,而另一个则是字符型的。如果必须要这么做,可以使用强制类型转换。例如:

ipstu=(struct student *)&stu.name;

【用指向结构体的指针作函数参数】

在新的ANSI C标准中,可以有三种方法将一个结构体变量的值传递到另一个函数。下面就来介绍这三种方法,其中前两种方法是兼容于旧版本的C标准的,第三种方法则只有新版的C标准才允许。

330 方法一,用结构体变量的成员作参数,将实参值传给形参。这种方法和普通变量作函数实参是一样的,属于“值传递”方式。

方式二,用指向结构体变量的指针作实参,将结构体变量或者数组的起始地址传递给函数的形参。

方式三,用整个结构体作为函数的参数传递,但这种方法要求必须保证实参和形参的类型相同。

下面通过实例来说明这三种方法的使用。关于第一种方法,其实在程序12-5中已经用过。在这个程序的printf函数中,使用结构体变量的成员ipstu->num、ipstu->name等来依次输出所要的数据。使用这种方法的弊端在于:编程时需要书写所用到的每一个成员,而在一般的结构体中都会有很多的成员,这就使书写的工作量加大并且增加了程序出错的机会。

下面这个实例是用第二种方法来实现的。题目的要求是计算五名学生的平均成绩和不及格的人数。详见下面的程序。

程序12-6:P12-6.c

struct student

{

int num;

char *name;

char sex;

float score;

}stu[5]={

{101,"Li ping",'M',87},

{102,"Zhang ping",'M',73.5},

{103,"He fang",'F',92},

{104,"Chen peng",'F',87},

{105,"Wang ming",'M',58},

};

main()

{

struct student *ipstu;

void ave(struct student *ipstu);

ipstu=stu;

ave(ipstu);

}

void ave(struct student *ipstu)

{

int c=0,i;

float ave,s=0;

for(i=0;i<5;i++,ipstu++)

{

s+=ipstu->score;

if(ipstu->score<60) c+=1;

}

printf("s=%f\n",s);

ave=s/5;

331 printf("average=%f\ncount=%d\n",ave,c);

}

程序分析

本程序首先在开始定义一个结构体,并将其放在主函数的外面,这样做的好处是同一源文件中的各个函数都可以使用它来定义结构体变量。比如在主函数中有关于结构体指针变量的定义:

struct student *ipstu;

在子函数ave的参数中,定义形参如下:

struct student *ipstu

主函数将实参以指针的形式传给子函数,即将结构体数组 stu 的起始地址传给了 ave函数。

对于第三种方法,可以参照下面的结构来实现程序。

main()

{

struct 结构体名

{

成员表列

}str1;

f(str1);

}

void f(str1)

struct 结构体名 str1;

{

...

}

在上面的定义中,主函数中的str1是结构体变量,将其作为实参直接传递给子函数f(),当然,根据C标准的规定,实参和形参必须是类型相同的,所以在子函数中指明str1的类型是同主函数一样的结构体类型。

这种用整个结构体作为函数的参数传递的方法并不值得推荐。这是因为:要将全部结构体变量的成员一个一个传递,既浪费时间又浪费空间,系统开销非常大。如果结构体类型中有非常多的成员或者一些成员是数组,那么根据数据结构的知识可以知道,这样做的时间复杂度和空间复杂度都非常大,程序的运行效率就会大大降低。在这样的情况下,用指针做函数参数就比较好,能提高程序的运行效率。

知识储备

如果要详细掌握C语言的各种技巧,就须学习一些诸如“数据结构和算法”

方面的理论知识。

有了数据结构方面的知识,就很容易理解这里的“链表”。学习“数据结构”

既为进一步学习其他高级语言课程提供必要的知识,又有助于提高程序的编写水

平。关于这方面的知识读者可以参考相关的专业书籍。

332 关于“算法”。或许细心的读者已经发现,几乎每一个程序都要涉及到算法的

问题。一个好的算法不但可以使程序更加易读,更加清楚,而且还大大提高程序

的运行效率,节省存储空间。所以,为程序设计一个优秀的算法是必要的。

为学习“链表”,读者还需要回顾一下第8章讲过的动态存储分配,因为链表

结构就是动态分配存储单元的。

【链表】

链表是线性表中的一种。而线性表是最简单、最常用的一种数据结构。线性表的逻辑结构是n个数据元素的有限序列。其中,用顺序存储结构存储的线性表称为顺序表;用链式存储结构存储的线性表称为链表;对线性表的插入、删除运算可以发生的位置加以限制则是两种特殊的线性表——栈和队列。

C语言里的一维数组就是用顺序方式存储的线性表,而链表是与顺序表并列的另外一种数据结构。如前面的定义中所说,链表中的每一个元素称为“结点”,除头指针外,每个结点中含有一个指针域和一个数据域。数据域用来存储用户需要用的实际数据,指针域用来存储下一个结点的地址,用来指出其后续结点的位置。而其最后一个结点没有后续结点,它的指针域为空(空地址NULL)。另外还需要设置一个“头指针”head,指向链表的第一个结点。

需要清楚的是,链表中各元素在内存中可以不是连续存放的。这样的话,如果要找某一元素就必须先找到上一个元素,根据它提供的下一个元素地址才能找到下一个元素。如果没有头指针,那么整个链表就都不能访问。

为实现链表这种结构,就必须用到指针变量,这是因为一个结点中必须包含一个指针变量,这个指针变量存放的是下一个结点的地址。

【链表的建立】

使用链表的一个很重要的优点就是插入、删除运算灵活方便,不需要移动结点,只要改变结点中指针域的值即可。现在先建立一个链表。

链表中的每一个结点都是同一种结构类型。例如,一个存放学生学号和成绩的结点应为以下结构:

struct student

{

int num;

int score;

struct student *next;

};

前两个成员项num和score组成数据域,后一个成员项next构成指针域,它是一个指向student类型结构体的指针变量。下面通过具体实例来讲述如何建立一个链表。见下面的

333例子,要求是写一个函数建立一个三结点的链表,存放学生数据(为简单起见, 假定学生数据结构中只有学号和年龄两项)。

程序12-7:12-7.c

#define NULL 0

#define TYPE struct student

#define LEN sizeof(struct student)

struct student

{

int num;

int age;

struct student *next;

};

TYPE *creat(int n)

{

struct student *head,*pf,*pb;

int i;

for(i=0;i<n;i++)

{

pb=(TYPE*) malloc(LEN);

printf("input Number and Age\n");

scanf("%d%d",&pb->num,&pb->age);

if(i==0)

pf=head=pb;

else

pf->next=pb;

pb->next=NULL;

pf=pb;

}

return(head);

}

程序分析

首先进行算法分析。设定三个指针变量head,pf和pb,均指向相同的结构体类型数据。head为头指针,pf为指向后两个结点的前一个结点的指针变量,pb为指向后一个结点的指针变量。在for循环语句内,首先用malloc函数开辟一个结点,其长度与student结构体长度相等。并且将该结点的首地址赋予pb。

关于malloc函数和sizeof函数(sizeof为求字节数运算符)的用法请参见第11章“指针”中的相关专题。

然后输入结点的数据。判断当前结点的属性:如果当前结点为第一个结点(i==0),则把pb值(该结点的指针)赋予头指针head和pf。如果不是第一个结点,就将pb值赋予pf所指结点的指针域成员next(pf?>next)。不管怎样,pb所指结点在当前总是为最后结点,其指针域(next)赋NULL。

再把pb值赋予pf以作下一次循环准备。

整个程序的算法思路就是:让 pb 接受新开结点的数据,并且将其原来的数据传送到pf中,即pb成为链表的最后一个结点。由此可见,算法在程序编制过程中是非常重要的。

334有关算法的知识,请参见相关的专业书籍。

下面对整个程序作一个分析。首先,在程序的第一行利用宏定义NULL为0,即用它来代表“空地址”。第二行定义TYPE代表一个结构体的定义形式。第三行令LEN代表structstudent结构体类型数据的长度。

程序中定义了一个指针类型的creat函数,它带回一个指针值,指向一个struct student类型数据。但实际上,此creat函数带回的是一个链表的起始地址。

在程序中,用malloc函数开辟了一个长度为LEN的内存区后,必须对其进行类型的强制转换。这是因为一般系统中的malloc函数带回的是指向字符型数据的指针,而pf和pb则是指向struct student类型数据的指针,两者所指的是不同类型的数据。所以必须使用强制类型转换使得malloc函数带回的指针变为结构体类型。

在函数creat的最后有语句:

return(head);

return的参数是指向struct student类型数据的指针变量,因此函数返回的是head的值即链表的头地址。

【链表的查找与输出】

如果问题是将链表中各个结点的数据依次输出,这就比较容易处理。首先,需要知道链表的头结点的地址,也就是head的值。然后可以设一指针变量p指向第一个结点,输出该结点后使p移向下一个结点,再输出下一个结点,直到链表的尾结点。但是有时候问题会比现在复杂的多。比如,需要在链表中查找某些符合条件的结点,然后将这些结点输出。下面通过实例说明如何查找链表。

程序12-8:P12-8.c

#define NULL 0

#define TYPE struct student

struct student

{

int num;

int age;

struct student *next;

};

TYPE *search (TYPE *head,int n)

{

TYPE *p;

int i;

p=head;

while(p->num!=n&&p->next!=NULL)

p=p->next; /* 如果不是要找的结点就将其后移一步*/

if(p->num==n)

return (p);

if(p->num!=n&&p->next==NULL)

printf ("Node %d has not been found!\n",n

335 }

程序分析

search函数有两个形参,head是指向链表的指针变量,n为要查找的学号。在while语句中逐个检查结点的num成员是否等于n,如果不等于n且指针域不等于NULL(不是最后结点)则后移一个结点,继续循环。如找到该结点则返回结点指针。如循环结束仍未找到该结点则输出“未找到”的提示信息。

下面说明如何输出链表,见下面的程序。

程序12-9:P12-9.c

#define NULL 0

#define TYPE struct student

struct student

{

int num;

int age;

struct student *next;

};

void print(head)

TYPE *head;

{

TYPE *p;

printf("\nNow,These %d records are:\n",n);

p=head;

if(head!=NULL)

do

{

printf("%d %d\n",p->num,p->age);

p=p->next;

}

while(p!=NULL);

}

程序分析

程序的算法很简单,就是先让p指向第一个结点,输出第一个结点之后再指向第二个结点,依次顺序输出直至遇到最后一个结点。而这种传递过程是通过语句:

p=p->next;

来实现的。p->next的值就是第二个结点的起始地址,将它赋给p也就是让p指向第二个结点。

【链表的删除操作】

删除链表就是将链表中的某一结点删除。但只是将该结点从链表中删掉,而并不是将其从内存中删掉。删除一个结点有两种情况:

第一种情况是被删除结点就是该链表的第一个结点,这种情况只需使头指针head指向

336第二个结点即可。

第二种情况是被删除结点不是第一个结点,这种情况只要使被删结点的前一结点指向被删结点的后一结点即可。

下面通过例题具体解释如何进行链表的删除操作,见下面的程序。

程序12-10:P12-10.c

TYPE *delete(TYPE *head,int num)

{

TYPE *pf,*pb;

if(head==NULL) /*如为空表,输出提示信息*/

{

printf("\nempty list!\n");

goto end;

}

pb=head;

while(pb->num!=num&&pb->next!=NULL)

/*当不是要删除的结点,而且也不是最后一个结点时,继续循环*/

{

pf=pb;

pb=pb->next;

} /*pf指向当前结点,pb指向下一结点*/

if(pb->num==num)

{

if(pb==head)

head=pb->next;

/*如找到被删结点,且为第一结点,则使head指向第二个结

点否则使pf所指结点的指针指向下一结点*/

else

pf->next=pb->next;

free(pb);

printf("The node is deleted\n");

}

else

printf("The node not been foud!\n");

end:

return(head);

}

程序分析

在考虑“链表的删除操作”的算法时,需要解决下面几种情况。首先要从第一个结点开始检查该结点中的num值是否等于输入的要求删除的那个学号,如果是就要将其删除,如果不是就继续查找直到链表的最后。

在第一个结点的时候,还要考虑链表是否为空表,如果为空表就不必查找了,如果非空就继续查找。还有一种情况是在链表中找不到要删除的结点,那么就要输出找不到结点的信息。

在一般的情况下要删除结点首先要判断该结点是否为头结点,如果是则使head指向第二结点,即把第一个结点从链表中删去;否则使被删结点的前一个结点(pf所指向的结点)

337指向被删结点的后一个结点(被删结点的指针域所指向的结点)。

338【链表的插入操作】

首先要设已经建立好了一个链表,并且链表中各个结点的成员项num已经按照学号由小到大的顺序排列好。

由于插入的位置在原链表中不同,因而算法也不同。根据插入位置可以分成下面的四种情况分析。

第一种情况是原来的链表是空表,这时只需使头指针head指向被插结点即可。

第二种情况是被插入的结点的值最小,这时就应插入第一个结点之前。这种情况下的算法可以设计成:使头指针head指向被插结点,而被插结点的指针域指向原来的第一个结点。即:

pi->next=pb;

head=pi;

第三种情况是插入结点的位置是链表中间的某一位置,这时可以使插入位置的前一个结点的指针域指向被插结点,使被插结点的指针域指向插入位置的后一个结点。即:

pi->next=pb;

pf->next=pi;

第四种情况是插入结点的位置在链表的末尾,这种情况只需使原链表表末结点指针域指向被插结点,而被插结点指针域置为NULL。即:

pb->next=pi;

pi->next=NULL;

下面我们给出链表插入操作的函数insert,结合这个例子说明链表插入操作的具体步骤。

程序12-11:P12-11.c

TYPE *insert(TYPE *head,TYPE *pi)

{

TYPE *pf,*pb;

pb=head;

if(head==NULL) /*空表插入操作*/

{

head=pi;

pi->next=NULL;

}

else

{

while((pi->num>pb->num)&&(pb->next!=NULL))

{

pf=pb;

pb=pb->next;

} /*找插入位置*/

if(pi->num<=pb->num)

{

339 if(head==pb)

head=pi; /*在第一结点之前插入*/

else

pf->next=pi; /*在其他位置插入*/

pi->next=pb;

}

else

{

pb->next=pi;

pi->next=NULL;

} /*在表末插入*/

}

return head;

}

程序分析

在本程序中,定义insert的两个形参head和pi均为指针变量。head指向链表,pi指向被插结点。在函数中首先判断链表是否为空链表,如果为空则使head指向被插结点。如果链表不是空链表,则用 while 语句循环查找应该插入的位置。找到之后再判断是否应该在第一个结点之前插入,如果是那么就使head 指向被插结点,而被插结点指针域则指向原第一个结点,否则就是在其他位置插入。如果插入的结点大于表中所有结点,则在链表末尾插入。

本函数返回值是一个指针,它是链表的头指针。当插入的位置在第一个结点之前时, 插入的新结点成为链表的第一个结点,因此head的值也有了改变, 故需要把这个指针返回主函数。

在上面讲述了链表的建立、链表的查找、链表的删除、链表的插入等四种关于链表的操作,同时给出了关于每一种操作的函数。下面我们给出一个主函数,它的作用是将上面的四种操作结合起来组成一个完整的链表程序,在这个程序里,包括了所有的关于链表的操作,见下面的程序。

程序12-12:P12-12.c

main()

{

TYPE *head,*pnum;

int n,num;

printf("Input number of node: ");

scanf("%d",&n);

head=creat(n);

print(head);

printf("Input the deleted number: ");

scanf("%d",&num);

head=delete(head,num);

print(head);

printf("Input the inserted number and age: ");

pnum=(TYPE *)malloc(LEN);

scanf("%d%d",&pnum->num,&pnum->age);

head=insert(head,pnum);

340 print(head);

}

程序分析

在这个main函数中,n为将要建立的结点的数目,num为将要删除的结点的数据域的值,head为指向链表的头指针,pnum为指向将要插入结点的指针。

调用的各个函数的分析可以参见前面的叙述,在此不再赘述。

指针和链表的总结

在第11章“指针”的叙述中读者可以看到,虽然将指针用于普通程序的编制

可以实现几乎所有的程序,但使用指针并不是一个很好的想法,有时候甚至会使

问题变得更加复杂。但是对于一些高级的数据结构来说,使用指针不但使问题变

得非常简单,有时候甚至是其他方法所无法办到的。这些数据结构包括链表、栈、

队列、树,等等。指针用在这些数据结构上才显示出它独特的作用。

链表是数据结构中比较简单的一种,可以用指针来方便地实现。对链表的熟

练掌握将有助于掌握其他类型的数据结构。

【共用体】

共用体又称为联合,它是使用覆盖技术,让几个变量互相覆盖,也就是使得几个不同的变量共占同一段内存。共用体类型变量的一般定义形式是:

union 共用体名

{

成员表列

}变量表列;

可以看出,共用体和结构体的定义形式是类似的。但同结构体相比,两者是有本质区别的:结构体变量的每一个成员都有自己的存储空间,一个结构体变量所占的内存长度就是各个成员所占的长度之和。但对于共用体来说,系统并不是为每一个共用体成员分配一个内存空间,而是所有成员公用同一段内存空间,这样一来,共用体变量所占的内存长度等于最长的成员的长度。

这里需要指明的是,共用体中所说的共用并不是说将所有的成员同时存储在这一段内存空间中,而是指该共用体变量可被赋予任一成员值,但每次只能赋一种值,赋入新值则冲去旧值。也就是说,不能同时为一个共用体变量的两个成员赋值。

同结构体变量的定义形式相同,共用体变量的定义也有三种不同的形式,如下所示:

union student

{

int num;

char sex;

float score;

}a,b,c;

341 这是一种定义形式。当然,也可以将类型定义与变量的定义分开:

union student

{

int num;

char sex;

float score;

};

union student a,b,c;

这是第二种方法,它是先定义一个union student类型,然后再定义a、b、c为union student类型。还可以直接定义共用体变量,例如:

union

{

int num;

char sex;

float score;

}a,b,c;

与结构体变量相同,对共用体变量的赋值和使用都要在共用体变量成员的基础上进行。也就是说不允许只用共用体变量名进行赋值运算或其他运算操作。而且C语言规定不能对共用体变量进行初始化赋值,赋值只能在程序中进行。还要注意的是,一个共用体变量每次只能赋给一个成员值,即一个共用体变量的值就是共用体变量里某一成员的值。

定义了共用体变量后就可以引用它。现在假设定义了共用体变量a,则引用它的形式是:

a.i (引用共用体变量中的整型变量i)

a.ch (引用共用体变量中的字符型变量ch)

但下面的引用是错误的:

printf("%d",a);

现在通过一个例子来说明共用体类型数据的特点。例子是这样的,有一张表格,里面存放着教师和学生的数据。学生数据中包括姓名、年龄、职业、班级四项,教师数据中包括有姓名、年龄、职业、教研室四项。现要编写一个程序输入人员的数据。程序如下所示。

程序12-13:P12-13.c

main()

{

struct

{

char name[10];

int age;

char job;

union

{

int class;

char office[10];

} depa;

342 }body[2];

int n,i;

for(i=0;i<2;i++)

{

printf("input name,age,job and department\n");

scanf("%s %d %c",

body[i].name,&body[i].age,&body[i].job);

if(body[i].job=='s')

scanf("%d",&body[i].depa.class);

else

scanf("%s",body[i].depa.office);

}

printf("name\tage job class/office\n");

for(i=0;i<2;i++)

{

if(body[i].job=='s')

printf("%s\t%3d %3c %d\n",

body[i].name,body[i].age,body[i].job,body[i].depa.class);

else

printf("%s\t%3d %3c %s\n",body[i].name,body[i].age,

body[i].job,body[i].depa.office);

}

}

程序分析

对于共用体变量来说,除了上面所说的特性之外,还需要读者注意的是:(1)共用体变量的地址和它的各成员的地址都是同一地址。(2)共用体变量不能作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针。(3)结构体类型的定义中可以出现共用体类型,反之,共用体类型的定义中也可以出现结构体类型,也就是说,这两种类型可以相互嵌套引用。(4)可以定义共用体数组,在数组中也可以引用共用体变量的成员。

对于上面的程序,使用一个结构体数组body来存放人员数据,该结构体共有四个成员。其中成员项depa是一个共用体类型,这个共用体又由两个成员组成,一个是整型量class,一个是字符数组office。

在程序的第一个for语句中,输入人员的各项数据,先输入前三个成员name、age和job,然后判别job成员项,如为"s"则对共用体depa.class输入(对学生赋班级编号),否则对depa.office输入(对教师赋教研组名)。在用scanf语句输入时要注意,凡为数组类型的成员,无论是结构成员还是共用体成员,在该项前不能再加"&"运算符。

【枚举类型】

ANSI C新标准增加了枚举类型。

“枚举”就是将某一变量的所有可能取值都罗列出来。比如一周之内有7天,可以用星期一,星期二,??星期日这样的数据列举出来。一年之内有12个月,可以用一月,二月??十二月列举出来,等等。在C语言中,专门提供了一种类型来表示这样的数据,这

343就是枚举类型。枚举类型也是同整型、字符型等数据类型一样的一种基本数据类型,这是同结构体类型和共用体类型等构造类型不同的。这种类型不能再分解为任何其他基本的数据类型。

枚举类型的一般定义形式是:

enum 枚举名

{

枚举值表列

};

例如下面的定义:

enum weekday

{

sun,mon,tue,wed,thu,fri,sat

};

定义了枚举类型之后,就可以用它来定义变量了。例如上面定义了weekday枚举类型,就可以用它来定义该种类型的变量。同结构体和共用体一样,有三种方法说明一变量为枚举类型的变量。如下:

enum weekday workday;

或者直接定义,如下:

enum weekday

{

sun,mon,tue,wed,thu,fri,sat

}workday;

也可以如下形式直接定义:

enum

{

sun,mon,tue,wed,thu,fri,sat

}workday;

在上面的定义中,像sun、mon?sat等被称为枚举元素或者枚举常量。它们是由程序员定义的标志符。标志符一旦定义完成后,在这个程序中就始终代表一定的意思。当然,这些标志符不是自动代表什么含义,而是必须要程序员定义之后才能代表特定的含义。

下面通过一个例子详细说明枚举类型变量的用法,见下面的程序。

程序12-14:P12-14.c

main()

{

enum body

{

a,b,c,d

}month[31],j;

int i;

j=a;

for(i=1;i<=30;i++)

344 {

month[i]=j;

j++;

if(j>d)

j=a;

}

for(i=1;i<=30;i++)

{

switch(month[i])

{

case a:printf(" %2d %c\t",i,'a'); break;

case b:printf(" %2d %c\t",i,'b'); break;

case c:printf(" %2d %c\t",i,'c'); break;

case d:printf(" %2d %c\t",i,'d'); break;

default:break;

}

}

printf("\n");

}

程序分析

在C的编译时,对于枚举元素是作为常量来处理的,所以称其为枚举常量。由于它们不是变量,所以不能对它们进行赋值。在上面的程序中如果有下面的语句那就是错误的:

a=1;b=30;

可以把枚举值赋予枚举变量,但不能把任意元素的数值直接赋予枚举变量。如果一定要把数值赋予枚举变量,则必须用强制类型转换,如:

j=(enum body)p;

还应该说明的是枚举元素不是字符常量也不是字符串常量, 使用时不要加单引号或者双引号。

枚举元素作为常量是有值的,它们的值是在定义时就确定好的。C编译时按照定义时的顺序给每一个枚举元素赋值0,1,2,?。在上面的定义中,a的值是0,b的值是1,依此类推。也可以改变枚举元素的值,这需要在定义时指明,比如可以像下面的形式一样改变枚举元素的值:

enum body

{

a=9,b=1,c,d

} month[31],j;

这样就定义了a是9,b是1,c和d的值是在b的基础上顺序加1,即c是2,d是3。

枚举值是可以用来作判断比较的,其比较的规则是:按其定义时的顺序号来比较。【typedef类型定义】

通过前面的介绍读者不难看出,C语言提供了丰富的数据类型。用户不但可以直接使

345用C提供的标准类型如int、char、float、double、long,等等,还可以定义结构体、共用体、指针、枚举类型。除此以外,C语言还允许用户用typedef定义新的类型名来代替已经存在的类型名。

例如现有整型变量a,b,其说明如下:

int a,b;

其中int是整型变量的类型说明符。int的完整写法为integer,为了增加程序的可读性,可把整型说明符用typedef定义为:

typedef int INTEGER

这以后就可用INTEGER来代替int作整型变量的类型说明了。 例如:

INTEGER a,b;

它等效于:

int a,b;

如果读者对FORTRAN这种语言非常熟悉,那么就可以用INTEGER来定义变量,以适应他们的习惯。typedef就是为适应这种需要而产生的。

如果现在有一结构体类型的定义:

typedef struct

{

int num;

char name[20];

char sex;

float score;

}STUDENT;

这里定义的新类型 STUDENT 就代表上面的结构体类型,以后就可以直接使用STUDENT来定义其他的结构体变量,例如:

STUDENT stu1;

用typedef来定义数组、指针、结构体等类型会给编程带来非常大的方便,而且可以使得程序的意义更加简明,书写更为简单,从而使可读性大大提高。

typedef定义的一般形式为:

typedef 原类型名 新类型名;

习惯上经常把用typedef定义的新类型名用大写字母表示,以便与系统提供的标准类型名相区别。定义的步骤可以分成三步,首先是按照定义变量的方法写出定义体,然后用typedef将变量名换成新类型名,再用新类型名定义变量。比如定义一个数组类型,首先按照原来定义数组的形式写成:

char count[100];

改变变量名,使用typeef产生新的类型名:

typedef char COUNT[100];

346 然后就可以用它来定义新的变量:

COUNT a,b;

用typedef可以定义各种类型名,但不能用来定义变量。如果要定义变量,需要用新定义的类型名来定义变量。这样就将类型的定义和变量的定义分离开来,利用新的类型可以定义多个相同类型的变量。

如果有一些数据类型需要在许多不同的源文件中用到,那么就可以用typedef定义这些数据类型,将它们单独放到一个文件里边,使用时可以用#include命令把它们包含到源文件中。

typedef只是将已经存在的类型变成另外的用户熟悉的类型名,并没有创造任何新的类型。typedef和宏命令#define有相似之处,但在本质上,两者是不同的。#define是在编译之前预先处理的,它只能作简单的字符串替换。而typedef则是在编译时处理,也不是仅作简单的字符串替换,而是如同定义变量那样来定义一个类型。

使用typedef的好处不但增加了程序的易读性,而且增加了程序的通用性和可移植性。例如,有时候程序会对硬件特性产生较大的依赖性。有的计算机系统整型数据用两个字节表示,而有的系统则需要4个字节来表示。如果把一个C程序从一个以4个字节存放整数的计算机系统移植到以两个字节存放整数的计算机系统,只需改变typedef定义:

typedef int INTEGER;

改为:

typedef long INTEGER;

即,如果需要进行程序移植,只需改动typedef定义即可。

典 型 例 题

【例题12-1 结构体和共用体的性质】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若有下面的说明和定义:

struct test

{

int ml;

char m2;

float m3;

union uu

{

char ul[5];

347 int u2[2];

}ua;

}myaa;

则sizeof(struct test)的值是( )。

A)12 B)16 C)14 D)9

解题方法

这道题显然是考查结构体和共用体知识的。对于结构体和共用体这两种数据类型,读者一定要清楚它们的定义和一些需要注意的细节知识。这是因为,这两种数据结构在所有的C语言的数据结构中是最复杂的,所以考查它们的概念的机会就比其他的数据类型多。特别是关于这两种数据类型和其他数据类型的不同之处一直是计算机等级考试考查的重点知识。

知识点分析

首先要清楚的是,共用体变量的各个成员是共同占有同一内存单元的。共用体变量所占的内存长度等于最长的成员的长度。在上面的定义中,共用体变量ua中最长的成员是字符数组u1,它的长度是5个字节。

其次还要清楚结构体与共用体类型数据是有区别的。结构体变量所占的内存长度是各个成员所占的内存长度之和。每个结构体变量成员都分别占有其自己的内存单元。

知道了上面的知识后,本题就不难解答了。题目中给出了一个结构体变量的定义,在结构体成员中,嵌套了一个共用体变量ua,它的长度是5个字节。结构体变量成员m1是一整型数据,它的长度是2个字节;成员m2是一字符型数据,它的长度是1个字节;成员m3是一浮点型数据,它的长度是4个字节。所有的结构体变量成员的长度相加就是结构体变量的长度。将上面的长度相加得到12,这就是结构体变量myaa的长度。

正确答案

A

难度提示

初级。本题主要考查关于结构体和共用体的基础知识,考查的知识点也不多,属于简单题目。

【例题12-2 链表的操作】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若以下定义:

struct link

{

int data;

struck link *next;

}a,b,c,*p,*q;

348 且变量a和b之间已有如下图所示的链表结构:

a b c

dat next dat next dat next

5 9 /0 7

p q

指针p指向变量a;q指向变量c。则能够把c插入到a和b之间并形成新的链表的语句组是:( )。

A)a.next=c; c.next=b;

B)p.next=q; q.next=p.next;

C)p?>next=&c; q?>next=p->next;

D)(*p).next=q; (*q).next=&b;

解题方法

链表是C语言中用指针来实现的一种数据结构,这是C语言中非常有特色的一部分内容,也是C语言中很复杂和难以掌握的一部分。对于这部分知识,读者要对例题中给出的程序有准确的理解,并对程序的结构了如指掌。因为对链表来说,其程序结构是固定的,没有什么大的变化。

知识点分析

题目中给出的是链表的一部分,需要进行的操作是插入操作。插入的位置不是链表的头结点,也不是链表的尾结点,而是链表中的一般结点之间。对这样的操作来说,它的算法是:将待插入结点的指针变量的值赋给上一结点的next指针,即让上一结点的指针域指向待插入的结点;然后将后一结点的值赋给待插入结点的指针域,

即让待插入结点指向后一结点。这样就在两个结点之间插入了一个结点。

在定义中有下面的语句:

struck link *next;

它是指针类型的成员,其指向为struct link类型的数据。成员next的作用是存放下一个结点的地址,程序设计人员可以不知道具体的地址值,只要保证将下一个结点的地址放到前一结点的成员next中就可以了。

弄清楚了算法和链表的结构实现方法之后就很容易解答本题了。

正确答案

C

难度提示

高级。本题是关于链表的一道考题。链表的操作比其他的数据结构更复杂,这就使得本题有一定的难度。由于平时这一部分受到的重视不够,所以对许多人来说,这又是增加

349难度的一个因素。要正确解答这种题目,需要读者对链表这一部分知识能灵活掌握。

350【例题12-3 共用体的基本概念】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

设有以下说明语句

typedef struct

{

int n;

char ch[8];

}PER;

则下面叙述中正确的是( )。

A)PER 是结构体变量名 B)PER是结构体类型名

C)typedef struct 是结构体类型 D)struct是结构体类型名

解题方法

本题是关于结构体和 typedef 类型定义的一道题。想要正确解答这道题,需要清楚typedef类型定义的基本概念。

知识点分析

typedef类型定义的形式跟一般的类型定义有非常相似的地方,特别是在跟结构体变量的定义混在一块儿的时候就更难区别。

比如下面的定义:

typedef int INTEGER;

如果将typedef关键字去掉的话,原来的语句就变成了下面的整型变量的定义:

int INTEGER;

即相当于定义了一个变量名为INTEGER的整型变量。而如果前面有typedef这个关键字,就说明该语句是typedef类型定义,即说明现在是定义INTEGER就是代表int这种类型。

与此类似,题目中的typedef去掉后,就变成了定义一个结构体类型的变量 PER,即PER是结构体变量名。但正因为有了typedef,才说明了现在进行的是typedef类型定义,就是说现在的意思是让PER代表一个结构体类型,属于类型定义,而不是变量定义。在以后的程序中就可以用PER这种类型来定义其他的结构体变量。

看一个语句是否为typedef类型定义,主要是看有否关键字typedef。如果有,就说明是typedef类型定义,没有就说明是一般的变量定义。

正确答案

B

难度提示

351 中级。本题涉及到的typedef类型定义很容易跟变量定义混淆,这一点也是考试出题的切入点。如果考生在平时能留意这些知识点,那么解答这道题就不成问题了。

【例题12-4 结构体定义】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下定义的结构体类型拟包含两个成员,其中成员变量info用来存入整型数据;成员变量link是指向自身结构体的指针,请将定义补充完整。

struct node

{

int info;

______link;

};

解题方法

本题是关于结构体类型定义的一道题。解答这种题的方法就是熟悉结构体这部分内容,除此之外没有什么特别的技巧。

知识点分析

对于结构体的定义,读者应该是很清楚了。而这道题的目的就是考查读者的掌握情况。值得注意的是,这道题的结构其实是定义一个链表的。定义中的成员info可以存放结点数据,而link题目中已经指明是一个指向自身结构体的指针,这就相当于一般定义中的next成员。

如果读者对链表的定义非常熟悉的话,是很容易看出这点的,那么这道题也就很容易解答。前面已经说过,熟练掌握链表的定义式是很有必要的。

正确答案

struct node *

难度提示

中级。同前面所说,如果熟悉了链表的定义,本题不难作答。

【例题12-5 typedef类型定义】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下各选项企图说明一种新的类型名,其中正确的是( )。

A)typedef v1 int;

B)typedef v2=int;

C)typedef int v3;

352 D)typedef v4: int;

解题方法

对于typedef类型定义这一部分内容,建议读者在它的基本概念上多花些时间。因为基本概念是考试的重点,也是掌握其他知识的一个基础。

知识点分析

typedef类型定义有其自己的形式,而且形式几乎是惟一的。其形式在基础知识点分析中已经做过介绍,如下所示:

typedef 原类型名 新类型名;

从这种形式看来,本题的答案十分明了。

正确答案

C

难度提示

初级。只要掌握了typedef类型定义的基本形式,本题就不难作答。

【例题12-6 结构体变量的定义和引用】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下程序的输出结果是( )。

程序12-15:P12-15.c

struct HAR

{

int x,y;

struct HAR *p;

}h[2];

main()

{

h[0].x=1;

h[0].y=2;

h[1].x=3;

h[1].y=4;

h[0].p=&h[1].p=h;

printf("%d %d \n",(h[0].p)->x,(h[1].p)->y);

}

A)1 2 B)2 3

C)1 4 D)3 2

解题方法

353 本题仍然是有关链表的问题。链表是C语言中用指针实现的很重要的一种数据结构。而且同其他复杂的数据结构相比,它又是相对来说较为简单的。所以对它的考查是这部分知识的一个重点。

知识点分析

对于结构体来说,对它的操作无非是结构体变量的定义,成员的赋值、引用等操作。而对链表而言,也大多集中在链表的建立、查找、删除和插入等操作上。也就是说,涉及到的操作并不是很复杂。只要读者掌握了一些基本的典型的操作,就可以应对这部分内容。

本题首先定义了一个结构体数组,或者说是一个链表。在main函数中对其进行赋值运算。然后将它打印出来。解题的关键在于判断(h[0].p)?>x和(h[1].p)?>y的值是多少。而判断该值就需要弄清楚下面语句的含义:

h[0].p=&h[1].p=h;

在执行了上面的语句后,h[0]变成了第二个结点的值,h[1]则变成了第一个结点的值。所以输出语句就相当于输出第二个结点的x成员值,输出第一个结点的y成员值。

正答案

D

难度提示

中级。本题考查的知识点仍然是结构体变量的定义和引用。没有什么特别的知识点在里面。

【例题12-7 共用体的性质】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下程序的输出结果是( )。

程序12-16:P12-16.c

union myun

{

struct

{

int x,y,z;

}u;

int k;

}a;

main()

{ a.u.x=4;

a.u.y=5;

a.u.z=6;

354 a.k=0;

printf(%d\n",a.u.x);

}

A)4 B)5

C)6 D)0

解题方法

这道题非常有意思。考生费尽心思分析完题目后发现,原来题目的答案很简单。

知识点分析

本题考查的是共用体的知识,还涉及到一点结构体变量的知识。为清楚起见,考查题目的逻辑关系。

首先定义了一个共用体变量a,在这个共用体变量中嵌套了一个结构体变量u。然后在main函数中对其进行赋值。先对结构体变量进行赋值:令其成员x,y,z依次是4,5,6。对结构体变量赋完值后接着对共用体变量的另外一个成员k进行赋值0。

看到这里,或许读者有这样的印象:共用体变量a的所有成员均赋值完毕,每一成员都有自己的数值了。这是非常错误的。

因为在共用体变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原来的成员就失去作用。也就是说,在完成题目中所有的赋值后,只有a.k=0是有效的,a.u已经失去意义了。因此在引用共用体变量时要十分注意当前存放在共用体变量中的究竟是哪一个成员。

正确答案

D

难度提示

高级。本题的编制角度非常好。直接命中了许多人都忽略的一个问题,同时也提醒考生,平时学习时应该多注意C语言的一些特别之处。

【例题12-8 链表的构造】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下程序段用于构成一个简单的单向链表,请填空。

struct STRU

{ int x, y ;

float rate;

______ p;

}a,b;

a.x=0; a.y=0;

a.rate=0; a.p=&b;

b.x=0; b.y=0;

355 b.rate=0; b.p=NULL;

解题方法

本题的题意已经明确指出这是关于构成单向链表的问题。

知识点分析

本题定义了两个结构体变量a和b,这两个变量存储链表的两个结点的值。下面接着对各个结点进行赋值运算,其中,后一个结点的指针成员p赋予空指针NULL,表明这是链表的最后一个结点。

题目考查的是考生对链表定义形式的掌握。只要掌握了一般的链表定义形式,就不难解答本题。

正确答案

struct STRU *

难度提示

中级。本题考查的知识点比较简单,是考生应该很熟悉的知识,属于中等程度的题目。【例题12-9 结构体数组的定义】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

若有如下结构体说明:

struct STRU

{ int a,b;

char c;

double d;

struct STRU p1,p2;

};

______ t[20];

请填空,以完成对t数组的定义,t数组的每个元素为该结构体类型。

解题方法

本题是关于结构体数组的定义的。读者在学习结构体这一部分时,可以将结构体与其它类型数据之间的关系作一总结,找出它们之间的相同之处与不同之处。

知识点分析

题目首先定义了一个结构体类型struct STRU,并没有定义该类型的任何变量。题目的要求就是定义一个该结构体类型的数组。

结构体数组的定义形式同结构体类型的其他变量的定义形式类似,为:

struct 结构体类型名 结构体变量名(结构体数组名);

清楚了上面的定义形式后,就容易对本题作出解答了。

356 正确答案

struct STRU

难度提示

中级。本题的难度不大,主要是关于定义结构体数组这个单一知识点的。

【例题12-10 结构体与数组】

(全国计算机等级考试二级笔试试卷19xx年9月)

题干

下面程序的输出结果为( )。

程序12-10:P12-10.c

struct st

{int x;

int *y;

}*p;

int dt[4]={10,20,30,40};

struct st aa[4]={ 50,&dt[0],60,&dt[1],70,&dt[2],80,&dt[3] };

main()

{ p=aa;

printf("%d\n",++p->x);

printf("%d\n",(++p)->x);

printf("%d\n",++(*p->y));

}

A)10 20 20

B)50 60 21

C)51 60 21

D)60 70 31

解题方法

本题同上一题一样也是关于结构体和数组的。

知识点分析

题目首先定义了一个结构体类型 st,同时定义了一个结构体指针变量 p。然后定义了数组dt并对其赋初值。接着定义了struct st结构体类型的变量aa并对其赋初值。

在主函数中,将结构体数组aa赋给同类型的变量p。

执行第一个printf语句,输出(p?>x)自加一后的值;执行第二个printf语句后,输出p自加一后的成员x的值;同样的道理执行第三个printf语句。

357正确答案

C

难度提示

高级。本题是一道综合的题目。关于结构体和数组的知识在此不再赘述。

358 本 章 练 习

【填空题】

1.结构体又称为“______”,是由具有不同数据类型的多个变量组合而成的数据存储形式。定义一个结构体类型的一般形式为:

struct 结构体名{成员列表};

其中的成员又可以称为“______”,成员表列可以称为“______”。

2.如果需要将几种不同类型的变量存放到同一段内存单元中,可以使用______类型数据。如果一个变量只有几种可能的值,则可以定义______类型数据结构。

3.以下程序用来输出结构体变量ex所占存储单元的字节数,请填空。

程序12-11:P12-11.c

struct st

{ char name[20];

double score;};

main()

{ struct st ex;

printf("ex size: %d\n",sizeof(______));

}

4.以下程序建立了一个带有头结点的单向链表,链表结点中的数据通过键盘输入,当输入数据为-1 时,表示输入结束(链表头结点的 data 域不放数据,表空的条件是ph->next==NULL)。

程序12-12:P12-12.c

#include<stdio.h>

struct list

{ int data;

struct list *next;};

______ creatlist()

{ struct list *p,*q,*ph;

int a;

ph=(struct list *)malloc(sizeof(struct list));

p=q=ph;

printf("Input an integer number,enter -1 to end:\n");

scanf("%d",&a);

while(a!=-1)

{ p=(struct list *)malloc(sizeof(struct list));

p->data=a;

q->next=p;

______=p;

scanf("%d",&a);

359 }

p->next='\0';

return(ph);

}

main()

{ struct list *head;

head=creatlist();

}

5.字符 '0' 的ASCII码的十进制数为48,且数组的第0个元素在低位,则以下程序的输出结果是

程序12-13:P12-13.c

#include<stdio.h>

main( )

{ union

{ int i[2];

long k;

char c[4];

}r,*s=&r;

s->i[0]=0x39;

s->i[1]=0x38;

printf("%c\n",s->c[0])

}

【综合题】

1.创建一个五结点的链表,输入数据并且按照从大到小的顺序排序。

2.现在有三个候选人,设计一个程序对候选人的得票进行统计。候选人每得一票,就将其名字输入一次,最后输出各候选人的得票情况。

3.有5个学生,每个学生的情况包括学号、姓名、成绩三项。现要求编写一个程序找出成绩最高和最低者的姓名和成绩。

4.定义一个可以存储下列数据的结构。

学生学号:整型数据

学生姓名:字符型数据

性别:字符型数据

年龄:实型数据

系名:字符型数据

宿舍楼号:整型数据

家庭成员1:字符型数据

家庭成员2:字符型数据

家庭成员3:字符型数据

5.利用上述数据结构,编制程序输入10组数据,然后按照从大到小的顺序排序。

360【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

361 第 13 章 位 运 算

考纲要求:

1.位运算符的含义及使用。

2.简单的位运算。

知识点讲析

【位运算】

所谓位运算是指进行二进制位的运算。也就是说,位运算是以“位”为单位进行的运算,即程序是在“位(bit)”一级进行运算和处理,而以前所接触的都是以字节为最基本单位进行的运算。

【位运算符】

C语言提供了六种位运算符,如下:

& 按位与

¦ 按位或

Λ 按位异或

~ 取反

<< 左移

>> 右移

位运算符中除了“~”以外,其余均为二目运算符,即要求运算符的两侧各有一个运算量。并且运算量只能是整型或者字符型的数据,不能是实型数据。

【“按位与”运算符&】

“按位与”运算的逻辑关系同数理逻辑中的“与”运算非常类似。即如果参加运算的两个运算量的两个相应的位都为“1”,那么该位的结果值为“1”;如果两个运算量的两个相应位有一个为“0”或者两个均为“0”,那么该位的运算结果为“0”。即有:

0&0=0;

0&1=0;

362 1&0=0;

1&1=1;

【“按位或”运算符¦】

“按位或”运算符的逻辑关系类同数理逻辑中的“或”运算。两个相应位中有一个为“1”或者两个均为“1”,则运算后的结果是“1”;如果两个均为“0”,那么结果值为“0”。即有:

0¦0=0;

0¦1=1;

1¦0=1;

1¦1=1;

【“按位异或”运算符Λ】

“按位异或”运算符,也被称为“XOR”运算符。它的意思是:判断两个相应的位值是否为“异”,如果为“异”结果就为“真”,否则为“假”。同数理逻辑中的“异或”运算类似,当参加运算的两个相应位同号的话,即两个位同为“0”或同为“1”,那么运算的结果值为“0”;如果两个相应位异号的话,即一个为“0”一个为“1”,那么运算值为“1”。即有:

0Λ0=0;

0Λ1=1;

1Λ0=1;

1Λ1=0;

【“取反”运算符~】

“取反”运算符是六个位运算符中惟一的一个单目运算符,其性质是对一个二进制数按位取反,也就是说,将0变为1,将1变为0。

【“左移”运算符<<】

“左移”运算符的作用是将一个数的各二进位全部左移若干位。左移的位数是由“<<”右边的数指定的。左移后溢出的高位丢弃,不足的低位补0。

【“右移”运算符>>】

“右移”运算可以说是“左移”运算的“反运算”,但也与“左移”运算有一定区别。在做“右移”运算时需要注意符号位的问题。对于无符号数,右移时左边高位移入0。对

363于有符号数,情况却复杂得多。如果原来的符号位是0,则左边高位移入0,这是同无符号数右移相同的;如果原来的符号位是 1,则左边高位的移入情况却是不确定的,需要视计算机系统而定。有的系统移入的是0,而有的系统移入的则是1。移入0的称为“逻辑右移”,即简单右移;移入1的则称为“算术右移”。

【位段】

位段就是以位为单位定义长度的结构体类型中的成员。位段的定义与结构体类型变量的定义类似,其形式为:

struct 位段结构名

{

位段列表

};

其中位段列表的形式为:

类型说明符 位段名:位段长度

例如下面的位段定义:

struct packed-data

{

int a:2;

int b:3;

int c:4;

};

位段变量的说明与结构体变量说明的方式相同。可以采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:

struct packed-data

{

int a:2;

int b:3;

int c:4;

}data;

这里说明了data为packed-data变量,共占两个字节。其中位段a占2位,位段b占3位,位段c占4位。

补充与扩展

【对位运算的补充】

如果读者学习过汇编语言,那么将对这一部分会有更好的理解。C语言有了位运算的

364功能,就使得C语言能像汇编语言那样可以用来编写系统软件。

计算机系统的内存储器是由许多的“字节”(byte)单元组成。每一个字节由8个二进制位(bit)位组成,最右边的一位称为“最低有效位”或者“最低位”,最左面的一位称为“最高有效位”或者“最高位”。并且每一个字节有一个地址,也就是说,计算机系统的最小管理单元就是字节。若干个字节组成一个称为“字”(word)的存储单元,每一个存储单元存放一个数据或者一条指令。

在一般的微机中以4个字节存放一个实数,以2个字节存放一个整数。数值在计算机内是以二进制形式存储的,最高位是数值的符号位。为了表示一个数值,可以有三种方法:原码、反码和补码。

【“按位与”运算符 & 的补充说明】

“按位与”运算符"&"是双目运算符。其功能在上面已经说明,即参与运算的两个数各对应的二进位作“相与”运算。只有对应的两个二进位均为1时,结果位才为1,否则为0。参与运算的数是以补码的方式出现的。

例如9&5的计算过程如下:

先将9和5分别以补码的形式表示出来,如下:

00001001 (9的二进制补码)

00000101 (5的二进制补码)

再在此基础上进行按位与运算,得到如下结果(请注意对应位是如何进行按位与运算的),

00000001

这是十进制数“1”的二进制补码,从而可以得出结论:

9&5=1

按位与运算有一些很特殊的用途。首先,按位与运算通常用来对某些位清0。例如,如果想将某一内存单元清0,即将该内存单元的全部二进位置0,那么只要找到这样的一个数,使其与原来的数进行按位与运算就可以实现这种清0的功能。所要找的这个数应该满足的条件是:跟原来的数相比较,原来的数中为1的位,新数中为0。

例如,如果要把数a的高八位清0,但保留其低八位,那么可以进行如下运算:

a&255

这里选择的新数为255,它的二进制数为0000000011111111。高八位均为0,低八位均为1,这样不管数a的高八位为何值,经过按位与运算后,各位均变成了0;不管数a的低八位为何值,经过按位与运算后,仍旧保持其原来的数值。9&5的程序编写如下所示。

程序13-1:P13-1.c

main()

{

int a=9,b=5,c;

365 c=a&b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

}

其次,按位与运算的另外一个作用是可以将一个单元的某些位保留下来。如果想将某一个数的某些位保留下来,只要让其与一个新数进行按位与运算,这个新数所要满足的条件是:此数与原来的数相比,其对应位为1即可。

例如想将01010100(十进制数84)这个数的左面第3、4、5、7、8这些位保留下来,可以选择新数为00111011

(十进制数59)。程序编写如下所示。

程序13-2:P13-2.c

main()

{

int a=84,b=59,c;

c=a&b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

}

按位与运算的第三个用途是获取一个数中的某些指定位。比如在第一个用途中的获取数a的高八位或者低八位,等等。

【“按位或”运算符¦的补充说明】

按位或运算符“¦”是双目运算符。其功能是参与运算的两数各对应的二进位相或。只要对应的两个二进位有一个为1时,结果位就为1。同按位与运算符一样,参与运算的两个数也是均以补码的形式出现。

例如9¦5的计算过程如下所示。

先将9和5分别以补码的形式表示出来,如下:

00001001 (9的二进制补码)

00000101 (5的二进制补码)

再在此基础上进行按位或运算,得到如下结果(请注意对应位是如何进行按位或运算的),

00001101

这是十进制数“13”的二进制补码,从而可以得出结论:

9¦5=13

程序编写如下所示。

程序13-3:P13-3.c

main()

{

int a=9,b=5,c;

c=a¦b;

printf("a=%d\nb=%d\nc=%d\n",a,b,c);

366 }

按位或运算经常用来对一个数据的某些位设为定值1。例如如果想将一个整数a的高八位保留原样,低八位则全部置为1,则可以进行下面的运算:

a¦255; (255是十进制数)

【“按位异或”运算符Λ的补充说明】

按位异或运算符“Λ”是双目运算符。其功能是参与运算的两个数各相应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算的两个数仍以补码的形式出现。

例如9Λ5可以用下面的程序来实现。

程序13-4:P13-4.c

main()

{

int a=9;

a=aΛ5;

printf("a=%d\n",a);

}

异或运算符有多方面的应用。

首先,它可以使得特定位翻转,即如果特定位是0则变为1,如果是1则变为0。因为一个数中值为1的位与1进行异或运算得0,该数中位值0与1进行异或运算的结果为1,所以要使该数的哪几位翻转,就将其与同位置置为1的数进行异或运算即可。例如,要将01111010的低四位翻转,可以将它与00001111进行异或运算,这样结果值的低四位正好是原来数的低四位的翻转。

其次,如果原来的数想保留某几位的值,可以将其与这些位值为 0的数进行异或运算。这是因为原来数中的1与0进行异或运算得1,0与0进行异或运算得0,所以可以保留原数。

按位异或运算的另外一个作用是可以不用临时变量而交换两个值。例如如果要交换变量a和b的值,可以编写程序来实现,如下所示。

程序13-5:P13-5.c

main()

{

int a=3,b=4;

a=aΛb;

b=bΛa;

a=aΛb;

printf("a=%d\nb=%d\n",a,b);

}

程序分析

上面用来实现交换的三个语句:

367 a=aΛb;

b=bΛa;

a=aΛb;

等价于下面两步,第一步是:

b=bΛ(aΛb)=bΛaΛb=aΛbΛb=aΛ0=a

这一步相当于前面的两个赋值语句:

a=aΛb;

b=bΛa;

bΛb的结果为0,这是因为同一个数与本身进行异或运算其结果必定为0。这样b就得到了a的值。在这一步中除了第一个b值以外,其余的a和b均是指原来的a和b。

第二步是:

a=aΛb=(aΛb)(bΛaΛb)=aΛbΛbΛaΛb=aΛaΛbΛbΛb=b

这样就把b的值赋给了a。

【“取反”运算符~的补充说明】

取反运算符“~”为单目运算符,具有右结合性。其功能是对参与运算的数的各二进位按位取反,即将0变为1,1变为0。例如~9的运算为:

~(0000000000001001)

结果为:

11xxxxxxxxxxxx0

需要注意的是,取反运算符的优先级别比算术运算符、关系运算符、逻辑运算符和其他运算符都高。下面说明取反运算符的应用。

如果想使一个数的最低一位变为0,可以使用下面的语句:

a=a&~1;

当然也可以使用下面的语句:

a=a&65534;

这里的65534即二进制数11xxxxxxxxxxxx0。但是这样做就降低了程序的可移植性,因为上面的语句只适用于16位的计算机系统,如果将其移植到32位的计算机系统,必须对其做较大的改进,否则就会出错。如果使用取反运算符就不会出现这种情况。这样就使得程序的可移植性增强。

【“左移”运算符<<的补充说明】

左移运算符“<<”是双目运算符。其功能是把“<<”左边的运算数的各二进位全部左

368移若干位,由“<<”右边的数指定移动的位数,高位左移后溢出,舍弃不起作用,低位则补0。例如:

a=a<<4;

指把a的各二进位向左移动4位,右补0。如果:

a=00000011; (十进制3)

左移4位后为:

a=00110000; (十进制48)。

左移一位相当于原来的数乘以2,左移两位就相当于原来的数乘以4。在上面的例子中,左移4位就相当于乘以2的四次方,即乘以16,显然上面的结果是正确的。

由于左移运算要比乘法运算快许多,所以在有些C编译程序中自动将乘2的运算用左移一位来实现。当然,上面的结论是有条件的,即原来的数在左移后被溢出而舍弃的高位中不包含1,否则上面的结论失效。例如对于:

a=64;

用二进制补码可以表示为:

a=01000000;

左移一位后变为10000000,它的十进制值为128。如果左移2位,可以看出高位将有1溢出,得到的结果是00000000,这是十进制的0,显然与上面的结论不符合。

【“右移”运算符>>的补充说明】

右移运算符“>>”是双目运算符。其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。

例如设:

a=15;

a>>2;

表示把000001111右移为00000011(十进制3)。

同左移运算符相同的是,右移一位即相当于除以 2。与左移运算不同的是,右移需要注意符号位的问题。对于有符号数,在右移时,符号位将随同移动。当为正数时,最高位补0,而为负数时,符号位为1,这时最高位是补0还是补1则取决于编译系统的规定。TurboC和很多系统规定为补1。看下面的例子,分析其中右移运算的使用。

程序13-6:P13-6.c

main()

{

unsigned a,b;

printf("input a number: ");

scanf("%d",&a);

b=a>>5;

369 b=b&15;

printf("a=%d\tb=%d\n",a,b);

}

程序分析

在上面这个例子中已经设定变量a和b为无符号数,这样一来,在做右移运算时左边高位始终移入的是0,这种移入情况就比有符号数的移入情况简单得多。为理解有符号数的移入情况,下面再看一例。

程序13-7:P13-7.c

main()

{

char a='a',b='b';

int p,c,d;

p=a;

p=(p<<8)|b;

d=p&0xff;

c=(p&0xff00)>>8;

printf("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);

}

程序分析

在上面的这个程序中,定义p、c和d均为整型数据,并且为p赋值字母a的ASCII值,即p的值为97(二进制数为01100001)。然后将p左移8位得到值为0,与b进行按位或运算得到的是b的值;将p与十六进制的0xff进行按位与运算,结果赋给变量d;将p与十六进制的0xff00进行按位与运算,再右移8位,将值赋给变量c。

这里需要注意的一个问题是:如果两个数据长度不同,当它们进行位运算时,系统就会将二者按右端对齐。比如本题中的p为8位,而0xff00则是16位,那么在做运算:

c=(p&0xff00)>>8;

时就会将p变成16位的,即在p的左侧补足。如果p为正数,那么就在其左侧补足0;如果p为负数,就在其左侧补足1;如果p为无符号整型数,则其左侧补足0。

另外,位运算符和赋值运算符结合可以组成扩展的赋值运算符。例如:

&=, ¦=, Λ=, ~=, <<=, >>=

【位运算的综合举例】

如果要对一个int型数据循环移位,要求把a进行右循环移n位,即将原来左边的16-n位右移n位,原来右边的n位移到最左边n位,编写程序如下所示。

程序13-8:P13-8.c

#include"stdio.h"

main()

{

unsigned x,y,z;

370 int n;

char ch;

printf("Left(L)/Right(R):");

ch=getchar();

printf("x,n=");

scanf("%o,%d",&x,&n);

if(toupper(ch)=='R')

{

y=x<<16-n;z=x>>n;z=z|y;

}

else

{

y=x>>16-n;z=x<<n;z=z|y;

}

printf("x=%o\nmove..=%o\n",x,z);

}

程序分析

在这个程序中,首先引入ch变量来控制左或者右的循环移位,然后输入l或者L来进行左循环移位,输入r或者R进行右循环移位。在左循环移位中,先将x的右端n位放到y中的高n位中,再将x向右移n位,其左端n位补0放到变量z中,最后y与z进行按位或运算。同样的道理可以理解右循环移位。

【位段的补充说明】

有时一些信息在存储时,并不需要占用一个完整的字节,而只需占用一个或者几个二进制位就可以了。为了节省存储空间,并且使程序的处理简便,C语言提供了一种特殊的数据结构,称为“位段”或者“位域”。所谓“位段”就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位段来表示。

对位段的定义和引用补充说明如下:

首先,各个位段可以不占满一个字节,并且一个位段必须存储在同一个字节中,不能跨两个字节。如果一个字节所剩的空间不够存放另一个位段时,应该从下一单元起存放该位段。当然也可以有目的地使某位段从下一单元开始。例如:

struct byteseg

{

unsigned a:4;

unsigned :0;

unsigned b:4;

unsigned c:4;

};

在这个位段的定义中,a占有第一个字节的前4位,该字节的后4位填0表示不使用,它的作用也可以说成是使下一个位段从下一个存储单元开始存放。b从第二字节开始,占用4位,

371c占用4位。这样一来,a单独放在一个存储单元中,b和c共同存放在一个存储单元中。

其次,一个位段必须存储在同一个存储单元中,即位段不能跨两个单元存储。如果一个单元空间不能容纳下一个位段,那么该存储空间不能用,而必须从下一个单元起存放该位段。

由于位段不允许跨两个字节存放,因此位段的长度不能大于一个字节的长度,也就是说不能超过8位二进位。还有一点就是不能定义位段数组。

位段可以是无名位段,这时它只用来作填充或者调整位置。无名位段是不能使用的。例如:

struct bytes

{

int a:1;

int :2;

int b:3;

int c:2;

};

在上面的定义中,位段a后面出现了无名位段,这个位段是不能使用的。

从以上分析可以看出,位段在本质上就是一种结构体类型,不过它的成员是按二进位分配的。

【位段的引用】

位段的使用方法和结构体变量的使用方法相同,它的一般引用形式是:

位段变量名?位段名

位段可以用整型格式符输出,也可以用%u,%o,%x等格式符输出。下面通过一个实例来说明位段的引用方法。

程序13-9:P13-9.c

main()

{

struct byteseg

{

unsigned a:1;

unsigned b:3;

unsigned c:4;

}bit,*pbit;

bit.a=1;

bit.b=7;

bit.c=15;

printf("%d,%d,%d\n",bit.a,bit.b,bit.c);

pbit=&bit;

pbit->a=0;

pbit->b&=3;

pbit->c|=1;

printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);

372 }

程序分析

在程序中首先定义了位段结构byteseg,它包括三个位段a,b和c。接着说明了byteseg类型的变量bit和指向byteseg类型的指针变量pbit。注意,位段也是可以使用指针的。

程序接下来分别为三个位段进行赋值,赋值不能超过该位段的允许范围。例如,不能给位段a赋值2或者2以上的数值,因为位段a的长度只有一位,所以只能为其赋值0或者1。

程序的第一个输出语句是以整型格式符输出三个位段的内容。

已经说过,位段也可以使用指针。位段指针的定义和结构体变量指针的定义方法相同,引用方法也相同。在本程序中,位段变量bit的地址送给指针变量pbit,这样指针变量pbit就指向了位段变量bit。下面就是用指针方式给位段a重新赋值为0,使用扩展的赋值运算符 "&=" 和 "| =" 对b和c进行赋值。这两行语句相当于:

pbit->b=pbit->b&3;

pbit->c=pbit->c|3;

在程序的第二个输出语句中,使用指针方式输出了这三个位段的值。

需要补充的一点是位段可以在数值表达式中引用,并且它将会被系统自动地转换成整型数。

对位运算的总结:

位运算是C语言的一种特殊运算功能,也是C语言区别于其他高级语言的重

要一点。位运算是以二进位为单位进行运算的。与其他运算不同的是,位运算只

有逻辑运算和移位运算两类。

位运算符可以与赋值运算符结合组成扩展的赋值运算符。

利用位运算可以完成汇编语言的某些功能,例如置位、移位、清0等。还可

进行数据的压缩存储和并行运算,等等。

位段在本质上是结构体类型,只不过它的成员是按二进制位分配内存的。其

定义、说明及使用的方式都与结构体变量相同。

典 型 例 题

【例题13-1 移位运算】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序的输出结果是( )。

程序13-10:P13-10.c

373 main()

{char x=040;

printf("%0\n",x<<1);

}

B)100 B)80 C)64 D)32

解题方法

本题是关于位运算中移位的问题。由于位运算的运算符只有六个,并且它们的运算跟数理逻辑中的逻辑关系非常相近,所以位运算比较容易掌握。本题中需要注意的一点就是不要将左移运算符和右移运算符搞混。

知识点分析

程序开始定义了一个字符变量x并且为其赋初值为八进制的040,换算成二进制就是00100000。接下来的输出语句中将 x 进行左移一位。得到的结果用二进制表示就是01000000,换算成八进制就是0100。

正确答案

A

难度提示

初级。本题是关于位运算的一道简单题。在作左移运算时并没有出现什么异常的情况。只要读者对左移运算这一知识点有所了解就不难回答这道题目。

【例题13-2 位运算】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

整型变量x和y的值相等,且为非0值,则以下选项中,结果为0的表达式是( )。

B)x||y B)x|y C)x&y D)x^y

解题方法

本题是关于位运算和逻辑运算的一道综合题目。题目中涉及到了位运算中的按位或、按位与和按位异或运算符,还涉及到了逻辑或运算符。考查的是读者能否分清楚位运算和逻辑运算之间的区别。

知识点分析

逻辑运算符“||”和位运算符“|”在逻辑关系上是相同的,即参与运算的两个操作数只要其中一个为1,则结果就是1。但这两个运算符在用法上却是不同的。

逻辑运算符“||”要求其左右两边参加运算的操作数是普通的整型、字符型等类型的数据或者是表达式,不能是“位”。即逻辑运算符的作用单元是字节,而不是位。

位运算符“|”要求其两边必须是以“位”为单位的数据,可以是整型或者字符型的数据,但不能是其他普通类型的数据。并且在处理这些数据时是以“位”为单位进

374行的。

对于选项A,如果x和y是正的整数的话,因为两者均为真,所以结果为真,即结果为1。要使结果为0,须使x和y为负的整数或者为0。对于选项B,因为x和y的值相等,且为非0值,所以按位或的结果仍旧是原来的x或y的值。对于选项C,同选项B一样,因为x和y的值相等,且为非0值,所以按位与的结果仍旧是原来的x或y的值。对于选项D,因为x和y的值相等,且为非0值,所以按位异或的结果是0。

正确答案

D

难度提示

中级。本题考查了逻辑运算和位运算的区别,并且考查了位运算中运算符的一些特殊用法。考查的知识都是比较简单的,读者应该能够较容易掌握。

【例题13-3 按位与运算】

(全国计算机等级考试二级笔试试卷19xx年9月)

题干

下面的语句:

printf("%d\n",12&012);

的输出结果是( )。

B)12 B)8 C)6 D)012

解题方法

这道题目是关于按位与位运算符的。题目比较简单。

知识点分析

按位与运算符可以作用于整型或者字符型的数据。比如在本题中就是作用于整型数据。其中“12”是十进制整型数据,转换成二进制是00001100;“012”则是八进制的整型数据,转换成二进制则是00001010。这两者进行按位与运算后,其结果为00001000,转换成十进制为8。

正确答案

B

难度提示

初级。本题考查的知识点非常简单,而且分析起来也不复杂,所以它的难度不大。【例题13-4 右移运算】

375 题干

假设:

int b=2;

则表达式(b>>2)/(b>>1)的值是( )。

B)0 B)2 C)4 D)8

解题方法

本题是关于位运算中右移运算的。考查的是读者对右移运算的掌握情况。为正确解答本题,读者不但需要掌握右移运算的基本概念,还要掌握右移运算的一些特殊情况。

知识点分析

题目首先定义了一个整型变量b并且为其赋初值2。在下面的表达式中,对b进行了右移位运算。对于无符

号数,右移时左边高位补足0,移到右边的低位舍弃。

在进行右移运算时需要考虑符号位的问题。对于无符号数,右移时左边高位移入 0。对于有符号数,如果原来的符号位为0,即该数为正,则左边高位也是移入0;如果原来的符号位为1,即该数为负,则左边高位移入0还是1取决于计算机系统。移入0的称为“逻辑右移”,移入1的称为“算术右移”。在Turbo C和其他一些C编译系统采用的是算术右移,即对有符号数右移时,左边高位移入的是1。

十进制 2 转换成二进制是 00000010,右移两位变成了 00000000,右移一位变成了00000001,所以结果就成了0/1。

还需要注意的一点是除法运算符的结合性是自左向右的,所以本题不难作答。

正确答案

A

难度提示

中级。关于位运算符说明如下,因为右移运算符对有符号数进行运算时,往往出现不确定性,这一点就限制了它应用的广泛性。除非题目中明确说明作右移运算时采用的是算术右移或者逻辑右移,否则题目可能出现无解的情况。

本 章 练 习

【填空题】

1.在位运算符中除了取反运算符“~”以外,其余均为______运算符,即要求运算符的两侧各有一个运算量。并且运算量只能是______或者______数据。

2.位段就是以位为单位定义长度的______类型中的成员,就是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的______。

376【综合题】

1.编写一个程序,完成下面的功能:截取一个整数a从右端开始的4~7位,并将其保存到整型变量b中。

2.总结一下可以用哪些方法解决下面的问题:(1)将一个单元进行清0;(2)截取一个数据中的某些位;(3)使数据中的某些特定位翻转;(4)交换数值;(5)循环移位。

3.指出下列语句中的错误:

struct packed-data

{ unsigned a:2

unsigned b:9

unsigned :2

unsigned c:5}data;

data.a=8;

printf("%d\n",data.a);

4.设计一个函数实现如下功能:从键盘上输入任意一个占有两个字节的整数,输出这个数的原码和补码。

5.比较一下逻辑运算符和位运算符的区别是什么,然后将 C语言中涉及到的运算符作一下总结,以表格的形式列出。

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

377 第 14 章 文 件 操 作

考纲要求:

1.只要求缓冲文件系统(即高级磁盘I/O系统),对非缓冲文件系统(即低

级磁盘I/O系统)不要求。

2.文件类型指针(FILE类型指针)。

3.文件的打开与关闭(fopen,fclose)。

4.文件的读写(fputc,fgetc,fputs,fread,fwrite,fprintf,fscanf函数)。

5.文件的定位(rewind,fseek函数)。

知识点讲析

【文件】

对于“文件”读者应该不会感到陌生,因为在前面的章节中已经出现过文件的概念,比如源程序文件、头文件(库文件)、目标文件、可执行文件,等等。所谓“文件”一般是指:存储在外部介质(例如磁盘等)上的一组相关数据的有序集合。这些数据的集合有一个名称,即文件名。不管是Windows操作系统还是UNIX操作系统,均是以文件为单位管理数据的。也就是说,如果想找到存储在磁盘上的数据,需要先按照文件名找到数据所在的文件,然后才能够从文件中读取所要的数据。同样道理,如果想将某些数据保存到磁盘里,需要先建立一个文件,然后才能向文件中输入数据。

文件通常是驻留在外部存储介质上的,当需要时才调入计算机系统的内存,使用完成后将结果再存放回原来的文件。

C语言把文件看做是一个字符(字节)的序列,即一个字符(字节)接着一个字符(字节)的顺序存放。根据数据的组织形式,或者说根据文件的编码方式,文件可分为 ASCII文件(又可以称为文本文件)和二进制文件。ASCII文件又称为文本(text)文件,这种文件在磁盘中存放时每个字节存放一个ASCII码,代表一个字符。ASCII文件可在屏幕上按字符显示,例如C语言的源程序文件就是ASCII文件,用DOS命令TYPE可以显示文件的内容。由于ASCII文件是按字符显示,因此能读懂文件内容。

二进制文件是把内存中的数据按其在内存中的存储形式原样输出到磁盘上存放。即是按照二进制的编码方式来存放文件的。二进制文件虽然也可以在屏幕上显示,但其内容却无法读懂。

378【缓冲文件系统】

在过去的 C语言的版本中存在两种处理文件的方法:一种就是“缓冲文件系统”,另外一种就是“非缓冲文件系统”。所谓的“缓冲文件系统”是指:系统自动地在内存区为每一个正在使用的文件名开辟一个缓冲区。这样一来,从内存向磁盘输出数据时必须先送到内存中的缓冲区,等到缓冲区装满后再一起送到磁盘去。如果是从磁盘向内存读入数据,那么就必须一次将一批数据从磁盘文件输入到内存缓冲区,然后再从缓冲区逐个地将数据送到程序的数据区。

缓冲文件系统又可以称为“高级(高层)磁盘I/O系统”,用缓冲文件系统进行的输入输出又称为高级(高层)磁盘输入输出(高层I/O)。

【文件类型指针】

在C语言中用一个指针变量指向一个文件,这个指针称为“文件指针”。文件指针是缓冲文件系统中非常重要和关键的一个概念。每一个被使用的文件都在内存中开辟一个区域用来存放该文件的相关信息,比如文件名、文件属性以及文件路径,等等。这些信息是保存在一个结构体类型的变量中的。该结构体类型由系统定义,其名为FILE。

通过文件指针就可对它所指的文件进行各种操作。定义说明文件指针的一般形式为:

FILE *指针变量标识符;

其中FILE必须为大写,因为这是由系统预先定义了的一个结构体类型。在编写源程序时不必关心FILE结构的细节。例如:

FILE *fp;

表示fp是指向FILE类型结构体的指针变量,可以使fp指向某一个文件的结构体变量,通过fp就可以找到这个存放文件信息的结构体变量,然后按结构体变量提供的信息即可找到该文件,从而完成对文件的操作。习惯上也笼统地把fp称为指向一个文件的指针。

如果有n个文件,一般应设n个指针变量,使它们分别指向n个文件,从而实现对文件的访问。还可以定义FILE类型的数组,如下所示:

FILE –efile[-MAXFILE];

结构体数组-efile[]有-MAXFILE个元素,?MAXFILE是一个符号常量,它的值是可以使用的文件的最大数目。

【文件的打开】

在C语言中,文件操作都是由系统提供的库函数来完成的。同其他高级语言一样,对一个文件进行读写操作之前必须先打开该文件。

379 ANSIC 规定了标准的输入输出函数库,同样也规定了实现打开文件的函数是 fopen()函数。fopen()函数调用的一般形式为:

FILE *文件指针名;

文件指针名=fopen(文件名,使用文件方式);

例如:

FILE *fp;

fp=fopen("file1","r");

这里打开的是一个文件名为file1的文件,并且说明对文件的操作方式是“只读”。fopen()函数带回指向file1文件的指针并将其赋给指针变量fp,即使得fp指向file1文件。【文件的关闭】

在使用完文件之后必须将文件关闭,这样才能够保证数据安全保存到文件中,以免文件数据丢失或者损坏。ANSI C规定了用来实现关闭文件的函数是fclose()函数。该函数的一般调用形式是:

fclose(文件指针);

例如:

fclose(fp);

先前用fopen()函数打开文件时所带回的文件指针赋给fp,现在将其关闭。“关闭文件”即使得文件指针变量不再指向该文件,也就是说使文件指针与文件脱离。正常完成关闭文件操作时,fclose()函数返回值为0。如果返回值非0则表示关闭文件时有错误发生,这时可以用ferror函数来进行测试。

【文件的读写】

在打开文件之后就可以对其进行读和写的操作了。对文件的读和写是最常用的操作。ANSI C规定了一系列的读写函数以用来对文件进行读写操作,它们包括:字符读写函数、字符串读写函数、数据块读写函数和格式化读写函数。

字符读写函数包括fgetc函数和fputc函数;字符串读写函数包括fgets函数和fputs函数;数据块读写函数包括freed函数和fwrite函数;格式化读写函数包括fscanf函数和fprinf函数。【fgetc函数】

功能

从fp所指定的文件中取得下一个字符。

380 语法

int fgetc(fp)

FILE *fp;

返回值

返回所得到的字符,如果读入出错,则返回EOF。

fgetc函数的功能是从指定的文件中读取一个字符,函数调用的形式为:

字符变量=fgetc(文件指针);

例如:

ch=fgetc(fp);

这里的fp为文件型指针变量,ch为字符变量。语句的意义就是带回一个字符赋给字符变量ch。

【fputc函数】

功能

将字符ch输出到fp指向的文件中。

语法

int fputc(ch,fp)

char ch;

FILE *fp;

返回值

成功则返回该字符ch,否则返回EOF。

fputc函数的功能是把一个字符写入到指定的文件中,函数调用的形式为:

fputc(字符量,文件指针);

其中待写入的字符量可以是字符常量或字符变量,例如:

fputc('a',fp);

其意义是把字符常量a写入到指针变量fp所指向的文件中。

【fputs函数】

功能

将str指向的字符串输出到fp所指向的文件中。

语法

int fputs(str,fp)

381 char *str;

FILE *fp;

返回值

成功则返回0,如果出错则返回非0。

fputs函数的功能是向指定的文件写入一个字符串,其调用的一般形式为:

fputs(字符串,文件指针);

其中字符串可以是字符串常量,也可以是字符数组名,还可以是字符型指针变量,例如:

fputs("abcd",fp);

其意义是把字符串“abcd”写入fp所指定的文件之中。

【fread函数】

功能

从fp所指定的文件中读取长度为size的n个数据项,保存到pt所指向的内存区。

语法

int fread(pt,size,n,fp)

char *pt;

unsigned size;

unsigned n;

FILE *fp;

返回值

返回所读的数据项的个数,如果遇到文件结束或者出错则返回0。

例如:

fread(fa,4,5,fp);

其意义是从fp所指向的文件中每次读取4个字节(即一个实数)送入实数组fa中,连续读5次,即读5个实数到fa中。

【fwrite函数】

功能

把ptr所指向的n*size个字节输出到fp所指向的文件中。

语法

int fwrite(ptr,size,n,fp)

char *ptr;

unsigned size;

382 unsigned n;

FILE *fp;

返回值

写到fp文件中的数据项的个数。

【fprintf函数】

功能

将args的值以format指定的格式输出到fp所指向的文件中。

语法

int fprintf(fp,format,args,...)

FILE *fp;

char format;

返回值

返回实际输出的字符数。

【fscanf函数】

功能

从fp指定的文件中按format给定的格式将输入数据送到args所指向的内存单元中,其中args是指针。

语法

int fscanf(fp,format,args,...)

FILE *fp;

char format;

返回值

返回已经输入的数据个数。

【文件的定位】

经常会出现这样的问题:需要读取的数据并不是文件的全部也不是从文件的开头顺序读写,而是仅读取文件中的某一部分,这种读写方式可以称之为随机读写。这时就要用到文件内部的位置指针来解决这个问题。

在每一个文件的内部都有这样的一个位置指针指向当前读写的位置。在顺序读写一个文件时,位置指针指向当前所要读的字符,当读完这个字符后,位置指针就自动指向下一个字符的位置。而要实现随机读写,其关键就是要按要求移动这个位置指针,使其强制指向指定的位置,这种技术称为文件的定位。

383C语言中实现文件定位的函数有rewind函数和fseek函数。

384【rewind函数】

功能

将fp所指定的文件中的位置指针置于文件的开头位置,并且清除文件结束标志和错误标志。

语法

void rewind(fp)

FILE *fp;

返回值

该函数没有返回值。

【fseek函数】

功能

将fp所指向的文件的位置指针移到以base所指出的位置为基准,以offset为位移量的位置。

语法

int fseek(fp,offset,base)

FILE *fp;

long offset;

int base;

返回值

返回当前的位置,否则返回-1。

补充与扩展

【文件的定义】

文件的分类。从不同的角度可对文件作不同的分类。从用户的角度看,文件可分为普通文件和设备文件两种。普通文件是指驻留在磁盘或其他外部介质上的一个有序数据集,设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看做是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出数据。例如前面经常使用的printf、putchar函数就是这类输出。键盘通常被指定为标准的输入文件,从键盘上输入就意味着从标准输入文件上输入数据。Scanf、getchar函数就属于这类输入。

385 按照文件编码的方式,可以分成ASCII文件和二进制文件。这两种文件格式各有优缺点。用ASCII码形式输出与字符一一对应,便于对字符进行逐个处理,也便于输出字符。但是这种形式的文件较之二进制码形式的文件一般占用较多的存储空间而且花费较多的转换时间。用二进制形式的文件就可以节省存储空间和转换时间,但其缺点也是显然的,就是不能直接输出字符形式。

C语言系统在处理这些文件时,并不区分类型,而是将它们都看成是字符流,按字节进行处理。输入输出的字符流的开始和结束仅受程序控制而不受物理符号(如回车换行符)的控制。因此也把这种文件称做“流式文件”。

前面讲述了缓冲文件系统,还有一种是非缓冲文件系统。所谓的非缓冲文件系统就是指系统不自动开辟确定大小的缓冲区,而是由程序为每一个文件设定缓冲区。在UNIX系统下,用缓冲文件系统来处理文本文件,用非缓冲文件系统来处理二进制文件。非缓冲文件系统又称为低级磁盘I/O系统,用非缓冲文件系统进行的输入输出称为低级(低层)输入输出系统。

新的ANSI C标准已经不采用非缓冲文件系统了,所以这里省略了对它的讲述。如果读者需要了解这方面的知识,可以参考有关的专业书籍。

【文件类型指针】

前面已经说明,C语言系统定义了结构体类型FILE,可以在头文件stdio.h中查看它的定义。因为头文件 stdio.h 是一个 ASCII 文件,利用任何一种文本文件编辑工具(例如Windows的记事本)就可以对其进行查看和编辑。在这个文件中可以看到下面的定义(TC2.0版本中的定义):

/* Definition of the control structure for streams */

typedef struct

{

short level; /* fill/empty level of buffer */

unsigned flags; /* File status flags */

char fd; /* File descriptor */

unsigned char hold; /* Ungetc char if no buffer */

short bsize; /* Buffer size */

unsigned char *buffer; /* Data transfer buffer */

unsigned char *curp; /* Current active pointer */

unsigned istemp; /* Temporary file indicator */

short token; /* Used for validity checking */

}FILE; /* This is the FILE object */

【fopen函数】

功能

以mode指定的方式打开名为filename的文件。

386 语法

FILE *fopen(filename,mode)

char *filename,*mode;

返回值

成功则返回一个文件指针,即文件信息区的起始地址,否则返回0。

打开一个文件就要通知编译系统需要打开的文件名是什么,也就是要准备访问的文件的名称,然后需要指明以何种方式使用文件,还有一点就是要指出让哪一个指针变量指向被打开的文件。

文件的使用方式可以分成文本文件使用方式和二进制文件使用方式两大类。文本文件使用方式有“r”、“w”、“a”、“r+”、“w+”、“a+”六种方式。

用“r(read)”方式打开文件只能用来向计算机内存读入数据而不能用来向该文件输出数据。并且该文件必须是已经存在的文件,即不能打开一个并不存在的文件,否则程序会显示出错信息。

用“w(write)”方式打开的文件只能用来向该文件中输出数据而不能用来向计算机内存读入数据。该方式可以打开一个并不存在的文件。如果打开的文件是原来并不存在的,则在打开时以指定的文件名新建立一个文件,如果打开的文件已经存在,则会将该文件删去,然后重新建立一个新文件。

用“a(append)”方式可以打开一个已经存在的文件。打开时,文件位置指针移到文件末尾,这样就可以向文件末尾添加新的数据而不损坏原来的数据。如果要打开的文件原来并不存在,将会得到出错信息。

用“r+”、“w+”、“a+”方式打开的文件既可以对其进行读操作,也可以向其输入数据。其中用“r+”方式时该文件必须已经存在;用“w+”方式时将新建一个文件,先向该文件输入数据,然后可以将文件中的数据读出;用“a+”方式打开文件后,位置指针移到文件的末尾,原来的文件仍然存在,可以对文件进行读操作或者添加新的数据。

打开二进制文件与此相同,只不过要在上面方式的基础上加一个“b”字符,例如“rb”、“rb+”等。

把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间。对二进制文件的读写则不存在这种转换。

在打开一个文件时,如果不能实现“打开”的任务,fopen函数将返回一个空指针值NULL。出错的原因是多方面的,比如用“r”方式打开并不存在的文件或者磁盘出错,等等。在程序中可以用这一信息来判别是否完成打开文件的任务,并作出相应的处理。常用以下程序段来打开文件:

if((fp=fopen("tcfile.txt","rb")==NULL)

{

printf("\nError on open tcfile.txt!");

getch();

exit(1);

}

387 程序的意义是,如果返回的指针为空,就表示不能打开tcfile.txt文件,这时就给出提示信息“Error on open tcfile.txt!”,getch()的功能是从键盘输入一个字符,但不在屏幕上显示。在这里它的作用是等待用户从键盘敲任意的一个键时,程序才继续执行,这样用户就可以利用这个等待时间来阅读出错提示信息。敲任意键后执行exit(1)语句,退出程序。

程序开始运行时系统会自动打开三个标准文件:标准输入文件(键盘),标准输出文件(显示器),标准出错输出(出错信息)。并且系统自动定义了三个文件指针 stdin、stdout和stderr,它们分别指向终端输入、终端输出和标准出错输出。

【fclose函数】

功能

关闭fp所指向的文件,释放文件缓冲区。

语法

int fclose(fp)

FILE *fp;

返回值

如果有错则返回非0值,否则返回0。

文件关闭后就不能再通过原来的文件指针来对与其相连的文件进行读写操作,除非使该指针变量重新指向该文件。

如果程序结束后不关闭文件的话将会使数据丢失,所以应该养成在程序终止之前关闭所有正在使用的文件的习惯。用fclose函数关闭文件时,先把缓冲区中的数据输出到磁盘文件后才释放文件指针变量,所以能够保证数据全部保存到磁盘文件中而不会发生丢失的情况。

【fgetc函数说明】

fgetc函数带回一个字符,如果在执行fgetc函数读字符时遇到文件结束符,函数会返回一个文件结束标志EOF。标志EOF是在stdio.h中定义的,其值为-1。读者在该文件中可以看到下面的语句:

/* End-of-file constant definition */

#define EOF (-1) /* End of file indicator */

EOF作为文件结束符是不可以输出的字符,不能在屏幕上显示。当程序读入的字符值等于?1时,表明读入的不再是正常的字符而是文件结束符。

下面通过程序来理解fgetc函数的使用方法。

程序14-1:P14-1.c

388 #include"stdio.h"

main()

{

FILE *fp;

char ch;

if((fp=fopen("file14-1.c","r"))==NULL)

{

printf("Cannot open the file,Press any key to exit!");

getch();

exit(1);

}

ch=fgetc(fp);

while(ch!=EOF)

{

putchar(ch);

ch=fgetc(fp);

}

fclose(fp);

}

程序分析

本程序的功能是从文件“file14-1.c”中逐个读取字符,然后在屏幕上显示出来。程序首先定义了文件指针fp,以只读方式打开文本文件“file14-1.c”,并使fp指向该文件。如果打开文件出错,那么就会给出出错提示信息并退出程序。

程序在进行读取文本时,首先读取第一个字符,然后进入循环,只要读取的字符不是文件结束标识符EOF就将该字符显示在屏幕上,然后再读取下一个字符。每读取一个字符,文件的位置指针就向后移动一个字符,当读取文件结束时,该指针指向EOF。

执行本程序将显示整个文件。

需要注意的是,在调用fgetc函数时,读取的文件必须是以只读或者读写的方式打开的。如果是以其他方式打开的文件在读取时会出现错误。

读取的字符也可以不赋给字符型变量,这时可以将语句:

ch=fgetc(fp);

写成下面的形式:

fgetc(fp);

但是这样处理的结果是读出的字符将不能保存在内存中。

应该注意的是文件指针和文件内部的位置指针并不是一回事。文件指针是指向整个文件的,它保存的是整个文件的信息,包括文件名、文件路径,等等。并且文件指针是在程序中定义说明,只要不关闭文件或者对其重新赋值,则它的值是不变的。文件内部的位置指针则是用来指示文件内部的当前读写位置,每读写一次,该指针均向后移动,它不需在程序中定义说明,而是由系统自动设置的。

【feof函数】

389 功能

检查文件是否结束。

语法

int feof(fp)

FILE *fp;

返回值

遇到文件结束符返回非0值,否则返回0。

文件结束符EOF是用来处理文本文件的,当用来处理二进制文件时会出现异常情况,因为二进制文件中是可能出现“-1”这个值的。在文本文件中由于不可能出现-1值,所以可以定义文件结束符EOF= -1,但在二进制文件中却不可能这样定义。

并且,在二进制文件中不可能找到一个像EOF这样的标志来说明文件结束。为解决这样的一个问题,ANSI C引入了feof函数来判断文件是否真的结束。

如果想顺序读入一个二进制文件中的数据,可以用下面的程序片段:

while(!feof(fp))

{

ch=fgetc(fp);

...

}

当遇到正常的字符时,!feof(fp)的值为1,这时程序读入一个字符并将其赋给变量ch,当遇到文件结束时,!feof(fp)的值变为0,结束while循环。

【fputc函数说明】

先看下面的程序,该程序是从键盘输入一行字符,然后将这些字符写入一个文件,再把该文件内容读出显示在屏幕上。

程序14-2:P14-2.c

#include"stdio.h"

main()

{ FILE *fp;

char ch;

if((fp=fopen("file14-2","w+"))==NULL)

{ printf("Cannot open file,Press any key exit!");

getch();

exit(1);

}

printf("Input a string:\n");

ch=getchar();

while(ch!='\n')

{ fputc(ch,fp);

ch=getchar();

}

390 rewind(fp);

ch=fgetc(fp);

while(ch!=EOF)

{ putchar(ch);

ch=fgetc(fp);

}

printf("\n");

fclose(fp);

}

程序分析

程序以读写的方式新建立了一个名为 file14-2的文本文件,接着从键盘输入一个字符后进入while循环,继续从键盘读入字符,当读入的字符不是回车符时就把字符写入到新建的文件中,然后继续读入下一个字符直到遇到回车符为止。

当将所有的字符都写入文件后,文件的位置指针已经指向文件末尾。如果想将文件中的内容读出,需要使得位置指针指向文件开始。这个功能是由rewind函数实现的。

需要注意的是,使用fputc函数时要求打开文件的方式是“w”、“a”、“r+”、“w+”和“a+”等。并且每写入一个字符,文件内部的位置指针就向后移动一个字符。

下面再来看一个程序。这个程序的功能是将命令行参数中的前一个文件名标志的文件,复制到后一个文件名标志的文件中,如果命令行中只有一个文件名那么就将该文件写到标准输出文件(显示器)中。

程序14-3:P14-3.c

#include"stdio.h"

main(int argc,char *argv[])

{ FILE *fp1,*fp2;

char ch;

if(argc==1)

{ printf("Have not enter file name,Press any key to exit");

getch();

exit(0);

}

if((fp1=fopen(argv[1],"r"))==NULL)

{ printf("Cannot open %s\n",argv[1]);

getch();

exit(1);

}

if(argc==2)

fp2=stdout;

else if((fp2=fopen(argv[2],"w+"))==NULL)

{ printf("Cannot open %s\n",argv[1]);

getch();

exit(1);

}

while((ch=fgetc(fp1))!=EOF)

fputc(ch,fp2);

fclose(fp1);

fclose(fp2);

391 }

程序分析

这个程序的main函数是带有参数的。程序中首先定义了两个文件指针fp1和fp2,分别指向命令行参数中给出的文件。如果命令行参数中没有给出文件名,则会给出提示信息。如果只给出一个文件名,则使fp2指向标准输出文件(即显示器)。

在上面的程序中出现了指针变量 stdout,这是由系统自动定义的文件指针,它指向终端输出。系统还自动定义了文件指针stdin和stderr,它们分别指向终端输入和标准出错输出。读者可以在头文件stdio.h中看到这些定义,如下所示:

/* Standard I/O predefined streams */

extern FILE _Cdecl _streams[];

#define stdin (&_streams[0])

#define stdout (&_streams[1])

#define stderr (&_streams[2])

【字符读写函数总结】

读者可以在头文件stdio.h中看到下面的宏定义:

#define putchar(c) fputc(c,stdout)

#define putc(ch,fp) fputc(ch,fp)

#define getc(fp) fgetc(fp)

从上面的定义可以看出来,putchar函数其实是从fputc函数派生出来的,这样做的目的就是使得书写起来方便。同样道理,为了书写方便起见,将宏定义的fputc函数和fgetc函数用putc函数和getc函数来代替。它们的本质其实是相同的。

【fgets函数】

功能

从fp所指向的文件读出一个长度为(n?1)的字符串,存入起始地址为buf的内存空间。

语法

char *fgets(buf,n,fp)

char *buf;

int n;

FILE *fp;

返回值

返回地址buf,如果遇到文件结束或者出错,则返回NULL。

为讨论fgets函数的功能,先考查下面的程序。程序的功能是从文件file14-4.c中读取一个字符串,要求该字符串包含10个字符。

392 程序14-4:P14-4.c

#include"stdio.h"

main()

{ FILE *fp;

char str[11];

if((fp=fopen("file14-4.c","r"))==NULL)

{ printf("Cannot open the file,Press any key to exit!");

getch();

exit(1);

}

fgets(str,11,fp);

printf("%s",str);

fclose(fp);

}

程序分析

因为在所有的字符均读取完之后在最后还要加一个 '\0' ,所以在定义字符数组str时,其长度取为11,即可以存放11个字节。程序开始以只读方式打开文件“file14?4.c”,并从中读出10个字符送入str数组,在数组最后加上一个 '\0' 字符。然后在屏幕上显示输出str数组。

如果在全部读取n?1个字符之前就遇到了换行符或EOF,那么读入即结束。fgets函数的返回值是str数组的首地址。

【fputs函数说明】

fputs函数的功能是向指定的文件输出一个字符串,例如:

fputs("How do you do?",fp);

把字符串"How do you do?"输出到fp所指向的文件中。该函数的第一个参数可以是字符串常量(例如本例)、字符数组名或者字符型指针变量。下面通过一个程序来详细了解该函数的用法。

程序14-5:P14-5.c

#include"stdio.h"

main()

{ FILE *fp;

char ch,st[20];

if((fp=fopen("file14-5.c","a+"))==NULL)

{ printf("Cannot open the file,Press any key to exit!");

getch();

exit(1);

}

printf("Input a string:\n");

scanf("%s",st);

fputs(st,fp);

393 rewind(fp);

ch=fgetc(fp);

while(ch!=EOF)

{ putchar(ch);

ch=fgetc(fp);

}

printf("\n");

fclose(fp);

}

程序分析

该函数的作用是新建一个文件然后追加一个字符串。程序开始以读写的方式打开一个名为file14-5.c的文件。然后输入字符串,并用fputs函数将字符串输入到文件中。程序中的rewind函数的作用是定位,让文件内部的位置指针指向文件开始。

【fread函数和fwrite函数说明】

新的ANSI C标准不再采用非缓冲文件系统,所以在缓冲文件系统中增加了fread函数和fwrite函数。这两个函数是数据块读写函数。下面通过一个具体的程序来说明这两个函数的使用方法。

程序14-6:P14-6.c

#include"stdio.h"

struct student

{ char name[10];

int num;

int age;

char addr[15];

}stua[2],stub[2],*pp,*qq;

main()

{ FILE *fp;

char ch;

int i;

pp=stua;

qq=stub;

if((fp=fopen("file14-6","wb+"))==NULL)

{ printf("Cannot open the file,press any key to exit!");

getch();

exit(1);

}

printf("\nInput data:\n");

for(i=0;i<2;i++,pp++)

scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);

pp=stua;

fwrite(pp,sizeof(struct student),2,fp);

rewind(fp);

fread(qq,sizeof(struct student),2,fp);

printf("\n\nname\tnumber age addr\n");

394 for(i=0;i<2;i++,qq++)

printf("%s\t%5d%7d%s\n",qq->name,qq->num,qq->age,qq->addr);

fclose(fp);

}

程序分析

程序的作用是从键盘输入两个学生的数据,然后写入文件中,接着用fread函数读出这两个数据将其显示在屏幕上。结构体数组stua和stub中各有两个元素,每一个元素均用来存放一个学生的数据。首先使用 for语句输入学生的数据,文件的打开方式是二进制的读写方式,并且是新建了一个二进制文件。在第二个for语句中将数据读出并且显示出来。

如果函数是以二进制形式打开,那么用fread函数和fwrite函数就可以读写任何类型的信息。因为fread函数和fwrite函数是按数据块的长度来处理输入输出的,所以这两个函数一般是用于二进制文件的输入和输出。这样一来,在字符发生转换的情况下就很可能出现与原来的假设情况不同。

【fprintf函数和fscanf函数的说明】

与以前介绍的printf函数和scanf函数类似,fprintf函数和fscanf函数是格式化读写函数。与前者的区别在于fscanf函数和fprintf函数的读写对象不是键盘和显示器这些标准输入输出设备,而是磁盘文件。前面的程序14-6可以用fscanf函数和fprintf函数改写如下所示。

程序14-7:P14-7.c

#include"stdio.h"

struct student

{ char name[10];

int num;

int age;

char addr[15];

}stua[2],stub[2],*pp,*qq;

main()

{ FILE *fp;

char ch;

int i;

pp=stua;

qq=stub;

if((fp=fopen("file14-7","wb+"))==NULL)

{ printf("Cannot open the file,Press any key to exit!");

getch();

exit(1);

}

printf("\nInput data:\n");

for(i=0;i<2;i++,pp++)

scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr);

pp=stua;

for(i=0;i<2;i++,pp++)

395 fprintf(fp,"%s %d %d %s\n",pp->name,pp->num,pp->age,pp->

addr);

rewind(fp);

for(i=0;i<2;i++,qq++)

fscanf(fp,"%s %d %d %s\n",qq->name,&qq->num,&qq->age,qq->

addr);

printf("\n\nname\tnumber age addr\n");

qq=stub;

for(i=0;i<2;i++,qq++)

printf("%s\t%5d %7d %s\n",qq->name,qq->num, qq->age, qq->addr);

fclose(fp);

}

程序分析

这里的输入输出语句均变成了用fprintf函数和fscanf函数来实现,程序的逻辑关系同程序14-6相同,不再赘述。

需要说明的一点是,用fprintf函数和fscanf函数对磁盘文件进行读写,使用方便并且容易理解,但是也有其缺点,那就是由于在输入时需要将文本形式转换成为二进制形式而在输出时却又需要将二进制形式转换回文本形式,这样就会花费较多时间,因此在进行大量数据交换的情况下最好不要用这两个函数。

【文件定位函数】

在进行随机读写时需要调用文件定位函数rewind函数和fseek函数。

rewind函数已经在前面的例子中使用过,它的作用就是让位置指针重新返回文件的开头。其使用方法可以参见前面的例子。

fseek函数可以任意改变文件的位置指针。其调用的一般形式是:

fseek(文件类型指针,位移量,起始点);

这里的“起始点”包括文件开始、文件当前位置和文件末尾三个位置,分别用数字0、1和2代替。其实读者在stdio.h头文件中可以看到下面的宏定义:

/* Constants to be used as 3rd argument for "fseek" function

*/

#define SEEK_CUR 1

#define SEEK_END 2

#define SEEK_SET 0

SEEK_CUR代表文件的当前位置,SEEK_END代表文件末尾,SEEK_SET代表文件开始。

“位移量”则是指以“起始点”为基点向前移动的字节数。注意这里是说向前移动而不是向后。这一点需要牢记以免出错。还需要注意的是,ANSI C标准要求位移量是long型数据以免当文件的长度大于64KB时出现问题,并且规定在数字的末尾加一个L字母就表示是long型数据。

下面通过一个例子来详细解释这个函数的用法。程序的作用是在文件 file14-8中读出

396第二个学生的数据。

程序14-8:P14-8.c

#include"stdio.h"

struct student

{ char name[10];

int num;

int age;

char addr[15];

}stu,*qq;

main()

{ FILE *fp;

char ch;

int i=1;

qq=&stu;

if((fp=fopen("file14-8","r"))==NULL)

{ printf("Cannot open the file,press any key to exit!");

getch();

exit(1);

}

rewind(fp);

fseek(fp,i*sizeof(struct student),0);

fread(qq,sizeof(struct student),1,fp);

printf("\n\nname\tnumber age addr\n");

printf("%s\t%5d %7d %s\n",qq->name,qq->num,qq->age,

qq->addr);

}

程序分析

程序开始定义结构体类型变量stu用来存储学生的数据,在程序的最后将这些数据打印至屏幕上。然后以只读的方式打开文件file14-8,并且用rewind函数将位置指针移到文件开始的位置,然后用fseek函数定位到第二个学生数据所在处,将其读出。

需要注意的一点是,fseek函数通常用在二进制文件的定位上,这是因为它用于文本文件时需要进行字符转换,而在计算位置时往往会发生混乱。

【ferror函数】

功能

检查对文件的输入输出是否有错。

语法

int ferror(fp)

char *fp;

返回值

调用函数出错返回非0值,成功则返回0。

397 ferror函数是用来检查调用各种对文件输入输出函数是否成功的。在对一个文件调用输入输出函数后立即就产生一个新的ferror函数值。因此,当调用一个输入输出函数后应该马上对其进行检查,否则信息会丢失。还有一点要说明的是,在执行 fopen 函数时 ferror函数的初始值将自动置为0。

398【clearerr函数】

功能

清除文件指针错误。

语法

void clearerr(fp)

FILE *fp;

返回值

该函数没有返回值。

如果调用输入输出函数对文件进行操作出错,就会出现错误标志,并且一直保留到clearerr函数、rewind函数或者任何的一个输入输出函数的出现。clearerr函数的作用就是将文件错误标志和文件结束标志置为0。

本章小结

C语言系统在处理文件时,并不区分类型,而是将它们都看成是字符流,按

字节进行处理。

按照编码方式分类,文件可分为ASCII文件(又可以称为文本文件)和二进

制文件。

在C语言中,使用文件指针来标志文件,当打开一个文件时,就可以得到该

文件的指针。

应该养成习惯:使用文件之前先打开文件,使用完毕后将文件关闭。

使用文件之前需要指明文件的打开方式,同时还要指明文件是二进制文件还

是文本文件。

对文件的读写可以按照字符、字符串和数据块三种方式进行。也可以按照指

定的方式进行读写。

对文件的读写可以分为顺序读写和随机读写两种,其中随机读写需要调用特

别的函数来参与。

典 型 例 题

【例题14-1 fopen函数的应用】

(全国计算机等级考试二级笔试试卷20xx年4月)

399 题干

若要打开A盘上user子目录下名为abc.txt的文本文件进行读、写操作,下面符合此要求的函数调用是( )。

A)fopen("A:\user\abc.txt","r")

B)fopen("A:\\user\\abc.txt","r+")

C)fopen("A:\user\abc.txt","rb")

D)fopen("A:\\user\\abc.txt","w")

解题方法

本题是关于文件的一道题,考查的是文件打开函数fopen()的使用。对于本题所涉及到的知识点读者应该熟练掌握。

知识点分析

打开文件的函数 fopen() 的使用方法比较简单,是读者应该掌握的基础知识。解答本题需要明确的两点是:第一需要知道文件名的写法,第二还要清楚文件打开的一般读写方式。fopen函数的使用方法:fopen(文件名,使用文件方式)。

文件名的写法。读者可能感到奇怪,在前面所举的例子中,使用的文件名仅仅是文件的名称而已,并没有指定文件的路径。这是因为上面的例子均已经假定了文件的默认路径,在这个默认的路径下,可以省略文件的路径而直接应用它的名称来标志即可。在通常的情况下,文件的默认路径就是编译程序当前的安装路径,当然这个默认路径可以在编译系统中进行设定,具体请参考“C语言上机指导”里的内容。

如果需要指定路径的话那又该怎么办呢?

读者已经知道了DOS系统中文件路径的表达方式。比如,现在需要查看C盘子目录file下的一个文件file1.txt的内容,可以使用下面的命令。

C:\>TYPE C:\FILE\FILE1.TXT

这里文件的路径就是C:\FILE\FILE1.TXT。该路径是从C盘根目录开始,经过一级子目录后找到文件的。目录名之间用反斜杠“\”相互隔开。

在C语言中的路径表达方式与DOS系统类似,也可以说是采用了DOS系统的管理方式。它的路径也是经过一级一级的目录后才能到达所需的文件。所不同的是各级目录之间是用双反斜杠“\\”来隔开的。比如C:\\file\\file1.c。

其实这也是容易理解的。因为在C语言中反斜杠“\”是有其自身的特殊的含义的,它跟其他一些字符组合构成转义字符,比如‘\n’、‘\t’、‘\012’,等等。而要表示普通的反斜杠需要用转义字符‘\\’来实现。所以在 C语言的路径表示方法中用‘\\’来代替普通的‘\’。

文件打开方式。关于文件的打开方式应该不难理解,因为文件的打开方式并不多。读者只要清楚文件打开方式分成二进制文件和文本文件两大类,并且能够记住文本文件的几种打开方式即可。因为二进制文件的打开方式只是在文本文件打开方式的基础上加了一个字母b(byte)而已。

400 正确答案

B

难度提示

中级。本题考查的知识点较为偏僻。需要读者经常上机练习才能掌握这个知识点。但由于文件这一部分是非常重要的一章,所以读者应该有足够的时间用来进行上机练习。特别是指定路径的文件的应用,更是练习的对象,因为在编写大型的程序时它是会经常被用到的。

【例题14-2 判断文件结束】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

以下程序用来统计文件中字符个数,请填空。

程序14-9:P14-9.c

#include"stdio.h"

main()

{ FILE *fp;

long num=0L;

if((fp=fopen("fname.dat","r"))==NULL)

{ pirntf("Open error\n");

exit(0);

}

while(______)

{ fgetc(fp);

num++;

}

printf("num=%1d\n",num-1);

fclose(fp);

}

解题方法

本题是关于文件操作的一道题。有关文件操作的题目可以分成两大类:一类是关于文件的打开方式和文件名的使用,比如典型例题14-1就属于这种题型;另外一类就是关于文件读写语句的使用方法的,比如本题中所涉及到的知识即属于此类。

知识点分析

首先程序以只读方式打开文件fname.dat。因为本题只要求统计该文件中的字符个数,所以用只读方式打开文件是合理的。

打开文件之后就可以读取文件中的数据。在开始读取数据时,文件中的位置指针指向起始位置,当读取了一个字符后,位置指针就顺序后移一个字符。显然本题是采用顺序读写的方式来读取数据而非随机读写。

401 在while语句里,逐个读取字符,每读取一个字符就使得变量num自加一,这样就可以得到整个文

件的字符个数。判断while语句停止的方法是,当读取的字符为文件结束标志时即跳出循环。

另外需要注意的一点就是在程序中定义了num为长整型数据(long),并且为其赋初值0L。C语言规定,在数字的末尾加一个字母L就表示它是long型数据。

正确答案

!feof(fp)

难度提示

中级。本题考查的知识点是在编写C语言程序时经常会遇到的。特别是在处理关于文件的问题时一般都会遇到判断文件结束的问题。所以本题的知识点对考生来说应该并不陌生。

【例题14-3 只读打开方式“w”】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

下面的程序执行后,文件test.txt中的内容是( )。

程序14-10:P14-10.c

#include"stdio.h"

void fun(char *fname,char *st)

{ FILE *myf;

int i;

myf=fopen(fname,"w" );

for(i=0;i<strlen(st);i++)

fputc(st[i],myf);

fclose(myf);

}

main()

{ fun("test","new world");

fun("test","hello,");

}

A)hello, B)new worldhello,

C)new world D)hello, rld

解题方法

这道题是关于文件打开方式和文件使用的一道题,属于上面所说的类型之一。也就是说,读者在复习文件这一章的时候,要紧紧抓住这两类问题,以此为复习的基点,然后在此基础上扩充基础知识和基本的编程技巧。因为这两类问题是掌握文件这一部分内容时的基本问题。

402 知识点分析

程序开始首先定义了一个函数fun用来实现对文件的读取任务。在这个子函数中并没有指定文件名,而是将其作为一个形参,通过指针来传递。也就是说传递的值不同就可以打开不同的文件。同时也将需要写入文件的内容作为函数形参,通过字符指针变量来传递。

在main()函数中,有两次调用子函数fun。解答本题的关键就是要清楚这两次调用之间的关系。在fun子函数中规定了打开文件的方式是“只写(w)”。以这种方式打开文件时,如果原来不存在该文件,在打开时就自动新建立一个以指定名字命名的文件;如果原来已经存在一个以该文件命名的文件,打开时就将其自动删除而重新建立一个新文件。

因为两次调用的文件名是相同的,所以当第二次调用fun函数时,就自动将第一次建立的文件删除而创建了一个新的空文本文件。然后就将数据“hello,”写入文件。

正确答案

A

难度提示

中级。本题主要考查的知识点是文件的打开方式,而这在进行文件操作时是非常普遍的一个问题。

【例题14-4 文件的打开和定位】

(全国计算机等级考试二级笔试试卷20xx年9月)

题干

以下程序段打开文件后,先利用 fseek 函数将文件位置指针定位在文件末尾,然后调用ftell函数返回当前文件位置指针的具体位置,从而确定文件长度,请填空。

FILE *myf;

long f1;

myf=______("test.txt","rb");

fseek(myf,0,SEEK_END);

f1=ftell(myf);

fclose(myf);

printf("%d\n",f1);

解题方法

题目是关于文件定位的一个问题,这也属于文件读写方面的问题。解答这种类型的问题,需要知道一些常用的文件定位函数的使用方法。

知识点分析

首先定义了文件指针变量myf和long型变量f1。给myf赋值,即将文件test.txt的信息告诉该文件指针,然后fseek函数的作用是将文件位置指针定位到文件末尾。

本题中出现了另外一个文件定位函数ftell。对它的用法简述如下所示。

ftell函数的功能是返回fp所指向的文件中的读写位置,返回值就是fp所指向的文件中

403的读写位置。其一般定义为:

long ftell(fp)

FILE *fp;

ftell 函数的作用就是得到流式文件中的当前位置,这个位置是用相对于文件开头的位移量来表示的。在进行文件操作时,由于位置指针经常移动,人们往往不容易弄清楚当前的位置,用ftell函数就可以得到当前位置。

如果出错,那么ftell函数的返回值是-1L。

正确答案

fopen

难度提示

中级。本题其实并不需要知道ftell函数的作用就可以作答。因为在使用文件时有一个基本的观点就是使用文件之前必须先打开文件,使用完后应该关闭文件。在本题中,在用fseek函数和ftell函数对文件进行操作之前当然应该先打开文件,并且在该语句的后面还有打开方式“rb”,从这里也可以看出本题的答案。

【例题14-5 feof函数的应用方法】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

若fp是指向某文件的指针,且已读到文件末尾,则库函数feof(fp)的返回值是( )。

A)EOF B)–1 C)非0值 D)NULL

解题方法

本题是关于文件操作中标准函数feof的使用方法的一道题。

知识点分析

首先应该清楚feof函数的作用,该函数是用来判断文件是否结束的。其次需要知道feof函数有两种返回值:当遇到文件结束,即到了文件末尾时,该函数的返回值是非0值,否则返回0值。

本题题意已经明确说明已经读到文件末尾,所以feof(fp)的返回值不难判断。

正确答案

C

难度提示

初级。本题是关于文件操作的一道简单题目。只要读者掌握了一些常用函数的使用方法就可以轻松解答本题。

【例题14-6 文件写入操作】

404 (全国计算机等级考试二级笔试试卷20xx年4月)

题干

下面程序把从终端读入的文本(用@作为文本结束标志)输出到一个名为 bi.dat 的新文件中,请填空。

程序14-11:P14-11.c

#include"stdio.h"

main()

{ FILE *fp;

char ch;

if((fp=fopen(______))==NULL)

exit(0);

while((ch=getchar())!='@')

fputc(ch,fp);

fclose(fp);

}

解题方法

本题是关于文件操作中写入文件的一道题。解答这种题目关键在于正确阅读题目所给的程序,然后运用学过的相关知识进行解答。

知识点分析

前面已经说过,在使用文件之前必须先将文件打开。程序中if语句的作用就是将文件打开并将文件信息赋给文件指针fp。

while语句的作用是将从终端输入的文本写入文件,这就需要指定文件要以只写的方式打开。

正确答案

"bi.dat","w"或"bi.dat","w+"

难度提示

初级。本题涉及到的知识点比较简单,是文件操作中最基本的知识。读者应该熟练掌握。

本 章 练 习

【填空题】

1.根据文件的编码方式,文件可以分为______和______。从用户的角度看,文件可分为______和______两种。C语言中存在两种处理文件的方法:一种就是“______”,另外一种就是“非缓冲文件系统”。

405 2.C语言中经常用到的格式化读写函数是______和______,这两个函数的读写对象是______。C语言中用于实现文件定位的函数有______和______。

3.以下 C语言程序将磁盘中的一个文件复制到另一个文件中,两个文件名在命令行中给出。请将程序补充完整。

程序14-12:P14-12.c

#include"stdio.h"

main(argc,argv)

int argc;

char *argv[];

{ FILE *f1,*f2;

char ch;

if(argc<______)

{ printf("Parameters missing!\n");

exit(0);

}

if(((f1=fopen(argv[1],"r"))==NULL)||((f2=fopen(argv[2],"w")) =

NULL))

{ printf("Can not open file!\n");

exit(0);

}

while(______)

fputc(fgetc(f1),f2);

fclose(f1);

fclose(f2);

}

【综合题】

1.总结常用的缓冲文件系统函数,列出函数名、功能、语法和返回值类型。

2.编写程序将磁盘上某个扩展名为.c的源程序中的所有注释删除。

3.编写程序把一个文本文件中凡是good的地方均变成bad。

4.编写程序生成一个链表,每个结点是文本文件的一行,并测试其长度。【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □ 好□

3.本章疑难问题汇总:

4.对自己说点什么吧:

406 第 15 章 笔试模拟题

引言

本章包含3套笔试模拟题,考生进入总复习阶段后,可以定期进行自测,每

一套模拟题后面都给出了一张“笔试模拟总结表”,考生可以利用该表对每次模拟

考试结果进行及时总结。请认真填写该表,因为进步来自总结。

全国计算机等级考试二级笔试模拟试卷一

基础知识和C语言程序设计

(考试时间120分钟,满分100分)

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

下列各题A、B、C、D四个选项中,只有一个选项是正确的,请将正确选项涂写在答题卡相应位置上,答在试卷上不得分。

1.能将源程序转换成目标程序的是( )。

A)调试程序 B)解释程序

C)编译程序 D)编辑程序

2.DOS控制键Ctrl+Alt+Del的功能是( )。

A)删除一个字符并退格 B)暂停标准输出设备的输出

C)热启动 D)终止当前操作

3.十进制数269转换成十六进制为( )。

A)10E B)10D

C)10C D)10B

4.下列DOS命令中正确的是( )。

A)REN \USER\A.TXT\B.TXT B)CD\

C)TYPE *.TXT D)MD\

5.CAI指的是( )。

A)系统软件 B)计算机辅助教学软件

C)计算机辅助设计软件 D)办公自动化系统

6.1MB等于( )。

A)1000KB B)1024KB

C)1000*1000KB D)1024*1024KB

4077.为了将当前盘当前目录中的ABC.FOR复制到当前盘当前目录下的文件XY.C中,并同时将文件ABC.FOR从盘中删去,应使用的命令( )。

A)REN ABC.FOR XY.C B)COPY ABC.FOR XY.C

C)DEL ABC.FOR XY.C D)DEL ABC.FOR

8.为了将当前目录下所有扩展名为.PAS的文件改成扩展名为.P,应使用的命令是( )。

A)REN *.PAS *.?AS B)REN PAS P

C)REN *.PAS *.P D)REN *.PAS *.P??

9.设当前盘为C盘,其根目录下有两个子目录D1与D2,且当前目录为D1,若要进入子目录D2,可使用的命令为( )。

A)CD D2 B)CD D2\

C)CD \D2 D)CD D1\D2

10.设当前盘为C盘,A盘当前目录为根目录,为了删除A盘目录\ws下扩展名为TXT的所有文件,可用的命令为( )。

A)DEL WS\*.TXT B)DEL \WS\*.TXT

C)DEL A:\WS\?.TXT D)DEL A:WS\*.TXT

11.设当前盘为C盘,所用的软盘已格式化,且容量相同,则下列DOS命令中的错误的是( )。

A)DISKCOPY A: A: B)COPY A: *.*

C)COPY *.* D)DEL A:WS\*.TXT

12.各种网络传输介质( )。

A)具有相同的传输速率和相同的传输距离

B)具有不同的传输速率和不同的传输距离

C)具有相同的传输速率和不同的传输距离

D)具有不同的传输速率和相同的传输距离

13.按通信距离划分,计算机网络可以分为局域网和远程网,下列网络中属于局域网( )。

A)Internet B)CERNET C)Novell D)CHINANET

14.在Windows中,启动应用程序的正确方法是( )。

A)用鼠标双击该应用程序图标 B)将该应用程序窗口最小化成图标

C)将该应用程序窗口还原 D)用鼠标右击该应用程序图标

15.在电子邮件中,所包含的信息( )。

A)只能是文字 B)只能是文字与图形图像信息

C)只能是文字与声音信息 D)可以是文字,声音和图形图像信息

16.若x,a,b和c均是int型变量,则执行表达式x=(a=1,b=2)后x的结果为(

A)1 B)2

C)3 D)不确定

17.下面不正确的转义字符是( )。

A)'\101' B)'\''

C)'\xaf ' D)'\018'

408 18.以下运算符中优先级最高的算符( )。

A)>= B)/=

C)-- D)sizeof

19.以下能正确定义整型变量a,b和c并为其赋初值1的语句是( )。

A)int a=b=c=1; B)int a, b, c=1;

C)a=b=c=1; D)int a=1, b=1, c=1;

20.设 c1,c2均是char类型变量,则以下不正确的函数调用为( )。

A)printf("%c,%c", c1, c2); B)getchar(c1); 。 )

C)putchar('\''); D)putchar(c1);

21.一个C语言程序是由( )。

A)一个主程序和若干子程序组成的 B)函数组成

C)若干过程组成 D)若干子程序组成的

22.下面正确的字符常量为( )。

A):"n" B)'\\'

C)'L' D)''

23.若有定义语句;float x[6];则以下对x数组元素的正确引用形式是( )。

A)x+2 B)&(x+2)

C)*(x+2) D)*&x[6]

24.在C语言中,要求运算数必须是整型的运算符是( )。

A)/ B)++

C)!= D)%

25.已有如下定义和输入语句,若要求a,b,c,d可得到的值分别为10,20,A和B,当分别开始输入数据时,正确的数据输入方式是( )(其中□表示空格)。

A)10A□20B<回车> B)10□A□20□B<回车>

C)10A20B<回车> D)10A20□B<回车>

26.已知ch是字符型变量,下面正确的赋值语句是( )。

A)ch="\"; B)ch='\xff';

C)ch='123'; D)ch='\08';

27.若x,y,z均为int类型,则执行以下程序段后的输出结果是( )。

x=10;

y=3;

z=7;

printf("%d\n",x>10?x+100:x-10);

printf("%d\n",y++||z++);

printf("%d\n",!y>z);

printf("%d\n",y&&z);

A) 0 B) 1 C)

1 1 1 1

1 1 0 0

1 1 1 0

40928.以下不是无限循环的语句是( )。

A)for(y=0,x=1;x>y++;x++)

B)for(;;x++)

C)while(-1) {x++;}

D)for(i=10;;i--) sum+=i;

29.以下程序段的执行结果是( )。

int x=5;

do

{

printf("%2d\n",x--);

}

while(!x);

) 0 0 D

A)1 B)无任何输出 C)4 D)陷入死循环30.以下对一维整型数组y的正确说明是( )。

A)int y(10); B)int k=10,y[k];

C)int k; D)#define SIZE 8

scanf("%d",&k); int y[SIZE];

int y[k];

31.以下对二维数组y的正确说明是( )。

A)int y[3][]; B)float y(3,4);

C)double y[1][4]; D)float y(3)(4);

32.若有说明int s[3][4]={0};则下面正确的叙述为( )。

A)只有元素s[0][0] 可得到初值0。

B)此说明语句不正确

C)数组s中各元素都可得到初值,但其值不一定为0。

D)数组s中每个元素均可得到初值0。

33.以下正确的数组定义语句是( )。

A)int y[1][4]={1,2,3,4,5};

B)float x[3][]={{1},{2},{3}};

C)long s[2][3]={{1},{1,2},{1,2,3}};

D)int m[1][4]={4};

34.以下程序段的输出结果是( )。

char str1[10]={'s','t','u','d','e','n','t'};

printf("%d\n",strlen(str1));

A)7 B)8 C) 10 D)存在语法错误35.以下正确的函数定义是(。 )

A)double f1(int x,int y) B)double f1(int x;int y)

C)double f1(int x;float y) D)double f1(int x,y)

36.C语言规定,函数返回值的类型是( )。

410 A)return语句中的表达式类型所决定的 B)调用该函数时的主调函数所决定

C)调用该函数时系统临时决定的 D)数组元素的个数

37.若用数组名作为实参,则传递给形参的是( )。

A)数组的首地址 B)数组第一个元素的值

C)数组中全部元素的值 D)数组元素的个数

38.以下程序的运行结果是( )。

#include"stdio.h"

main()

{

int k=4,m=1,p;

p=fun1(k,m);

printf("%d,",p);

p=fun1(k,m);

printf("%d\n",p);

}

fun1(int x,int y)

{

static int m=0,i=2;

i+=m+1;

m=i+x+y;

return(m);

}

A)8,17 B)8,16 C)8,20 D)8,8

39.以下程序的运行结果是( )。

#define item(a) a*10

#define out(x) "%d",(int )item(x)

#define prt(x) printf(out(x))

main()

{

int a=2;

prt(a+2);

}

A)30 B)40 C)22 D)产生出错信息40.以下程序的输出结果是(

main()

{

int x[]={15,10,17,21,16,23},i,*pt;

pt=x;i=2;

printf("%d\n",(pt+=2)[i]);

}

A)21 B)16

C)数组元素x[3]的地址 D)数组元素x[4]地址 。 )

41.若有说明int *p,n;以下能通过scanf语句正确读入数据的程序段是( )。

411 A)p=&n;scanf("%d",&p); B)p=&n;scanf("%d",*p);

C)*p=&n;scanf("%d",p); D)p=&n;scanf("%d",p);

42.以下程序的运行结果是( )。

#include"stdio.h"

fex(int **ss)

{

int *temp;

temp=(int *)malloc(sizeof(int));

*ss=temp;

*temp=5;

}

main()

{

int *s=NULL;

fex(&s);

printf("%d\n",*s);

}

A)动态单元temp的地址 B)空指针

C)5 D)报错

43.设有语句:int a=1,b=2,*p1=&a,*p2=&b;以下可使指针p1指向变量b的赋值语句是(

A)p1=*p2 B)*p1=p2 。 )

C)p1=p2 D)*p1=p2

44.以下与int *p[3];等价的定义语句是( )。

A)int *p; B)int *(p[3]);

C)int p[3]; D)int (*p)[3]

45.在以下选项中,操作不合法的一组是( )。

A)int x[6],*p;p=&x[0];

B)int x[6],*p;*p=x;

C)int x[6],*p;p=x;

D)int x[6],p;p=x[0];

46.设有如下定义,若字符型变量1KB,整型变量占2KB,双精度变量占8KB,则结构变量s占用内存的字节数是( )。

struct data

{

int i;

char ch;

double f;

union{

int a;

int b;

}un;

}s;

412A)8 B)13 C)15 D)17

47.以下对结构体变量mix中成员x的引用正确的是( )。

struct

{

int i;

int x;

}mix,*p;

p=&mix;

A)(*p).mix.x B)(*p).x

C)p->mix.x D)p.mix.x;

48.若有定义:

enum weekday

{

mon,tue,wed,thu,fri

}workday;

则以下不正确的赋值语句是( )。

A)workday=(enum weekday) 3;

B)workday=(enum weekday) (4-2);

C)workday=3;

D)workday=wed;

49.设有定义:unsigned op=8;则与语op=op>>2;等价的语句是( )。

A)op=op*2; B)op=op/2; C)op=op*4; D)op=op/4;50.下面程序的功能是(

#include "stdio.h"

。 )

main()

{

int i=0,n=0;

char s[80],*p;

p=s;

strcpy(p,"It is a book.");

for( ;*p!='\0';p++)

if(*p==' ') i=0;

else if(i==0) {n++;i=1;}

printf("n=%d\n",n);

}

A)统计字符串中的单词个数 B)统计字符串中的空格个数

C)统计字符串中的字母个数 D)统计字符串中的全部字符个数二、填空题(每空2分,共40分)

请将每一个空的正确答案写在答案卡上,答在试卷上不得分。

1.为了将当前盘当前目录下所有扩展名为.txt的文件内容打印输出,其DOS命令

413为(1)。

2.设当前盘为C盘,如果想知道A盘的当前目录是什么,又不想改变当前盘,则应使用的DOS命令为(2)。

3.设当前盘当前目录下有一个可执行程序文件ABC.EXE,现要执行该程序,并要求将该程序的输出结果存放到当前盘当前目录下的文件XYZ.DAT ZG,则应使用的DOS命令为(3)。

4.计算机网络按通信距离来划分,可分为局域网和广域网,因特网属于(4)。

5.十六进制数1D560转化为十进制数为(5)。

6.设C语言中,一个int型数据在内存中占两个KB,则int型数据的取值范围为(6)。

7.在 scanf函数调用语句中,可以在格式化字符和%号之间加一个星号,它的作用是(7),当输入以下数据;10□□20□□30□□40<回车>(‘□’表示空格)

x, y, z将分别得到的值是(8)。

int x,y,z;

scanf("%d%d*d%d%d",&x,&y,&z);

8.如果运行输入字符“Q”,则以下程序的运行结果是(9)。

main()

{

char ch;

scanf("%c",&ch);

ch=(ch>='A'&&ch<='Z')?(ch+32):ch;

ch=(ch>='a'&&ch<='z')?(ch-32):ch;

printf("%c",ch);

}

9.设i,j,k均是int变量,则执行以下for循环后,k的值为(10)。

for(i=0;j=10;i<=j;i++,j--)

k=i+j;

10、执行以下程序段后,num的值是(11)

char ch1[]="600";

int x,num=0;

for(x=0;ch1[x]>='0'&&ch1[x]<=’9’;x++)

num=10*num+ch1[x]-'0';

11.以下函数fmax返回数组arr中最大元素的下标,数组中元素的个数由x传入,请填空。

fmax (int arr[],int x)

{

int max,q;

for(q=1,max=0;q<x;q++)

if(arr[q]>arr[max])

( 12 )

}

414 12.以下程序调用swap函数利用指针px和py将两个整型变量x和y的值进行交换,请填空。

void swap(int *p1,int *p2)

{ int t;

t=*p1;*p1=*p2; *p2=t;

}

main()

{ int x=5,y=7;

int *px=&x, *py=&y;

swap(13);printf("%d,%d\n",x,y);

}

13.以下程序的运行结果是(14)。

struct pp

{ int *rear;

}

int a=1,b=2,c=3;

struct pp x[3]={&a,&b,&c},*s=x;

main()

{ printf("%d\n",*++s->rear);

}

14.下面程序是把从终端读入的20个字符作为字符串放在字符数组中,然后用指针变量输出上述字符串,请填空。

#include "stdio.h"

main()

{

int i;

char s[21],*p;

for(i=0;i<20;i++)

s[i]=getchar();

s[i]=(15);p=(16);

while(*p) prtchar (17);

}

15.以下程序将数组a的4个元素和数组b的6个元素写到名为lett.dat的二进制文件中,请填空。

#include "stdio.h"

main()

{

FILE *fp;

char a[4]="1234",b[6]="abcdef";

if((fp=fopen("18",“wb”))=NULL) exit (0);

fwrite(a,sizeof(char),19,fp);

fwrite(b,20,1,fp);

fclose(fp);

}

415 笔试模拟总结表一

1.模拟得分:

2.得到该成绩的原因:

3.问题汇总(请填写“问题”、“是否解决”和“解决办法”):

4.对自己说点什么吧:

416 全国计算机等级考试二级笔试模拟试卷二

基础知识和C语言程序设计

(考试时间120分钟,满分100分)

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

下列各题A、B、C、D四个选项中,只有一个选项是正确的,请将正确选项涂写在答题卡相应位置上,答在试卷上不得分。

1.在微机系统中,基本的输入输出模块BIOS存放在( )。

A)ROM中 B)RAM中

C)硬盘中 D)寄存器中

2.不属于计算机的常用技术指标的是( )。

A)机器所能接受的指令条数 B)可靠性

C)体积 D)允许配置的外设数量

3.微机的运行速度中,指令的执行速度主要取决于( )。

A)外存的速度 B)主频

C)内存容量 D)内存的速度

4.在计算机中,一个字节最大容量的二进制数换算成无符号十进制整数为( )。

A)257 B)256 C)254 D)255

5.二进制数00101001和01011101相加结果是( )。

A)01111001 B)100000110

C)00110011 D)011011101

6.浮点型数据在机器中表示的方法有( )。

A)双精度型和单精度型 B)定点法和ASCII码法

C)BCD码法和浮点法 D)定点法和浮点法

7.程序员用各种高级语言编写的程序,一般都要翻译成以( )形式表示的机器语言程序,计算机才能执行。

A)指令 B)命令 C)二进制数码 D)符号

8.文件型病毒传染的对象主要是( )类文件。

A).EXE和.WPS B).COM和.EXE

C).WPS D).DBF

9.DOS使用了一些专用的设备名,如:打印机的设备名为( )。

A)CON B)AUX C)COM1 D)PRN

10.在高密驱动器A里格式化一张360KB的软盘,且在格式化盘上放置DOS的系统文件的命令是( )。

A)FORMAT A:/4/S B)FORMAT A:/0/P

417 C)FORMAT A:/1/S D)FORMAT A:

11.下面的DOS命令,( )为外部命令。

A)CHKDSK B)DIR C)ERASE D)RENAME

12.关于局域网的几种拓扑结构比较正确的是( )。

A)环形结构的传输速率比总线型结构低

B)总线结构的费用比环型结构的费用高

C)总线型结构连接的总长度大于星型结构

D)总线型结构连接的总长度小于星型结构

13.Windows环境下,PrintScreen键的作用是( )。

A)复制当前窗口到剪贴板 B)打印当前窗口的内容

C)打印屏幕内容 D)复制屏幕到剪贴板

14.在Windows环境下,实现窗口移动的操作是( )。

A)用鼠标拖动窗口中的标题栏

B)用鼠标拖动窗口中的控制按钮

C)用鼠标拖动窗口中的边框

D)用鼠标拖动窗口中的任何部位

15.若想要通过异或运算对变量a进行高4位求反,低四位不变,则b应为(

A)11110000 B)00001111

C)视a值而定 D)不可能实现

16.C语言中文件的存取方式是( )。

A)顺序存取 B)随机存取

C)a与b均可 D)a与b均不可

17.C语言中各种基本数据类型的存储空间长度排列为( )。

(二进制表示)。 )

A)char?long?int?float?double

B)double?float?long?int?char

C)char?int?long?float?double

D)float?int?long?char?double

18.对两个静态函数A和B进行如下初始化:

static char A[]="ABCDEF";

static char B[]={'A','B','C','D','E','F'};

则下列叙述正确的是( )。

A)A和B完全相同 B)A和B只是长度相等

C)A和B不相同,A是指针数组 D)A数组长度比B数组长

19.C语言中的循环语句有for,while,do-while,还有一个是( )。

A)if B)switch C)goto D)break

20.以下描述中,正确的是( )。

A)调用函数时,实参可以是表达式

418 B)调用函数时,将为形参分配内存单元

C)调用函数时,实参与形参的原型必须一致

D)调用函数时,实参与形参可以用内存单元

21.若有以下说明,则对结构体变量exp10中成员std的引用不正确的是(

{

int std;

float std1;

}exp10,*p;

。struct example )

A)exp10.std B)example.std

C)p->std D)(*p).std

22.写出下列程序段的输出结果( )。

char a=9,b=020;

printf("%o\n",~a&b<<1);

A)00100000 B)8 C)040 D)077

23.下列标识符中不合法的是( )。

A)student_name B)_name

C)name8 D)3DS

24.C语言标准库函数"strcat(strcpy(str1,str2),str3)"的功能是(

A)将串str1复制到串str2中后再连接至串str3之后

B)将串str1连接至str2之后再复制至串str3之后

C)将串str2复制到串str1串后再将串str3连接到串str1之后

D)将串str2连接到串str1之后再将串str1复制到串str3串

25.以下程序的输出结果是( )。

main()

{

int i,a[10];

for(i=9;i>=0;i--)

a[i]=10-i;

printf("%d%d%d",a[2],a[5],a[8]);

}

。 )

A)258 B)741 C)852 D)369

26.以下程序的输出结果是( )。

main()

{

int a[4][4]={{1,3,5},{2,4,6},{3,5,7}};

printf("%d%d%d%d\n",a[0][3],a[1][2],a[2][1],a[3][0];

}

A)0650 B)1470 C)5430 D)输出值不定

27.以下程序的输出结果是( )。

419 main()

{

int a, b;

for(a=1,b=1;a<=100;a++)

{

if(b>=10)

break;

if(b%3==1)

{

b+=3;

continue;

}

}

printf("%d\n",a);

}

A)101 B)6 C)5 D)4

28.一个C程序由函数A、B、C和函数P构成,在函数A中分别调用了函数B和函数C,在函数B中调用了函数A,且在函数P中也调用了函数A,则可以说( )。

A)函数B中调用的函数A是函数A的间接递归调用

B)函数A被函数B中调用的函数A间接递归调用

C)函数P直接递归调用了函数A

D)函数P中调用的函数A是函数P的嵌套

29.参考下面函数f的定义:

f(int a){printf("%d",a);}

函数f的返回类型( )。

A)同参数a的类型相同 B)是void类型

C)没有返回值 D)无法确定

30.下列程序运行结果是( )。

#include<stdio.h>

#define M 3

#define N M+1

#define NN N*N/2

main()

{

printf("%d,",NN);

printf("%d\n",5*NN);

}

A)3,17 B)4,18

C)6,18 D)8,40

31.若有以下的说明,则对初值中字符 'a' 的引用方式为( )。

static struct

{

420 char ch;

double x;

char a[];

}c[2][2]={

{

{'a',3.5,'bc'},

{'c',4.5,'de'},

{'m',8.6,'abc'}

}

};

A)c.ch B)c[0][0].ch C)c[1][1].ch D)a[0]32.从高至低排出运算符~,<<,>>,| 的优先级( )。

A)~, >>, <<, | B)|, ~, >>, <<

C)~, |, <<, >> D)<<, ~, >>, |

33.若希望向文件末尾添加新的数据则应以( )方式打开文件。

A)"r"方式 B)"w"方式

C)"a"方式 D)"rb"方式

34.设变量定义为int A=5,B=6,表达式(++A==B--)?++A:--B的值是( )。

A)5 B)6 C)7 D)8

35.C语言提供的预处理功能包括条件,其基本形式为:

#xxx 标识符

程序段1

#else

程序段2

#endif

这里xxx可以是( )。

A)define或include B)ifdef或include

C)indef或ifndef或define D)ifdef或ifndef或if

36.在16位IBM?PC机上使用C语言,如定义下列共用体类型变量:

union data

{

int i;

char ch;

float f;

}a,b,c;

则共用体变量a,b,c占用内存的字节数为( )。

A)1 B)2 C)4 D)7

37.下列if语句中,不正确的是( )。

A)if(x>y); B)if(x==y) x+=y;

C)if(x!=y) scanf("%d",&x) else x=1; D)if(x<y) {x++;y++}

38.在下列选项中,没有构成死循环的程序段是( )。

421 A)int i=100;

while(1)

{ i=i%100+1;

if(i>100)break;

}

B)for(;;);

C)int k=1000;

do{++k;}

while(k>=1000);

D)int s=36;

while(s);

--s;

39.以下程序运行后,输出结果是(

main()

{

int y=18,i=0,j,a[8];

do

{

a[i]=y%2;

。 )

i++;

y=y/2;

}

while(y>=1);

for(j=i-1>0;j--)

printf(%d",a[j]);

printf("h\n");

}

A)1000 B)10010 C)00110 D)10100

40.以下程序执行后a的值是( )。

main()

{

int a,k=4,m=6,*p1=&k,*p2=&m;

a=p1==&m;

printf("%d\n",a);

}

A)4 B)1 C)0 D)运行时出错,a无定值

41.以下程序运行后,输出结果是( )。

func()

{

int m=0,i=2;

i+=m+1;

m=i+a+b;

return(m);

}

422 main()

{

int k=4,m=1,p;

p=func(k,m);printf("%d,",p);

P=func(k,m);printf("%d\n",p);

}

A)8,15 B)8,16 C)8,17 D)8,8,

42.在以下一组运算符中,优先级最高的运算符是( )。

A)<= B)= C)% D)&&

43.以下对枚举类型名的定义中正确的是( )。

A)enum a={one,two,three};

B)enum a {one=9,two= -1,three};

C)enum a={"one","two","three"};

D)enum a {"one","two","three"};

44.以下程序的输出结果是( )。

#include

subl(char a,char b)

{

char c;

c=a;

a=b;

b=c;

}

sub2(char* a,char b)

{

char c;

c=*a;

*a=b;

b=c;

}

sub3(char* a,char*b)

{

char c;

c=*a;

*a=*b;

*b=c;

}

main()

{

char a,b;

a='A';b='B';sub3(&a,&b);putchar(a);putchar(b);

a='A';b='B';sub2(&a,b);putchar(a);prtchar(b);

a='A';b='B';sub1(a,b);putchar(a);putchar(b);

}

A)BABBAB B)ABBBBA

423 C)BABABA D)BAABBA

45.以下程序的输出结果是(

union myun

{

struct

{

int x,y,z;

}u;

int k;

}a;

main()

{

a.u.x=4;

a.u.y=5;

a.u.z=6;

a.k=0;

printf(%d\n",a.u.x);

}

。 )

A)4 B)5 C)6 D)0

46.设i,x都是int类型,则下面的for循环体可执行( )次。

for(i=0,x=0;i<=9&&x!=876;i++)

printf("*");

A)9次 B)876次 C)10次 D)无限循环

47.有语句:

char str1[10],str2[10]={"books"};

则能将字符串books赋给数组str1的正确语句是( )。

A)str1={"Books"}; B)strcpy(str1,str2);

C)str1=str2; D)strcpy(str2,str1);

48.类型定义:

char S[3]="AB";

char *p;

执行了语句p=S以后,*(p+2)的值是( )。

A)‘B’ B)‘\0’

C)不确定 D)字符'B'的地址

49.若x为int型变量,y是float型变量,所调用输入语句格式为:

scanf("x=%d,y=%f,&x,&y)

则将为使x=20,y=166.6,正确的输入是( )。

A)20 166.6 <回车> B)x=20,y=166.6 <回车>

C)20 <回车> 166 <回车> D)20,166.6 <回车>

424 50.下列程序段执行后,s的值是( )。

static char ch[]="600";

int a,s=0;

for(a=0;ch[a]>='0'&&ch[a]<='9';a++)

s=10*s+ch[a]-'0';

A)600 B)6 C)0 D)出错

二、填空题(每空2分,共40分)

请将每一个空的正确答案写在答案卡上,答在试卷上不得分。

1.自动批处理文件名为(1)。

2.DOS命令分为内部命令与外部命令,COMP命令属于(2)命令。

3.设当前为C盘,现要用一条DOS命令在打印机上输出A盘当前目录下所有扩展名为.DAT的文件内容。这一条DOS命令应为(3)。

4.要将当前盘当前目录下的子目录XYZ中所有扩展名为.BAK 的文件名改成扩展名为.FOR,应使用的DOS命令为(4)。

5.设DOS外部命令文件所在的路径均已用PATH命令打通。现要将当前盘当前目录下的文件WST.TXT设置为只读属性,应使用的DOS命令为(5)。

6.设x和y均为int型变量,且x=1,y=2,则以下表达式的值为(6)。

1.0+x/y

7.设i,j,k均为int型变量,则执行完下面的for循环后,k的值为(7)。

for(i=0,j=10;i<=j;i++,j--)

k=i+j;

8.设有以下定义的语句:

int a[3][2]={10,20,30,40,50,60},(*p)[2];

p=a;

则*(*(p+2)+1)值为(8)。

9.以下程序的输出结果是(9)。

#include

int fun(int x,int y)

{

static int m=0,i=2;

i+=m+1;

m=i+x+y;

return m;

}

main()

{

int j=4,m=1,k;

k=fun(j,m);

printf("%d,",k);

425 k=fun(j,m);

printf("%d\n",k);

}

10.若函数fun的类型void,且有以下定义和调用语句:

#define M 50

main()

{

int a[M];

...

fun(a);

...

}

定义fun函数首部可以用三种不同的形式,请写出这三种形式: 10)、(11)、(12)(注意:①形参的名字请用q,②使用同一种风格)。

11.若给fun函数的形参s传送字符串:"uuuu6354abc",则函数的返回值是(13)。

log fun(char s[])

{

long n;

int sign;

for(;isspace(*s);s++);

sign+(*s=='-')?-1:1;

if(*s=='+'‖*s=='-')

s++;

for(n=0;isdigit(*s);s++)

n=10*n+(*s-'0');

return sign * n;

}

12.以下函数用来在w数组中插入x,w数组中的数已按由小到大顺序存放,n 所指存储单元中存放数组中数据的个数。插入后数组中的数仍有序,请填空。

void fun(char *w,char x,int *n)

{

int i,p;

p=0;

w[*n]=x;

while (x>w[p])

(14);

for(i=*n;i>p;i--)

w[i]=(15);

w[p]=x;

++*n;

}

13.fun1函数的调用语句为:fun1(&a,&b,&c); 它将三个整数按由大到小的顺序调整后依次放入a,b,c,三个变量中,a中放量大数。请填空。

void fun2(int *x,int *y)

426 {

int t;

t=*x;

*x=*y;

*y=t;

}

void fun1(int *pa,int *pb,int *pc)

{

if(*pc>*pb)

fun2(16);

if(*pa<*pc)

fun2(17);

if(*pa<*pb)

fun2(18);

}

14.下面fun函数的功能是将形参x的值转换成二进制数,所得二进制数的每一位数放在一维数组中返回,二进制数的最低位放在下标为0的元素中,其他依次类推。请填空。

fun(int x,int b[])

{

int k=0,r;

do

{

r=x%(19);

b[k++]=r;

x/=(20);

}

while(x);

}

笔试模拟总结表二

1.模拟得分:

2.得到该成绩的原因:

3.问题汇总(请填写“问题”、“是否解决”和“解决办法”):

4.对自己说点什么吧:

427 全国计算机等级考试二级笔试模拟试卷三

基础知识和C语言程序设计

(考试时间120分钟,满分100分)

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

下列各题A、B、C、D四个选项中,只有一个选项是正确的,请将正确选项涂写在答题卡相应位置上,答在试卷上不得分。

1.PC机中的显示卡是通过( )与主机板连接的。

A)适配器 B)RAM

C)ROM D)驱动器

2.将十进制数0.7109375转换成二进制数是( )。

A)0.1011001 B)0.0100111

C)0.1011011 D)0.1010011

3.在计算机中,一个字节最大容量的二进制数换算成无符号十进制整数为( )。

A)257 B)256

C)254 D)255

4.在确保计算机安全方面,不合理做法是( )。

A)将重要的系统软件,应用软件做备份并贴上写保护

B)经常用防病毒软件检测系统并清除

C)直接使用从公共机房复制的磁盘

D)安装防病毒卡

5.Netware 386是一个( )。

A)用于网络的终端 B)网络服务器操作系统

C)网络服务软件 D)多用户多任务的局域网操作系统

6.浮点型数据在机器中表示的方法有( )。

A)双精度型和单精度型 B)定点法和ASCII码法

C)BCD码法和浮点法 D)定点法和浮点法

7.MS-DOS启动后,下面文件( )已驻留内存。

A)COPY.COM B)REN.COM

C)COMMAND.COM D)DISKCOPY.COM

8.若当前盘为C盘,在A盘目录\MY中只有文本文件FN.DAT,A盘当前目录为根目录,则查看该文件的内容可使用的命令是( )。

A)TYPE MY\FN.DAT B)TYPE A:\MY\*.*

C)TYPE \MY\FN.DAT D)TYPE A:\MY\FN.DAT

9.DOS使用了一些专用的设备名,如:打印机的设备名为( )。

428 A)CON B)AUX C)COM1 D)PRN

10.下面的DOS命令,( )为外部命令。

A)CHKDSK B)DIR C)ERASE D)RENAME

11.在高密驱动器A里格式化一张360KB的软盘,且在格式化盘上放置DOS的系统文件的命令是( )。

A)FORMAT A:/4/S B)FORMAT A:/0/P

C)FORMAT A:/1/S D)FORMAT A:

12.关于局域网的几种拓扑结构比较正确的是( )。

A)环型结构的传输速率比总线型结构低

B)总线结构的费用比环型结构的费用高

C)总线型结构连接的总长度大于星型结构

D)总线型结构连接的总长度小于星型结构

13.Windows环境下,PrintScreen键的作用是( )。

A)复制当前窗口到剪贴板 B)打印当前窗口的内容

C)打印屏幕内容 D)复制屏幕到剪贴板

14.在Windows环境下,实现窗口移动的操作是( )。

A)用鼠标拖动窗口中的标题栏

B)用鼠标拖动窗口中的控制按钮

C)用鼠标拖动窗口中的边框

D)用鼠标拖动窗口中的任何部位

15.计算机可以直接执行的程序是( )。

A)机器语言程序 B)C语言程序

C)高级语言程序 D)汇编语言程序

16.下列正确的转义字符是( )。

A)\1234 B)057

C)\' D)\\060

17.下列是C语言标识符的是( )。

A)Aa+ B)__123 C)a*bc D)b&cd

18.表达式1||2||3||4的值是( )。

A)1 B)2 C)3 D)4

19.设有int a=8,b,c;则执行b=c=a++;a=b==c;后,变量a的值是(

A)0 B)1 C)8 D)9 。 )

20.设int i=10,j=11,k=12,x=0;执行语句

if(i>5)

if(j>100)

if(k>11)

x=3;

else x=4;

else x=5;

429后x的值是( )。

A)0 B)3 C)4 D)5

21.若有以下说明,则对结构体变量exp10中成员std的引用不正确的是(

{

int std;

float std1;

}exp10,*p;

A)exp10.std B)example.std

C)p->std D)(*p).std

22.写出下列程序段的输出结果( )。

char a=9,b=020;

printf("%o\n",~a&b<<1);

A)00100000 B)8 C)040 D)077

23.根据下面的定义,能打印出字母M的语句是( )。

struct person 。struct example )

{

char name[9];

int age;

};

struct person class[10]={

"John",17,"Paul",19,"Mary",18,"Adam",16

};

A)printf("%c\n",class[3].name);

B)printf("%c\n",class[2].name[0]);

C)printf("%c\n",class[3].name[1]);

D)printf("%c\n",class[2].name[1]);

24.下面程序的输出是( )。

main()

{

int a[]={ 2,4,6},*prt=&a[0],x=8,y,z;

for(y=0;y<3;y++)

z=(*(prt+y)<x)?*(ptr+y):x;

printf("%d\n",z);

}

A)4 B)5 C)6 D)7

25.以下程序的输出结果是( )。

main()

{

int arr[10],i,k=0;

for(i=0;i<10;i++)

430 arr[i]=i;

for(i=1;i<4;i++)

k+=arr[i]=i;

printf('%d\n",k);

}

A)12 B)10 C)9 D)14

26.以下程序的输出结果是( )。

#define PR(ar) printf("%d",ar)

main()

{

int j,a[]={1,3,5,7,9,11,13,15},*p=a+5;

for(j=3;j;j--)

{

switch(j)

{

case 1:

case 2:PR(*p++);break;

case 3:PR(*(--p));

}

}

}

A)9910 B)9911 C)1235 D)9999

27.以下程序的输出结果是( )。

unsigned fun6(unsigned num)

{

unsigned k=1;

do {

k*=num%10;

num/=10;

}

while(num);

return(k);

}

main()

{

unsigned n=26;

printf("%d\n",fun6(n));

}

A)11 B)16 C)15 D)12

28.一个C程序由函数A、B、C和函数P构成,在函数A中分别调用了函数B和函数C,在函数B中调用了函数A,且在函数P中也调用了函数A,则可以说( )。

A)函数B中调用的函数A是函数A的间接递归调用

B)函数A被函数B中调用的函数A间接递归调用

C)函数P直接递归调用了函数A

431 D)函数P中调用的函数A是函数P的嵌套

29.参考下面函数f的定义:

f(int a){printf("%d",a);}

函数f的返回类型( )。

A)同参数a的类型相同 B)是void类型

C)没有返回值 D)无法确定

30.下列程序运行结果是( )。

#include<stdio.h>

#define M 3

#define N M+1

#define NN N*N/2

main()

{

printf("%d,",NN);

printf("%d\n",5*NN);

}

A)3,17 B)4,18

C)6,18 D)8,40

31.若有以下的说明,则对初值中字符 'a' 的引用方式为( 。 )

long fun5(int n)

{

long s;

if((n==1)||(n==2))

s=2;

else

s=n+fun5(n-1);

return(s);

}

main()

{

long x;

x=fun5(4);

printf("%ld\n",x);

}

A)9 B)8 C)2 D)16

32.若fp是指向某文件的指针,且已读到该文件的末尾,则C语言函数feof(fp)的返回值是(

A)EOF B)-1

C)非0值 D)NULL

33.设有如下定义:char *aa[2]={"abcd","ABCD"};则以下说法中正确的是( )。

A)aa数组成元素的值分别是"abcd"和ABCD"

B)aa是指针变量,它指向含有两个数组元素的字符型一维数组

。 )

432 C)aa数组的两个元素分别存放的是含有4个字符的一维字符数组的首地址

D)aa数组的两个元素中各自存放了字符'a'和'A'的地址

34.设变量定义为int A=5,B=6,表达式(++A==B--)?++A:--B的值是( )。

A)5 B)6 C)7 D)8

35.以下程序的输出结果是( )。

fun(int n,int *s)

{

int f1,f2;

if(n==1||n==2)

*s=1;

else

{

fun(n-1,&f1);

fun(n-2,&f2);

*s=f1+f2;

}

}

main()

{

int x;

fun(6,&x);

printf("%d\n",x);

}

A)8 B)9

C)3 D)7

36.阅读程序:

main()

{

char str1[]="how do you do",str2[10];

char *p1=str1,*p2=str2;

scanf("%s",p2);

printf("%s",p2);

printf("%s\n",p1);

}

运行上面的程序,输入字符串HOW DO YOU DO 则程序的输出结果是( )。

A)how do you do B)HOW do you do

C)HOW how do you do D)how HOW do you do

37.下列if语句中,不正确的是( )。

A)if(x>y); B)if(x==y) x+=y;

C)if(x!=y) scanf("%d",&x) else x=1; D)if(x<y) {x++;y++}

38.在下列选项中,没有构成死循环的程序段是( )。

A)int i=100;

while(1)

433 { i=i%100+1;

if(i>100)break;

}

B)for(;;);

C)int k=1000;

do{++k;}

while(k>=1000);

D)int s=36;

while(s);

--s;

39.以下程序运行后,输出结果是( )。

#include <stdio.h>

main()

{

int k=4,m=1,p;

p=func(k,m); printf("%d,",p);

p=func(k,m); printf("%d \n",p);

}

func(int a,int b)

{

static int m=0,i=2;

i+=m+1;

m=i+a+b;

return m;

}

A)9,16 B)9,17 C)8,16 D)8,17

40.执行下列程序时输入:123<空格>456<空格>789<回车>,输出结果是( )。main()

{

char s[100];

int c,i;

scanf("%c",&c);

scanf("%d",&i);

scanf("%s",s);

printf("%c,%d,%s\n",c,i,s);

}

A)123,456,789 B)1,456,789

C)1,23,456,789 D)1,23,456

41.以下程序运行后,输出结果是( )。

func()

{

int m=0,i=2;

i+=m+1;

m=i+a+b;

434 return(m);

}

main()

{

int k=4,m=1,p;

p=func(k,m);printf("%d,",p);

P=func(k,m);printf("%d\n",p);

}

A)8,15 B)8,16 C)8,17 D)8,8

42.在以下一组运算符中,优先级最高的运算符是( )。

A)<= B)= C)% D)&&

43.以下对枚举类型名的定义中正确的是( )。

A)enum a={one,two,three};

B)enum a {one=9,two= -1,three};

C)enum a={"one","two","three"};

D)enum a {"one","two","three"};

44.变量a所占内存字节数是( )。

union U

{

char st[4];

int i;

long l;

};

struct A

{

int c;

union U u;

}a;

A)4 B)5 C)6 D)8

45.以下程序输出的最后一个值是( )。

int ff(int n)

{

static int f=l;

f=f*n;

return f;

}

main()

{

int i;

for(i=1;i<=5;i++)

printf("%d\n",ff(i));

}

A)140 B)105 C)120 D)100

46.设i,x都是int类型,则下面的for循环体可执行(

435 for(i=0,x=0;i<=9&&x!=876;i++)

printf("*");

)次。

A)9次 B)876次 C)10次 D)无限循环

47.有语句:

char str1[10],str2[10]={"books"};

则能将字符串books赋给数组str1的正确语句是( )。

A)str1={"Books"}; B)strcpy(str1,str2);

C)str1=str2; D)strcpy(str2,str1);

48.以下程序的输出结果是( )。

#define M(x,y,z) x*y+z

main()

{

int a=1,b=2,c=3;

printf("%d\n", M(a+b,b+c,c+a));

}

A)19 B)17

C)15 D)12

49.整型变量x和y的值相等、且为非0值,则以下选项中,结果为0的表达式是(

A)x || y B)x | y

C)x & y D)x ^ y

50.以下选项中,不能正确赋值的是( )。

A)char s1[10];s1="Ctest";

B)char s2[]={'C','t','e','s','t'};

C)char s3[20]="Ctest";

。 )

D)char *s4="Ctest\n";

二、填空题(每空2分,共40分)

请将每一个空的正确答案写在答案卡上,答在试卷上不得分。

1.结构化程序设计所规定的三种基本控制结构是(1)结构、选择结构和循环结构。

2.为了将当前盘当前目录中的所有文本文件(扩展名为.TXT)的内容打印输出,正确的单条DOS命令为(2)。

3.设当前为C盘,现要用一条DOS命令在打印机上输出A盘当前目录下所有扩展名为.DAT的文件内容。这一条DOS命令应为(3)。

4.设当前盘为C盘。为了在A盘的当前自录\USER下建立一个新的子目录X,正确的DOS命令为(4)。

5.在Windows中,为了终止一个应用程序的运行,首先单击该应用程序窗口中的控制菜单框,然后在控制菜单中单击(5)命令。

436 6.表达式10<<2+1的值为(6)。

7.设有FILE *fp;对fp对应的二进制文件f.dat只读打开的语句是(7)。

8.以下定义的语句:

int a[3][2]={10,20,30,40,50,60},(*p)[2];

p=a;

则*(*(p+2)+1)值为(8)。

9.设有short s= ?1;则执行printf(“%ho”,s);后的输出是(9)。

10.若x为int类型,请以最简单的形式写出与逻辑表达式!x等价的C语言关系表达式(10)。

11.函数void fun(float *sn,int n)的功能是:根据以下公式计算S,计算结果通过形参指针sn传回;n通过形参传入,n的值大于等于0,请填空。

void fun( float *sn,int n)

{

float s=0.0,w,f=-1.0;

int i=0;

for(i=0;i<=n;i++)

{

f=(11)*f;

w=f/(2*i+1);

s+=w;

}

(12)=s;

}

12.以下函数用来在w数组中插入x,w数组中的数已按由小到大顺序存放,n 所指存储单元中存放数组中数据的个数。插入后数组中的数仍有序。请填空。

void fun(char *w,char x,int *n)

{

int i,p;

p=0;

w[*n]=x;

while (x>w[p])

(13);

for(i=*n;i>p;i--)

w[i]=(14);

w[p]=x;++*n;

}

13.下面的程序将一个任意整数转换成二进制数,并把它打印出来。例如从键盘上输入14,就能打印出0000000000001110。请填空。

#include<stdio.h>

main()

{

unsigned int y=(15);

int x;

437 scanf("%d",&x);

do

{

printf("%c",x&y?1:0);

(16);

}

uchile(17);

}

14.下面的程序把键盘输入的字符存放到一个文件中,用“#”作为结束标识符,请填空。

#inlude<stdio.h>

main()

{

FILE *fp;

char ch,fname[10];

printf("Input the name of file\n");

gets(fname);

if((fp=fopen(18))==NULL)

{

printf("Can not open\n");

(19);

}

while((ch=getchar())!='#')

fputc(20);

fclose(fp);

}

三、笔试模拟总结表

1.模拟得分:

2.得到该成绩的原因:

3.问题汇总(请填写“问题”、“是否解决”和“解决办法”):

4.对自己说点什么吧:

438 第 16 章 上机考试指导

考纲要求:

1.在指定的时间内使用微机完成下述操作。

2.完成指定的计算机基本操作(包括机器启动和操作命令的使用)。

3.按给定要求编写和运行程序。

4.调试程序,包括对给出的不完善的程序进行修改和补充,使之能得到正

确的结果。

MS-DOS操作系统

【MS-DOS操作系统简介】

大家知道,“DOS”来源于磁盘操作系统英文名称“Disk Opreating System”的缩写,也就是说,它是以磁盘为基础的操作系统。自19xx年IBM PC推出其第一个操作系统DOS 1.0以来,DOS几经修改,新版本不断推出,但每个新版本对于以前的版本几乎是全兼容的。区别就是新版本增加和完善了各种功能,以适应PC机系列逐渐扩充和发展的需要。

大量厂家在DOS环境下开发了极为丰富的软件,在该环境下加入有关汉字输入输出的程序、字库以及文件后就形成了所谓的汉字操作系统,如CCDOS、UCDOS,等等。

进入DOS系统

Windows操作系统同样提供了DOS系统,在Windows系统下使用DOS系统有多种方法。

如果你使用的是Windows 98系统,可以在开始程序中找到MS-DOS方式,单击它后就可以进入Windows环境下的模拟DOS方式。注意这时进入的并不是纯粹的DOS系统,但对于学习DOS的使用来说,在这种环境下就可以了。另外一种进入DOS的方式是单击“关闭系统”选择“重新启动计算机并切换到MS-DOS方式”(见图16-1所示)。如果读者有DOS的安装盘的话还可以自己安装一个纯粹的DOS系统。

如果你使用的是Windows 2000、NT或者XP系统,因为这些系统都已经完全脱离了DOS的基础而是基于NT技术构建,所以在这些系统中将找不到类似于Windows 98系统中的“重新启动计算机并切换到MS-DOS方式”。但为了方便用户使用,这些系统仍然保留了“命令提示符”这样的模拟DOS系统,使用“命令提示符”也同样可以达到学习DOS

439的目的。

图16-1 进入MS-DOS方式

不论哪种DOS系统,其命令都是兼容的。下面就以Windows 2000系统中的命令提示符为例来说明DOS系统。进入DOS可以看见下面的界面(如图16-2所示):

图16-2 模拟方式下的DOS进入界面

熟悉DOS界面

下面说明该界面的一些符号的含义和用法。首先可以看到下面的文本:

Microsoft Windows 2000 [Version 5.00.2195]

(C) 版权所有 1985-2000 Microsoft Corp.

D:\>

在Windows 98操作系统的DOS窗口中或者纯DOS界面中均可以看到类似下面的文字:

Microsoft(R) Windows 98

(C)Copyright Microsoft Corp 1981-1999.

C:\WINDOWS>

前面的 Microsoft 说明该操作系统版权属于美国微软公司。下面则属于命令行。其中C:\>是系统命令提示符,为用户提供了一个输入命令的环境。用户可以在光标闪烁的位置键入一条DOS命令并按回车键(Enter),通知DOS接受并执行所键入的命令。光标是一个闪烁的下划线字符“-”,它指示当前键入字符的位置。

在窗口的标题按钮处右击即可以看到有“编辑”选项和“属性”选项(如图16-3所示)。

440在“编辑”选项下还可以看到如图16-4所示的命令。

图16-3 右击菜单选项 图16-4 编辑菜单

编辑菜单下的标记、复制、粘贴等选项可以使得DOS 窗口下的操作如同在Windows环境下的操作一样,具有简单方便和操作直观的特点。

属性选项则可以使得用户能够按照自己的意愿来设置DOS环境,比如窗口下的字体、字体大小、屏幕缓冲区大小、窗口大小以及屏幕背景和屏幕文字的显示颜色,等等。

设置汉化DOS系统

DOS 系统本身并没有中文版本,一些厂家为其制作了汉化版本,例如 CCDOS 和UCDOS。在普通的DOS系统下(非窗口形式),汉字是无法显示的,将以乱码的形式出现。由于Windows 2000及其以上版本对DOS做了改进,成为了模拟DOS的“命令提示符”,所以它在全屏幕时也可以显示汉字,但这与纯粹的DOS系统是有所区别的。

在DOS命令提示符C:\>后键入以下命令(确定已经安装了UCDOS):

C:\>cd ucdos

C:\ucdos>ucdos

将进入UCDOS的汉化界面,如图16-5所示。关于UCDOS的使用方法可以参考UCDOS手册,在此不作介绍。

图16-5 UCDOS的汉化界面

【DOS命令综述】

441 DOS命令对一个新手来说好像非常复杂,但如果仔细研究一番就会发现,其实这些命令是有一定规律的。下面就对DOS命令作一个概述。

DOS命令的语法规则

DOS命令以特定的字符串开头,以回车键结束。有些命令必须要求带有某些项目或者参数,有些可以不要参数,这对于不同的命令有不同的规定。命令中除了有极少数不能在命令行中运行外,绝大部分可以通过键盘输入操作来执行。命令的语法格式一般由三部分组成:

命令名称 [操作对象] [命令选择项目]

其中的命令选择项目也可以称为切换参数或者命令开关。命令名称代表欲进行的DOS操作,它与系统中一个同名的程序文件相对应。操作对象是命令的附加部分,用于指定命令所涉及的计算机设备或者目标文件及其所在的位置。命令选择项目用来规定该命令具体的执行特征。

DOS命令的文件说明

文件说明是对于DOS命令操作对象的简单概括。文件说明可以包括文件名(主文件名和扩展名)、文件所在的目录以及文件所隶属的逻辑磁盘驱动器。例如下面的定义:

[盘符][目录路径]主文件名[.扩展名]

文件所属的盘符通常以“A:”、“B:”、“C:”、“D:”等形式出现。目录名或者子目录名称前面用反斜线“\”来表明从属或者嵌套的关系,并由此构成多级目录检索路径。

文件名由主文件名和文件扩展名两部分组成。

DOS并不介意用户在键入命令和文件名时所用的字符是大写还是小写,只要符合文件名命名规则就可以。

DOS命令的分类

按照运行机制,可以将DOS命令分为内部命令和外部命令两大类。

内部命令(Internal Command)随操作系统调入内存后常驻内存,包含在命令解释处理程序Command.com中,随时可以被调用,运行速度快。内部命令要求系统执行一些最基本的操作,比如列文件目录(DIR),查看文件内容(TYPE),复制文件(COPY),改变文件名(REN),删除文件(DEL)等。系统接到命令后首先检查该命令是否是Command.com中的命令,如果是并且符合规则就去执行该命令对应的程序。内部命令可以直接从命令行运

行,但一般不出现在目录列表中。

外部命令(External Command)平时驻留在磁盘上,作为独立的DOS可执行命令程序文件存在。使用时必须在文件说明中指明其搜索路径,由系统将其自磁盘驻存位置暂时调入内存中的自由空间。外部命令的执行速度相对内部命令来说稍慢一些。大多数外部命令使用过后就立即释放其占用的内存空间,有一小部分外部命令使用后并不立即释放其占用的空间,这就是所谓的TSR(Terminate and Stay Resident)“内存驻留”程序。外部命令可以具有“.COM”、“.BAT”和“.EXE”扩展名,其对应的程序文件可以出现在“DIR”目录列表中,可以直接从命令行运行。DOS系统提供的外部命令仅仅是DOS外部命令的一部

442分,更多的外部命令是由用户程序或其他软件系统在各自程序的基础上提供的。DOS执行命令的先后顺序是内部命令、.COM文件、.EXE文件和.BAT文件。

443 DOS命令详解

下面将详细介绍DOS命令的使用方法。因为DOS命令非常多,但其中常用的并不是很多,所以这里只选择常用的命令分类讲述。如果读者想要使用某一个命令而本篇又没有讲述的话,可以参考DOS命令手册。其实,只要读者能够熟练掌握基本的DOS命令,就可以顺利地使用DOS系统,再遇到复杂生僻的DOS命令就可以通过查阅相关资料来掌握。

考虑到叙述的方便和读者学习的系统性,本篇从下面几个方面来介绍DOS命令:文件管理、目录管理、内存管理、磁盘管理、服务工作命令和其他一些方面的DOS命令。

注意,DOS 的各种命令均自带了帮助,在各个命令后面加入“/?”就可以得到帮助。即如下面形式command_name/?可以得到诸如命令的格式、命令功能详述以及可能带的参数等信息。比如在命令COPY后加入/?可以看到如图16-6所示的信息。

图16-6 type命令的帮助

DOS服务工作命令

DOS服务工作命令包括

PROMPT、VER、COLOR、CLS、DATE、TIME

【PROMPT(内部命令)】

功能

根据指定的要求改变系统提示符。

格式

PROMPT [text]

说明

其实系统命令提示符是可以改变“形象”的。用户可以使用DOS命令“PROMPT”来设定系统命令提示符,格式如上所示。如果在没有参数的情况下使用,prompt将命令提示符重置为默认设置,当前驱动器盘符后跟着当前目录和大于符号(>)。[text]指定要包含在系统提示符中的任何文本和信息。比如$q为=(等号);$t为当前时间;$d为当前日期等等。具体的字符代表什么意思,可以通过“/?”命令来查看。

示例

444 命令PROMPT $n $q $t可以将提示符变成如图16-7所示的效果:

图16-7 改变命令提示符

【VER(内部命令)】

功能

在屏幕上显示当前操作系统的版本号。

格式

VER

说明

该命令显示的是当前操作系统的版本号,如果读者是在Windows 98系统的模拟DOS下运行该命令则得到的是Windows的版本号,如果是在2000、XP系统下则显示的是该两者的版本号。如果是在纯DOS系统下则显示DOS系统的版本号。

示例

在Windows 2000下运行该命令得到如图16-8所示的效果:

图16-8 VER命令的显示效果

【COLOR(内部命令)】

功能

设置默认的屏幕背景色和文本前景色。

格式

COLOR bf

说明

b是指定背景色的十六进制数字;f指定前景色。比如,0代表黑色;1代表蓝色;A代表浅绿;F 代表亮白色。如果没有给定参数,该命令将颜色还原成系统默认的颜色。如果指定的前景和背景值相同,color将返回ERRORLEVEL 1。注意该命令只能在Windows2000以上的版本中使用。

445 示例

读者可以键入下面的命令COLOR fc查看效果(略)。

color 1F

【CLS(内部命令)】

功能

清除屏幕上的所有内容。

格式

CLS

说明

清除后的屏幕只显示命令提示符和提示光标。

示例

清屏前后的效果如图16-9所示。

图16-9 清屏前后的效果图

【DATE(内部命令)】

功能

显示当前日期并设置系统时间。

格式

DATE [/T|date]

说明

仅键入DATE而不加参数,可以显示当前日期设置,并且提示输入新的日期。按Enter键即可保持原有日期。

示例

在DOS提示符下键入该命令,可以看见如图16-10所示效果。

446图16-10 DATE命令效果图

447【TIME(内部命令)】

功能

显示系统时间或设置计算机的内部时钟。

格式

TIME [hours:[minutes[:seconds[.hundredths]]][A|P]]

说明

如果在没有参数的情况下使用,time将显示计算机的时钟时间并提示输入新时间。按回车键保持时间不变,或使用上述语法键入新时间。hours指定小时,有效值范围为0~23;minutes指定分钟,有效值范围为0~59;seconds指定秒数,有效值范围为0~59;hundredths指定百分之一秒,有效值范围为0~99。A|P指定12小时时间格式为A.M或P.M。如果键入了有效的12小时时间,但没有键入A或P,time使用将A代表A.M。

示例

TIME命令的效果如图16-11所示:

图16-11 TIME命令的效果图

DOS文件管理命令

DOS文件管理命令包括

TYPE、COPY、XCOPY、DEL、REN、ATTRIB

【TYPE(内部命令)】

功能

显示文本文件的内容。

格式

TYPE [drive:][path]filename

说明

使用type命令查看文本文件而不修改文件。指定要查看的一个或多个文件的位置和名称,用空格分开多个文件名。并且需要注意,type命令只能查看文本文件,如果查看其他类型的文件是没有意义的。

448 示例

查看E驱动盘下的名为key.txt(内容为:指针就是变量的地址)和tc.txt(内容为:它是一个特殊的变量)的两个文件,可以使用命令:

type e:\key.txt e:\tc.txt

得到如图16-12所示的效果。

图16-12 查看文件结果图

【COPY(内部命令)】

功能

将一个或多个文件复制到其他位置。

格式

COPY source destination

说明

source指定要从其中进行复制的文件或文件集的位置和名称,可以由驱动器号和冒号、文件夹名、文件名或组合所组成。

destination指定要复制到其中的文件或文件集的位置和名称,它的组成跟source类似。

如果要复制多个文件,那么可以指定单个目标文件和多个源文件(源文件可以使用通配符或file1+file2+file3)。需要注意的是,如果是复制多个文件,并不是将这些文件分别复制到目标文件夹,而是将所有的文件进行合并,最后在目标文件夹中得到的是一个与第一个源文件同名(未指定文件名)或者指定文件名的文件,而它的内容包括所有源文件的内容。

示例

例如要将E:\key.txt文件复制到E:盘的copy文件夹下并命名为key1.txt,则可以使用下面的命令。

copy e:\key.txt e:\copy\key1.txt

再例如要将E:盘下的key.txt(内容为:指针就是变量的地址)和tc.txt(内容为:它是一个特殊的变量)两个文件复制到E:盘的copy文件夹下并命名为key1.txt,则可以使用下

449面的命令。

copy e:\key.txt+e:\tc.txt e:\copy\key1.txt

可以使用命令type来查看文件key1.txt的内容,结果如图16-13所示。

图16-13 COPY命令的结果图

【XCOPY(内部命令)】

功能

复制文件和目录,包括子目录。

格式

XCOPY source [destination]

说明

source指定要复制的文件的位置和名称,该参数必须包含驱动器或路径。destination指定要复制的文件的目标,该参数可以包含驱动器盘符和冒号、目录名、文件名或者组合。

如果要复制多个文件,那么可以指定多个源文件(源文件可以使用通配符或file1+file2+file3)。

示例

如果要想将E:盘下的名为TC20的文件夹复制到E:盘的copy文件夹的同名文件夹下,可以使用如下命令。

xcopy e:\tc20 e:\copy\tc20

xcopy和copy命令的不同之处在于:copy命令只能用来复制文件而不能复制文件夹或者目录,xcopy不但能复制普通的文件,还能复制文件夹和目录。

【DEL(内部命令)】

功能

删除一或数个文件。

格式

450 DEL [drive:][path] filename

说明

[drive:][path]filename指定一个或数个文件或目录列表。通配符可被用来删除多个文件。如果指定了一个目录,目录中的所有文件都会被删除。

del 命令是非常危险的一个命令,除非你已经确定要删除某一个文件才能使用这个命令。因为文件一旦删除,就不会恢复了。

示例

例如将E:盘下的key.txt文件删除,可以使用下面的命令。

del e:\key.txt

如果要查看是否将文件删除,可以使用命令dir来查看。关于dir命令,将在下面讲述。【REN(内部命令)】

功能

更改一个文件或一组文件的名称。

格式

REN [drive:][path]filename1 filename2

说明

[drive:][path]filename1指定要重命名的文件或文件集的位置和名称,filename2为文件指定新名称。如果使用通配符(* 和 ?),filename2 为多个文件指定新名称,重命名文件时不能指定新驱动器或路径。

注意,使用ren命令可以重命名符合指定文件名的所有文件,但不能用来重命名驱动器之间的文件或者将文件移动到不同的目录位置。另外ren命令也可以写成rename。

示例

可以使用ren命令来为E:盘下的文件key.txt重命名为key1.txt,然后再将其改回原来的名字,可以使用命令如下所示。

ren e:\key.txt key1.txt

【ATTRIB(外部命令)】

功能

显示或更改文件属性。该命令显示、设置或删除指派给文件或目录的只读、存档、系统以及隐藏属性。

格式

451 ATTRIB [+r|-r] [+a|-a] [+s|-s] [+h|-h]

[[drive:][path]filename] [/s[/d]]

说明

+是为文件设置属性,-是将原来的属性清除。比如+r是设置只读属性,-r则是清除只读属性。r为只读文件属性,a为存档文件属性,s为系统文件属性,h为隐藏文件属性。[[drive:][path]filename]指定要处理的目录、文件或文件集的位置和名称。可以在 filename参数中使用通配字符(?和*)显示或更改一组文件的属性。

/s处理在当前目录及其全部子目录中的匹配文件,/d处理目录。

示例

将E:盘下的名为key.txt的文件改变属性,设定为只读属性和存档文件属性,可以使用下面的命令。

attrib e:\key.txt +r +a

设置完成后可以使用下面的命令来查看该文件的属性,结果如图16-14所示。

attrib e:\key.txt

图16-14 设置并且查看文件的属性

DOS目录管理命令

DOS目录管理命令包括

DIR、MD、CD、RD、TREE、PATH

【DIR(内部命令)】

功能

显示目录中的文件和子目录列表。

格式

DIR [drive:][path][filename]

说明

如果在没有参数或开关的情况下使用,则dir显示磁盘的卷标和序列号,后接磁盘上目录和文件列表,包括它们的名称和最近修改的日期及时间。dir可以显示文件的扩展名以及文件的字节大小。dir也显示列出的文件及目录的总数、累计大小和磁盘上保留的可用空间

452(以字节为单位)。[drive:][path]指定要查看其列表的驱动器和目录;[filename]指定要查看其列表的特殊文件或文件组。可以使用多个文件名。文件名可以使用空格、逗号或分号分开。可以在filename参数中使用通配字符(?和*)显示一组文件。

示例

例如显示E:盘中的文件和文件夹信息,可以使用下面的命令。

dir e:

如果想查看E:盘中的某一个文件的信息,比如文件key.txt的信息,可以使用下面的命令。

dir e:\key.txt

需要注意的是,dir命令有两个非常有用的开关/p和/w。/p表示每次显示一个列表屏幕,要查看下一屏需要按键盘上的任意键;/w则表示以宽格式显示列表,在每一行上最多显示5个文件名或目录名。例如下面的用法。

dir e:/p

【MD(内部命令)】

功能

创建目录或子目录。

格式

MD [drive:]path

说明

[drive:]指定要在其中创建新目录的驱动器;path指定新目录的名称和位置,单个路径的最大长度由文件系统决定。该命令也可以写成MKDIR。

示例

在E:\tc文件夹下创建一个目录copy,可以使用如下的命令。

md e:\tc\copy

可以使用命令dir查看结果。

【CD(内部命令)】

功能

显示当前目录名称,或者更改当前的文件夹。

格式

CD [/D] [drive:][path]或者CD [..]

453 说明

如果在没有参数的情况下使用,则cd显示当前驱动器和文件夹的名字。如果只与驱动器号一起使用(例如

cd C:),cd将显示指定驱动器上的当前目录。

使用/D命令选项,除了改变驱动器的当前目录之外,还可改变当前驱动器;[drive:][path]指定要更改的驱动器(如果不是当前驱动器)和目录;[..]指定要更改成为的父文件夹。该命令可以写成CHDIR。

示例

比如,当前的文件夹是E:\tc\copy,需要转到目录E:\可以使用下面命令。

cd e:\

效果如图16-15所示。

图16-15 CD命令效果图

【RD(内部命令)】

功能

删除一个目录。

格式

RD [drive:]path [/s] [/q]]

说明

[drive:]path指定要删除的目录的位置和名称,[/s]删除指定目录和所有子目录以及包含的所有文件,[/q]在安静模式中运行 rd 并且不经确认即删除目录。该命令也可以写成RMDIR。

示例

删除E:\tc\copy目录,即将名为copy的文件夹删除,命令如下所示。

rd e:\tc\copy

【TREE(外部命令)】

功能

以图形显示驱动器或路径的文件夹结构。

454 格式

TREE [drive:][path] [/F] [/A]

说明

[drive:]指定包含要显示目录结构的磁盘的驱动器;[path]指定要显示目录结构的目录;[/F]显示每个文件夹中文件的名称;[/A]使用ASCII字符,而不使用扩展字符。

示例

显示E:\tc路径的文件夹结构,可以使用如下命令。

tree e:\tc

其结果如图16-16所示。

图16-16 tree命令的结果图

【PATH(内部命令)】

功能

为可执行文件显示或设置一个搜索路径。

格式

PATH [[drive:]path[;...][;%PATH%]

说明

如果在没有参数的情况下使用,则path显示当前的搜索路径;[drive:]path指定要搜索的驱动器、目录和任何子目录;将%PATH%包括在新的路径设置中会将旧路径附加到新设置中。

示例

比如在命令行键入path命令而不带任何的参数就可以得到当前的搜索路径,如图16-17所示。

图16-17 path命令的效果图

DOS磁盘管理命令

455 DOS磁盘管理命令包括

FORMAT、DISKCOPY、CHKDSK

【FORMAT(外部命令)】

功能

格式化指定卷中的磁盘。

格式

FORMAT volume

说明

volume指定驱动器(后面跟一个冒号)、装入点或卷名。该命令可以带有开关,比如/V指定卷标;/Q执行快速格式化,等等。如果需要可以查看DOS手册中的相关命令。

示例

将软盘A格式化,可以使用命令format A:。

【DISKCOPY(外部命令)】

功能

将源驱动器中软盘的内容复制到目标驱动器中已格式化或未格式化的软盘上。

格式

DISKCOPY [drive1: [drive2:]] [/V]

说明

drive1指定包含源盘的驱动器,drive2指定包含目标盘的驱动器。开关/V是校验信息,校验复制得是否正确。另外,两张软盘的类型必须相同。

示例

该命令的源盘驱动器和目标盘驱动器可以是同样的驱动器,比如下面的命令是正确的。

diskcopy a: a:

【CHKDSK(外部命令)】

功能

基于所用的文件系统,创建和显示磁盘的状态报告,Chkdsk也可以用来列出并纠正磁盘上的错误。

456 格式

CHKDSK [volume[[path]filename]]]

说明

如果chkdsk不能锁定驱动器,则它将在下一次重启计算机时检查该驱动器。如果在没有参数的情况下使用,则chkdsk显示当前驱动器中的磁盘状态。volume指定驱动器(后面跟一个冒号)、装入点或卷名;[path]filename指定需要 chkdsk 检查碎片整理的文件或文件集的位置和名称,使用通配字符(* 和 ?)可以指定多个文件。

示例

例如可以用该命令来检查软盘驱动器中A盘的状态,命令如下所示。

chkdsk a:

457【批处理】

批处理程序是无格式的文本文件,它包含一条或多条命令,文件扩展名为.bat 或.cmd。当在命令提示符下键入文件名时,文件中的命令将顺序执行。批处理程序也被称为批处理文件。

如果有一组命令经常使用,可以把这些命令放到一个批处理文件中,每次打入批处理文件名,就可以使系统执行所需要的操作,而不必再一个个打入这些命令。

在批处理文件中,除了包含用户欲执行的DOS主命令外,还可以包含为构成特定执行过程的控制转移所需要的子命令,这些特殊的DOS内部命令,被称为批处理子程序或者简称为批处理命令。

批处理命令用于批处理文件的流程控制以及信息显示控制。最常用的命令有下面几个:ECHO、GOTO、IF、FOR和CALL。下面讲述它们的用法。

ECHO

其用法为:

echo [on|off] [message]

功能是打开或关闭请求回显功能,或显示信息。其中的[on|off]指定是否打开命令回显功能,如果要显示当前的回显设置,可以使用不带参数的echo命令。

GOTO

其用法为:

goto label

功能是定向到由指定标签标记的批处理程序的行中。例如GOTO END表示由此转向批处理文件中的标号行:END。批处理文件的标号行以冒号开始,后面以一个标识符作为标号,该行没有其他内容,只作为转移的目的地。

IF

功能是在批处理程序中执行条件处理。如果if命令中指定的条件为真,将执行该条件后的命令。如果条件为假,将忽略if子句中的命令,并执行else子句中的任何命令(如果已经指定了命令)。例如,IF EXIST TMP.TXT DEL TMP.TXT,规定如果存在文件TMP.TXT就执行后面的删除文件命令DEL TMP.TXT。

FOR(FOR-IN-DO)

其用法为:

for %%variable in (set) do command[command-parameters]

功能是重复执行DO之后的命令。%%variable代表可替换的参数,(set)指定要用指定的命令处理的一个或多个文件或文本字符串,command指定要在指定的set所包含的每个文件上执行的命令,command-parameters指定要用于指定命令(如果指定的命令要使用任何参数或开关)的任何参数或开关。

458 CALL

其用法为:

call [drive:][path] filename [batch-parameters]

功能是从一个批处理程序调用另一个批处理程序,而不使其上一级批处理程序停止。[drive:][path] filename指定要调用的批处理程序的位置和名称;batch-parameters指定批处理程序所需要的任何命令行信息。

批处理文件运行命令的格式是:

[盘驱动符][目录路径]批处理文件主名[.BAT] [批处理工作参数]

其中[盘驱动符][目录路径]是可选的。用户最先知道的批处理文件是AUTOEXEC.BAt,它的作用是在系统开始

时设定DOS的工作环境。该批处理文件又称为自动批处理文件。该文件可以使系统“自动”进入特定的应用系统。

【输入输出改向】

基本概念

键盘和显示器作为“标准输入”和“标准输出”,当需要以文件或者其他设备作为输入输出对象时,要采用输入输出转向,也称为重定向。

例如,系统执行命令DIR时是将当前目录的目录清单显示在作为标准输出的显示屏幕上,执行命令DIR>A:ONEDIR则是将目录清单写入A盘文件ONEDIR中,命令DIR>PRN则是在打印机上输出目录清单。

DOS指定了某些特定的名字作为标准设备的文件名,用来代表作为重定向对象的外部设备。比如下面的文件名所代表的设备如下所示。

CON:控制台,即键盘输出或者屏幕输出。

AUX,COM1:第一个串口。

COM2、COM3、COM4:第二、三、四个串口。

PRN,LPT1:第一个打印机,只能作为输出。

LPT2、LPT3:第二、三个打印机,只能作为输出。

NUL:空设备,即空文件,某些应用系统要求使用时给出输出文件名,而用户实际上并不需要这个输出,就可以给出NUL。

输入输出重定向

通常的DOS命令后所处理的信息是由标准输入设备输入的,当然也可以这些信息预先写入某一个文件,然后再由文件输出,这样一来,如果程序需要输入较多的数据就可以通过文件输入,从而提高了执行效率。例如:

TCfile<DATA

其中TCfile.COM是所要执行的文件,它所要处理的信息是通过文件DATA读入的。

459符号“<”是输入重定向操作符,其后的文件名或者设备名是重定向的输入源。

输出重定向符号是“>”或者“>>”,在其后列出指定的文件名或者标准设备文件名作为输出。“>”和“>>”的不同之处在于:在“>”之后的文件需要重新建立,而在“>>”之后的文件则可以是已经存在的文件名,需要输出的内容续接在该文件的原有内容之后。

例如执行下面的命令。

D:\>TREE>FILE

则D盘的树形目录结构将输出到新建的文件FILE中,并不在屏幕上显示。

输入输出重定向可以同时使用,比如下面的命令。

D:\>SORT<DATA>SORT.DAT

文件DATA中存放着若干的数据,可以通过上面的命令将这些数据按照字典的顺序排序后放入文件SORT.DAT中。

打印输出

命令TYPE可以在接通打印机或输出转向打印机的情况下打印输出指定的文件内容,比如,命令TYPE FILE1>PRN可以得到文件FILE1的打印文本。

命令PRINT可以执行后台打印,即在系统执行其他任务时打印输出由该命令列举的文件内容。

管道

在DOS命令行中,可以出现用竖线字符“|”分开的多个命令,这时是将符号“|”之前的命令输出,同时作为“|”之后的命令输入,这就是所谓的“管道功能”。其中“|”是管道操作符。

例如,命令DIR|FIND"COM">COMDIR即利用了管道功能。DIR命令的输出是当前目录的列表,但它不出现在屏幕上而是成为FIND命令的输入。FIND命令在输入文件中寻找指定字符串"COM",并且将包含该字符串的行输出。

管道功能可以理解为将若干个命令用输入输出“管道”串连在一起。

Windows常用命令和操作

在二级考试中,不仅要求熟悉DOS常用命令和操作,对Windows的基本操作也要求比较熟练。在新增的Visual FoxPro、Visual Basic科目的上机考试中尤其如此。

【Windows概述】

Windows最初由Microsoft公司于19xx年11月开始研制,在19xx年11月Microsoft公司推出了最早的美国国内版——Windows 1.01 版,经过十几年的发展又陆续推出了

460Windows 3.1/3.2/95/98/2000/ME/XP等系列。Microsft Windows能够充分发挥计算机的作用,其图形接口能够组织用户程序和文件,同时运行几个用户程序,在文档之间移动和复制信息,在平台上进行应用程序的切换等。

Windows的基本特点包括:

(1)友好的图形窗口界面、通用的菜单和对话框,使操作形象化、简便化。

(2)可以同时运行多个应用程序。

(3)可以使用16MB以上的内存空间,大大挖掘了计算机的潜力。

(4)支持TrueType 字体技术,使字体无变形失真。

(5)支持对象链接和嵌入技术(OLE),可将一个应用程序嵌入到另一个中。

(6)支持声音、音乐等多媒体技术。

【Windows基本要素及其基本操作】

鼠标

(1)鼠标的组成

鼠标分两键鼠标和三键鼠标。Windows使用两键鼠标。其基本键为左键,用于大部分的鼠标操作。第二键为右键。通常忽略鼠标的中间键。除非特殊说明,后面提到的鼠标键都是指鼠标的基本键。

(2)指针的形状

在屏幕上出现的对应于鼠标的对象称为指针。指针的形状依赖于它与屏幕上的其他对象所处的关系。常见鼠标指针的形状及其意义如下:

标准选择 帮助选择

后台操作 忙指针

精度选择 文字选择

手写 不可用

调整垂直大小 调整水平大小

左上右下对角线调整 左下右上对角线调整

移动 其他选择

链接选择

(3)鼠标的操作

鼠标的基本操作分为以下5种。

① 指向:方法是移动鼠标,将指针移到屏幕上的指定位置。

② 单击:方法是按下并释放鼠标键。

③ 选中:方法是将鼠标指向对象,并单击鼠标。

④ 双击:方法是在很短的时间间隔内按下并释放鼠标键两次。

⑤ 拖动:方法是按住鼠标键,将鼠标移到屏幕上的指定位置后,再释放鼠标键。

461 没有用过鼠标的人,需要反复练习上述的鼠标基本操作。练习时,必须手眼配合,才能灵活自如。

图标

Windows环境是由许多窗口和小的图形对象组成的。这些小的图形对象,叫做图标。所有图标都排列在一个背景上,叫做平台。每一个图标都是由图形符号和名称组成的。以“程序管理器”组图标为例,其名称是“程序管理器”,而图形符号在其名称上方。

图标可以分成两大类。一类是程序项图标(或称小图标),另一类是程序组图标(或称组图标)。组图标包含若干个程序项图标,也可以再包含若干个组图标。

图标的选中:方法是将鼠标指向需要选择的图标的任意位置,并单击鼠标。选中的图标被称为活动图标。活动图标的名称具有高亮度,且每次只能有一个活动图标。

图标的移动:方法是将鼠标指向需要移动的图标的任意位置,并拖动鼠标到指定位置。

窗口

在Windows环境下,所有工作均是在窗口中进行的。Windows中的窗口与图标有着很大的相关关系。一方面,一个图标可以打开成一个窗口,一个组图标可以打开成一个组窗口,一个窗口也可以缩小成一个图标,一个组窗口也可以缩小成一个组图标。另一方面,一个程序组图标打开的窗口中可以包含若干个窗口,一个窗口中可以包含若干个程序组图标或程序项图标。虽然Windows允许同时在屏幕上打开多个不同的窗口,但是太多的打开窗口会使平台显得杂乱而无秩序。组图标和程序项图标有助于保持应用程序井然有序,易于执行。一个组图标可以容纳许多程序项图标,当需要执行一个应用程序时,可以迅速打开其程序项图标,使其成为一个窗口。

(1)窗口的组成

典型的Windows窗口主要由标题栏、菜单栏、系统菜单框、最小化按键、最大化按键(恢复按键)、缩放边框和滚动条等组成。

① 标题栏。它总是出现在窗口的顶部,用于显示窗口的名称。拖动标题栏还可以移动整个窗口。

② 菜单栏。它总是出现在标题的下面,包括一系列可执行的菜单名。

③ 系统菜单框。

④ 最小化按键。

⑤ 最大化按键。

⑥ 恢复按键。

⑦ 缩放边框。它是除最大化和最小化窗口以外,在窗口四周可见的边框。

⑧ 滚动条。当窗口中的内容较多,而窗口太小不能同时显示它的所有内容时,窗口的右边会出现一个垂直的滚动条,或者在窗口的下边会出现一个水平的滚动条。

(2)窗口的操作

窗口的操作是以鼠标和图标的基本操作为基础的。它主要包括以下几种:

① 窗口的选中。将鼠标指向需要选择的窗口,并单击鼠标即可选中窗口。

② 窗口的移动。将鼠标指向需要移动的窗口,并拖动鼠标到指定位置即可实现窗口的

462移动。最大化的窗口是无法移动的。

③ 窗口的打开和关闭。程序管理器窗口中的每个图标都可以打开成为一个组窗口,而任何一个打开的组窗口可以缩小成为一个组图标。

④ 窗口的最大化、最小化和恢复。每一个窗口都可以三种方式之一出现,即由单一图标表示的最小化形式、充满整个屏幕的最大化形式、或者是允许窗口移动并可以改变其大小和形状的恢复形式。

⑤ 窗口大小的改变。当窗口不是最大时,可以改变窗口的宽度和高度。

⑥ 窗口内容的滚动。当减少窗口的大小或者在其中增加更多的东西,以至于窗口不足以显示其中的每一个内容时,窗口的右边和下边会出现垂直滚动条和水平滚动条。

滚动操作包括以下3种:①小步滚动窗口内容,单击滚动箭头,这样可以实现一小步滚动。②大步滚动窗口内容,单击滚动箭头和滚动框之间的区域,这样可以实现一大步滚动。③滚动窗口内容到指定位置,拖动滚动框到指定位置,这样可以实现随机滚动。

菜单和命令

菜单是一些命令的列表:每个菜单都有一个描述其整体目的和

功能的名称。不同窗口的菜单一般是不同的。菜单通常出现在窗口

的上部(菜单栏)。每个窗口还有一个系统菜单,其中包括允许关

闭窗口或改变其物理概貌的命令。如右图所示:

打开菜单的方法是:将鼠标指向窗口的菜单栏中的某一菜单名,或者指向系统菜单框,并单击鼠标,该菜单即被拉下。也可以使用键盘操作:先按住Alt 键,再按下菜单名中圆括号内带下划线的字母。

关闭菜单的方法是将鼠标指向菜单栏以外的任意区域,并单击鼠标,该菜单即被关闭。

对话框

对话框是小型的特殊的窗口,它出现在程序执行过程中,提出选项并要求给予答复。对话框的类型较多,主要有以下几种:①信息对话框。它一般只有“确定”按钮。②提示对话框。它一般有“确定”、“否”和“取消”按钮。③列表和编辑对话框。这类对话框较为复杂。

【程序的启动】

启动程序的方式

程序安装成功后,用户就可以启动它为自己工作,启动程序的方法很多,下面列出了几种:

(1)在资源管理器(Explorer)中的应用程序图标上右击,然后在显示出来的右键快捷菜单中单击“开始”。

(2)在资源管理器中或在文件管理器中双击应用程序的图标。

(3)在资源管理器中双击与应用程序相关联的数据文件(这要求你创建文件关联,或该应用程序为你创建了这个关联。要改变文件关联,打开Windows资源管理器,选择“查

463看(View)”|“文件夹选项(Folder Options)”,单击“文件类型(File Type)”选项卡,并单击“编辑(Edit)”按钮来定义哪一个应用程序应该打开那种类型的文件)。

(4)选择“开始”|“运行”,并在其中输入应用程序的路径和文件名,单击“确定”按钮启动该应用程序。

(5)选择“开始”|“运行”,然后把应用程序的图标拖放到“运行”对话框中。单击“确定”按钮启动该应用程序。

(6)从“开始”菜单中选择该应用程序的项目。

(7)在桌面上创建一个快捷方式图标。可以通过右击或双击该图标来启动该应用程序。

(8)设置桌面来使用新的Windows单击界面,这样将可以更快地启动应用程序或选择文件。

(9)分配一个快捷键给该应用程序,然后通过键盘快捷方式来启动它,此时必须创建一个LNK文件来做这件事情。

(10)把该应用程序图标或与之相关联的数据文件放置在“开始”菜单中的“启动”文件夹中,让它在你下一次启动Windows时自动运行。

(11)使用“查找”对话框找到应用程序,然后右击或双击它。

(12)在OLE复合文档中嵌入或链接该应用程序的数据。用户可以通过双击嵌入在该文档中的对象来启动该应用程序,但该应用程序必须支持OLE。

如此多的启动方式,到底使用哪一种好呢?这要由用户自己来决定。在不同的情况下,不同的方式暗示了不同的效率,需要用户在不断的尝试中做出判断。实际上,这些方式中,有一些是较常用的,因而我们也将把重点放在这些较常用的方式上。至于其他的方式,考试中也不做要求。

从“开始”菜单启动程序

从Windows的“开始”菜单启动程序的方法为:

(1)单击屏幕左下角的“开始”,然后指向“程序”。

(2)若所需的程序名称不在直接菜单中,则指向包含该程序的文件夹。

(3)单击该程序名,启动程序。

需要注意的是,启动程序后,该程序的按钮出现在“任务栏”上;要在正在运行的程序之间切换,请单击其任务栏按钮;如果要启动的程序没有出现在“程序”菜单或其子菜单上,单击“开始”,指向“查找”,然后单击“文件或文件夹”。使用“查找”对话框来定位程序文件。

直接从文件启动程序

如果要启动的程序没有出现在“程序”菜单或其子菜单上,用户可以打开“我的电脑”或“资源管理器”,找到程序所在的文件夹,然后双击有关的可执行文件(*.exe)或相应的快捷方式。

使用这种方式最为直接,但当硬盘上的文件比较多,或者可执行文件所在的目录层次比较大时,启动程序比较麻烦。

使用“运行”命令启动程序

464 使用命令的方式也可以启动程序,具体方法为:

(1)单击“开始”按钮,打开“开始”菜单。

(2)单击“运行”,打开“运行”对话框。

(3)在“打开”文本框里,键入要打开的程序所在路径和文件名,或者单击“浏览”,定位要打开的程序。

(4)选择“确定”或按Enter键。

文本框的右端有一个向下的箭头,单击该箭头,会出现一个下拉式列表,列出了用户最近用“运行”命令打开的项目。如果用户要打开的程序在此列表中,也可以单击它来打开。此外,用户还可以在文本框中键入路径或网址,来打开文件或文件夹,连接到其他计算机、Internet或者intranet。

【在正在运行的程序间切换】

正如前面所述,只要运行一个程序,就会打开一个窗口。所以,在运行的程序之间进行切换,与在窗口之间进行切换完全一样。

切换方法一

通过在“任务栏”上单击该程序按钮进行切换。Windows操作系统的任务栏如下图所示:

在上图中,若要切换到“上机指导”,单击任务栏上的“上机指导.doc”按钮。

如果看不见到任务栏,可能是因为已经设置了“任务栏自动隐藏”,指向任务栏所在的屏幕区域(例如,如果任务栏位于屏幕底部,就指向屏幕底部),任务栏就会出现。任务栏不可见也可能是因为用户将其手动设置到了最小,如果是这样,用户可以手动调整任务栏为可见。

切换方法二

进行程序间切换的另外一种方法是使用Alt+Tab组合键,这样会切换到用户使用的上一个程序窗口。

或者,按住Alt键时反复地按动Tab键,此时

将会出现一个小窗口,显示着所有打开的程序图

标,一个方框在各个图标的上面交替移动,如果停

止按 Tab 键,方框会停在某个图标上,这时放开

Alt键,用户就会进入该图标相应的程序窗口,如

右图中所示。

切换方法三

使用Alt+Esc组合键,各个程序窗口将会交替出现。这种方法与方法二的区别在于不出现程序切换窗口。

465【最大/小化、还原打开的窗口】

最小化方法一

单击窗口右上角的最小化按钮 ,该窗口最小化为任务栏上的程序按钮。

还原该窗口至先前尺寸,单击任务栏上的程序按钮;或者在程序按钮上右击,选择“还原”或“最大化”。

最小化方法二

单击任务栏上的程序按钮,相应的程序窗口会最小化为任务栏上的程序按钮。

还原该窗口至先前尺寸,单击任务栏上的程序按钮;或者在程序按钮上右击,选择“还原”或“最大化”。

最小化所有方法一

(1)右击任务栏上的空白区域,弹出快捷菜单。

(2)单击“最小化所有窗口”。

需注意的是,此项操作Windows 不会最小化打开的对话框。 要打开已最小化的窗口,在任务栏上单击其按钮。 要将窗口恢复为先前状态,右击任务栏上空白区域,然后单击“撤消所有最小化”。

最小化所有方法二

要最小化所有窗口和对话框( 注意这里),单击任务栏上“快速启动”工具栏中的“显示桌面”按钮 ,所有打开的窗口将最小化为任务栏上的按钮,而对话框则会隐藏。

要打开已最小化的窗口,在任务栏上单击其按钮。

要将窗口恢复为先前状态,右击任务栏上空白区域,然后单击“撤消所有最小化”。

再次单击 可将所有窗口和对话框恢复为先前尺寸。

如果“快速启动”工具栏在任务栏上不可见,按照下列几种方法可使其显示:

(1)在任务栏上右击鼠标。

(2)指向“工具栏”。

(3)选择“快速启动”,如右图所示。

最大化之一

单击窗口右上角的最大化按钮 ,将该窗口最大化。

单击还原按钮 ,可将窗口还原为先前尺寸。

最大化之二

双击窗口的标题栏也可将窗口最大化。

再次双击窗口的标题栏可将该窗口恢复为原始大小。

其他方法

可以使用程序窗口的“控制菜单”中的各项命令。

要打开“控制菜单”:单击窗口左上角的程序图标;或者按“Alt+空格”。

466【退出没有响应的程序】

在用户运行某个程序的时候,由于可能的很多种原因,有可能发生程序停止运转的事情,这时候,如果用户等待响应的话,只能是浪费时间。所以,在确定程序的确是“死”掉了之后,应强行退出该程序。

方法一

(1)按下Ctrl + Alt + Del组合键;

(2)打开“任务管理器”;

(3)单击没有响应的程序,然后单击“结束任务”;或者单击“取消”,回到桌面。

需注意的是,按Ctrl + Alt + Del,然后单击“关闭程序”对话框中的“关闭系统”,也可以关闭计算机。

方法二

在任务栏上右击鼠标,选择“打开任务管理器”,然后按照方法一中所述继续进行。有时候强行退出程序时,系统会给出提示信息。例如在强行退出没有保存文件的编辑软件的时候,可能会提示是否保存文件,再执行退出命令。

如果在一定的时间内没有响应提示信息对话框,系统将自动强行结束程序。这种情况下,没有保存的数据将会丢失。

【退出程序】

退出方法一

单击“文件”菜单上的“退出”;

有的程序带有自己的“退出”命令,如一般的游戏,选择它也可退出。

退出方法二

单击标题栏最右边的 按钮也可以退出程序。

其他方法

利用类似于前面退出没有响应程序的方法,也可以退出程序,但是一般来说那样是比较危险的,因为可能导致数据的丢失。

【创建快捷方式】

在桌面上创建

(1)从文件夹中拖出快捷方式

① 在“我的电脑”或“Windows资源管理器”中,找到要创建快捷方式的项目(如用户本人或网上其他用户的计算机文件、程序、文件夹、打印机或计算机)。

② 拖动文件至桌面上。

467 这样,该文件的快捷方式就建立起来了。

比如,打开“我的电脑”,拖动“控制面板”图标到桌面上,

可以看到“我的电脑”中还保留着“控制面板”,而随着鼠标指针

移动的图标虚像已经变成了实的,同时还多出了一个弯弯的箭头,

如右图所示。

用户要打开“控制面板”,只要双击该快捷方式就行了。

(2)将快捷方式复制在桌面上

① 在“我的电脑”或“Windows 资源管理器”中, 右击要为其创建快捷方式的项目,弹出快捷菜单。

② 选择“复制”菜单项。

③ 回到桌面,在空白处右击鼠标。

④ 选择“粘贴快捷方式”。

这样就将快捷方式建立到桌面上了。

(3)在文件夹里创建快捷方式

① 在“我的电脑”或“Windows 资源管理器”中,找到右击要为其创建快捷方式的项目,右击它弹出快捷菜单,或者单击它并打开窗口菜单栏的“文件”菜单。

② 在菜单中选择“创建快捷方式”,即可在该项目的旁边出现一个与其几乎完全一样的图标,可以对它进行命名。

③ 将新创建的快捷方式拖至桌面。

要删除桌面上的快捷方式,可以将它直接拖入桌面上的“回收站”,然后在“确定”对话框中选择“是”。或者单击它,再按下Delete键,也可使通过鼠标右键快捷菜单进行删除。

添加到“开始”菜单中

(1)方法一,步骤如下:

① 打开“开始”菜单,指向“设置”。

② 选择“任务栏与开始菜单”。

③ 选择“开始菜单程序”选项卡。

④ 在“自定义‘开始’菜单”中选择“添加”。

⑤ 在弹出的对话框中,键入要创建快捷方式的项目的位置和名称;或单击“浏览”查找该项目。

⑥ 单击“下一步”,选择在“开始”菜单里放置新的快捷方式的文件夹,或者单击“新建文件夹”以创建一个新的文件夹并可以为它命名。

⑦ 单击“下一步”,在新的对话框里键入快捷方式的名称,单击“完成”。

然后再打开“开始”菜单,找到刚才设置的位置,就会看到用户刚刚创建的快捷方式已经出现了。

下面做几点说明:

① 可创建快捷方式的项目可以是程序文件,也可以是其他类型的文件、文件夹、网页、其他计算机、打印机、驱动器。

② 关于“浏览”:很多地方需要用到“浏览”功能,特别是当用户对需要键入的项目

468位置和具体名称不清楚时。使用该功能,单击对话框提供的按钮即可,然后会出现一个对话框,其中“文件类型”默认指出了要打开的文件的类型(在下拉列表中可选择不同的文件类型)。像使用“我的电脑”一样,找到要创建快捷方式的项目,选择它并单击“打开”或双击该项目,即可完成“浏览”。

(2)方法二,步骤如下:

在“开始”按钮上右击,弹出快捷菜单,然后选择“打开”,“开始”菜单的文件夹窗口就会出现。

这个文件夹显示了“开始”菜单中可由用户自定义的所有项目,其中一个即是“程序”文件夹,包含了“程序”菜单中的所有快捷方式。另外的快捷方式是属于“开始”菜单中的上一部分(有一浅色线标志出了分界)。用户可以将其他快捷方式拖动或复制/粘贴至该文件夹的任意位置,但建议用户注意保持“开始”菜单良好的逻辑性,以便于使用。

添加到任务栏

(1)建立新的工具栏

首先用户要在磁盘中建立一个文件夹,并在该文件夹里创建一些常用的快捷方式;然后,在任务栏的空白处右击,指向“工具栏”,选择“新建工具栏”。

(2)添加到“快速启动”工具栏

如果“快速启动”工具栏在任务栏上可见,可将要建立快捷方式的程序图标拖动至“快速启动”工具栏,看到一条黑粗线,它表示新建的快捷方式的位置,用户可以调整快捷方式的位置。

如果“快速启动”工具栏在任务栏上不可见,在任务栏的空白处右击,通过快捷菜单可激活“快速启动”工具栏。

使用快捷键

有的用户喜欢使用自定义的快捷键来快速启动应用程序,但如果将很多的应用程序都设定快捷键的话,用户就会背上记忆大量组合键的包袱。因此这种方式应该慎用。在此不赘述。

需注意的是,不能使用Esc、Enter、Tab、Spacebar、Print、Screen 或 Backspace 键。同时,其他程序不能使用该组合键。如果该快捷键与另一个Windows 程序的快捷键冲突,Windows程序的快捷键就会失去作用。

快捷方式属性

有时用户对快捷方式指向的程序文件做了重命名或改变位置等操作,当用户再次打开此快捷方式的时候,系

统会报错。这时如果不想重新建立快捷方式的话,可按照下列步骤操作,对快捷方式的属性做一些改动:

(1)右击快捷方式图标,选择“属性”,选择“快捷方式”选项卡。

(2)在“目标”中键入已改动的程序文件的正确位置和名称,单击“确定”命令按钮。

469【从CD或软盘添加程序】

一般的Windows应用程序都需要在进行安装后才能使用,而载有这些程序的软件包内都有“setup.exe”文件(或类似的其他安装文件),运行该文件,然后按照屏幕提示即可将程序安装。

另外,用户也可以通过“控制面板”来安装程序,步骤如下所示。

① 打开“开始”菜单,指向“设置”,选择“控制面板”。

② 选择“添加/删除程序”。

③ 选择“安装新程序”,单击“CD或软盘”。

④ 按照窗口中指示完成余下操作。

需注意的是,使用此操作只能安装Windows应用程序。

【更改或删除程序】

更改或删除程序的步骤为:

① 打开“控制面板”中的“添加/删除程序”。

② 单击“Change or Remove Programs”,在下面的列表中选择要更改或删除的程序。对于已安装但未在该列表中出现的程序,需要参见销售商的文档,以获取有关如何删除程序的说明。

③ 要更改程序,单击Change/Remove或Change;要删除程序,单击Change/Remove 或Remove。

下面做几点说明:

① 更改/删除程序的时候,要格外小心。当用户选择了“Change/Remove”之后,有的程序会做另外的提示,而有的程序不会做任何提示而将程序直接更改/删除。

② 使用此操作,只能删除Windows应用程序;对于其他程序,检查文件目录看其他文件,如.ini文件等是否应该被移动。

③ 用户可通过选择不同的“排列方式”来排列程序项目。

【链接和导入对象】

一样工作往往需要多个人协作完成,在Windows中也是如此,几乎每个应用程序都要用到其他应用程序的信息。要提高程序处理任务的效率,就必须学会如何使用不同的程序共享信息资源,这里我们先介绍两个概念:“链接”和“嵌入”,并以“写字板为例”加以说明。

(1)链接

链接对象:一段在其他应用程序中创建的信息(如位图、文本)被粘贴到用户目前的程序中但同时仍与源文件保持关联。当被链接对象编辑时,源文件也会相应地发生改变。 要

470编辑链接对象,双击它,创建它的程序中的工具栏和菜单就会出现。

(2)导入

导入对象:一段在其他应用程序中创建的信息(如位图、文本)被粘贴到用户目前的程序中。当对象被导入后,用户可以利用创建对象的程序中的工具栏和菜单对其进行编辑。此时该对象的源文件不会发生改变。要编辑导入对象,双击它,创建它的程序中的工具栏和菜单就会出现。

(3)导入和链接对象到写字板

打开“程序”菜单,指向“附件”,选择“写字板”,在“写字板”里,选择“插入”|“对象”,打开相应的对话框。

① 单击“由文件创建”。

② 在文本框里键入源文件的路径和名称,或者单击【浏览】选择源文件。

③ 要导入对象,确认链接复选标记已被清除,单击“确定”即可;要链接对象,确认链接复选框已被选中,单击“确定”即可。提示,使用“复制”|“粘贴”|“复制”|“特殊粘贴”也可以相应地实现导入和链接。

④ 要新建一个对象,单击“新建”,然后选择对象类型,单击“确定”。完成之后,在对象外单击鼠标,可回到写字板。

当上面所说的一切都做好之后,我们再来学习如何编辑导入/链接对象:

① 单击对象,在“编辑”菜单中, 指向已选择的对象类型,如位图对象,弹出子菜单。

② 如果要在写字板窗口中编辑对象,单击子菜单中的“编辑”,编辑完成后单击对象外的写字板区域可回到写字板。

③ 如果要在创建对象的程序窗口中进行编辑,单击“打开”,编辑完成后,关闭窗口回到写字板。

【选择文件的打开程序】

使用过Windows 95或98的用户一定非常清楚,在以前的Windows中,双击一个文档时,如果此文件类型已经关联了某个应用程序,则Windows就会启动这个应用程序并打开这个文档。如果此文件类型没有关联任何应用程序,用户可以选择文件的打开方式。

如果需要选择文件的打开方式,可以在一个文件上右击,选择打开方式,则可弹出 “打开方式”对话框,使用户可以更容易地使用正确的应用程序来打开这些文件。

Turbo C 2.0集成编译环境

C语言编译器有许多种,比如Microsoft公司的MS C,Borland公司的Borland C以及Borland C++,还有就是现在要讲的Turbo C。这些都是非常好用的C编译器。其中Turbo C是一个非常简单好用的编译器,由于它使用的广泛性,下面就以它为基础来讲述C编译器的使用方法。

当然,各个编译器的新版本和旧版本之间都是兼容的,所以读者也可以在C++编译器

471上来运行C源程序。比如可以在使用极为广泛的Visual C++上运行。关于Visual C++的使用方法,可以参考相关教程。

这里讲述的Turbo C版本为2.0,下面将简称TC 2.0。

【进入TC编译系统】

首先确定已经将TC安装到了硬盘里,这里假设安装路径是E:\tc20,当然也可以安装到其他驱动器的相关路径。

打开DOS窗口或者运行全屏模式的DOS系统(这里建议运行全屏模式,因为在窗口下有可能出现乱码现象),如果希望能有汉字输入输出系统,可以先运行 UCDOS 等汉化DOS系统,然后再进入TC编译系统。

进入DOS后,再进入TC系统所在的文件夹,这里是进入E:\tc20,完成后效果如图16-18所示。

图16-18 进入TC20文件夹

这时就可以运行TC编译系统的所有命令了,比如键入tc就可以运行TC的集成开发环境,键入readme就可以查看TC的说明文件,等等。下面键入tc(这是一个可执行文件,扩展名为.EXE),可以进入TC集成开发环境,见图16-19所示。

图16-19 进入TC集成开发环境

【TC 2.0集成环境介绍】

下面介绍改集成环境的使用方法。首先看主菜单(Main Menu),共有File、Edit、Run、

472Compile、Project、Options、Debug和Break/Watch八个菜单。这里只介绍常用的几个菜单。

File菜单

该菜单是使用最频繁的,因为如果要编写C源程序就必须使用到它。它的作用是装入或者保存文件,管理目录,转入DOS方式以及推出TC系统。先看它里面的选项,如图16-20所示。

图16-20 File菜单选项

Load是将默认的或者上一次编辑的C源程序装入进行编辑或者运行;Pick选项的作用是选择需要装入的源程序;New则是新建一个文本文件,可以对其进行编辑;Save命令的作用是将目前的打开的文件保存;Write to则是将目前的文件写到指定的位置,该命令可以指定保存路径;Directory可以通过指定文件路径来打开文件;Change dir是将原来的目录更改为指定的目录;Os shell命令执行后会转到DOS方式下;Quit则是退出TC编译系统。

Edit菜单

选中该菜单即进入文本编辑状态,可以在下面的编辑窗中写入C语言源程序。其实C语言源程序并不一定要在该环境下进行编辑,任何一个文本编辑软件均可以用来编写源程序。比如Windows里的记事本等工具。在文本编辑工具中编写完源程序后就可以在TC编译器里进行编译。

Run菜单

该菜单中最有用的命令就是Run命令,它的快捷键是Ctrl+F9。当程序编写完成或者调入编译器并且检查无误后,就可以选择该菜单或者使用快捷键来运行程序。

Compile菜单

该菜单下的命令可以连接目标文件、生成可执行文件、连接可执行文件以及得到编译信息等。

Project菜单

该菜单下的命令是关于多文件工程管理的。

Option菜单

Option菜单可以用来设置集成开发环境的工作方式,如图16-21所示。

473 图16-21 Option菜单选项

该菜单下有七个选择项,它们的用法简述如下。

Compiler选项下有七个可选项:Model 存储器模式)、Define 宏定义)、Code Generation(代码生成)、Optimization(代码优化)、Source(源程序处理菜单)、Error(出错菜单)和Names(命名菜单)。

Linker选项也有七个选项,主要是关于连接程序的。在一般情况下不必对其作改动。

Environment选项有八个选择项,主要是关于信息跟踪、编辑自动保存以及制表键大小,等等。用户可以根据自己的喜好来进行设定。

Directories选项用于设置TC的操作路径,各个选项如图16-22所示。

图16-22 Directories选项

如果读者不是将TC装在默认的路径C:\TC20下,那么就必须对该选项进行一下改动。比如现在的TC是装在E:\TC20路径下,就可以将TC的操作路径按照如图16-22所示来进行设置。

Arguments选项可以使用户如同使用DOS状态下的命令行参数一样来输入运行程序命令行参数。

Save Options选项将Options菜单的内容进行保存,当再次运行TC时就按照目前的设置进行初始化界面。

Retrive Options选项可以装入以前保存过的配置文件,可以改变系统配置。

Debug菜单

该菜单适用于高级用户,初学者可以将其略过。

Break/Watch菜单

该菜单的主要功能是控制断点和监视表达式。

【编译C源程序的步骤】

首先编辑C源程序,这一步可以在任何的文本编辑器中进行,当然也可以在TC的主窗口中进行。编辑时要按照C语言的要求,以一定的格式进行,这样将有助于程序阅读和调试。

编辑完后,将文件保存为扩展名为.C的文件。文件的路径可以随意指定,但最好是选择容易找到的路径。

然后先检查一下程序有无明显的语法错误,如果有马上将其改正。在确认程序无误后可以按快捷键F9对程序进行检查,看是否有语法错误存在。一般来说,如果语法没有错误,预编译是可以通过的。也就是说,如果程序中存在逻辑错误一般是检查不出来的,这显然

474不可能得到正确的结果。

预编译通过后,可以按快捷键Ctrl+F9对程序进行编译,在这个过程中将连接可能用到的库文件、目标文件以及其他指定文件。编译通过后,可以按快捷键Alt+F5转换到DOS窗口下查看结果。

如果结果不正确就需要认真查看源文件中是否含有语法错误或者逻辑错误,直到调试正确为止。程序调试是一项细致深入的工作,需要在实践的基础上逐步积累经验。

上机考试系统说明

全国计算机等级考试上机考试使用教育部考试中心研制开发的专用考试系统,该系统提供了DOS和Windows两个版本,根据考生选择的编程语言种类使用不同的版本。上机考试系统提供了开放式的考试环境,具有自动计时、断点保护、自动阅卷和回收等功能。C语言考试使用DOS版本。下面将详细介绍如何使用上机考试系统。

【上机考试环境】

硬件环境

386以上单机系统,2MB以上内存、640?480(VGA)以上显示器、100MB以上硬盘(10MB以上剩余空间)。

软件环境

操作系统:MS-DOS5.0以上;

汉字系统:希望汉字系统(UCDOS)V3.1以上;

二级考试软件系统:Turbo C 2.0 以上。

【上机考试时间】

二级考试时间为60分钟。

如果考生登录成功,则系统将自动抽取考题并且在屏幕上显示上机须知。考生应该认真阅读该须知,在确认已经清楚了考试规则之后按“S”键开始考试。此时系统开始计时。如果是二次登录,则系统将累计计时,考生必须在规定的时间内完成考试内容。当考生超出考试所用时间时机器将自动关闭;当考试只剩下指定时间时,屏幕上会自动报告所剩考试时间,此时考生只需按任意键继续答题,不会影响考生成绩。

【上机考试题型和分值】

二级考试的题型有三类:一类是完成指定的计算机基本操作,在二级C语言的上机环境下是MS-DOS操作系统常用命令操作,这一部分的分值占30%。第二类题型是根据题目

475给定的要求修改和调试程序,并得到正确结果,这一部分占30%。第三类题型是根据题目给定的要求编制程序,经调试和运行,并得到正确结果,这一部分占40%。

【上机操作考试全程讲解】

进入UCDOS系统

考生提前5分钟进入考场,开机进入DOS操作系统。在这个过程中,各个考点会给考生提示如何进入DOS系统以及如何运行UCDOS。在一般情况下,UCDOS安装在C:\UCDOS目录下,只要考生进入该目录然后运行UCDOS命令即可进入。

进入全国计算机等级考试系统

进入等级考试系统所在的文件夹,一般各个考点都会事先通知考生该文件夹位于什么位置,这里假定为C:\kssys。进入该文件夹后就可以运行该目录下的ID.EXE可执行文件,即可进入等级考试系统。也就是说使用命令

cd C:\kssys

ID.EXE

其画面如图16-23所示。

图16-23 全国计算机等级考试界面

可以看见,这里需要考生输入验证信息,在“请输入准考证号”一栏输入考生的准考证号(这里输入模拟的考号 240199990003),然后按回车键,即可以进行验证,如果验证通过就可以进入下面的界面,如图16-24所示。

图16-24 系统验证界面

476 如果考生确定姓名与身份证号相符即可按“Y”键进入系统,若发现不对可输入“N”,重新输入准考证号,系统允许考生登入准考证号最多三次,如果均不符合,则请监考人员帮助查找原因,给予更正。可以看见界面显示“试题已抽取”并进入如图16-25所示界面。

图16-25 系统开始界面

此时系统并不进行计时,只是告诉考生应该注意的事项和试题结构等信息。同时告诉考生,任何时候按F12功能键即可显示考试题目选择菜单,这样就有利于考生进行阅读考题和答题。考生确认清楚了这些之后,就可以按S键进入考试状态。

自动计时与结束考试

按S键进入考试状态后,考试系统将自动进行倒计时。考试过程中,考生经常注意考试剩余时间,及时调整

答题速度,并且注意答完一类题目后要及时存盘,以免数据丢失。

考试结束前5分钟,系统会自动弹出信息框报警,提示考生剩余时间只有5分钟了,应该将答题结果存盘。

倒计时为0时,考试结束。这时,考生将不能进行任何操作,惟一可做的事就是离开考场,等待评分结果。

等待评分结果

上机考试结束后,考生将被安排到考场外的某个休息场所等待评分结果,考

生切忌提早离开,因为考点将马上检查考试结果,如果有数据丢失等原因引起的

评分结果为0的情况,考点将酌情处理。说不定需要重考一次。如果这时找不到

考生,考点只能将其机试成绩记为0分。

DOS操作系统常用命令操作

如前所述,二级上机考试都有三个题型。二级 QBASIC、C、FORTRAN、FOXBASE+四种语言第一部分就是MS-DOS操作系统常用命令操作题。

477 DOS系统的常用命令操作题以DOS 5.0为基准,包括所有的外部命令和内部命令。必须了解所有命令的参数的使用(前面已经系统介绍了各种DOS命令)。但是据统计发现,常考的命令约六条(即RD、MD、DEL、COPY、RENAME和ATTRIB),只要考生掌握了这六条命令的用法,并且在平时多做些练习,DOS操作系统常用命令操作题将迎刃而解。对于基础不太好的学生,更是应该在这类题型上少丢分。

下面给六条常考的DOS命令分别举一个例子,因为比较简单(没有什么道理可说,记住就行了),所以只给出答案。

题干

1. 将考生目录下的KS\EJKS子目录删掉。

2. 将考生目录下的GHF\FGG子目录中的文件TTTT.BAK删除。

3. 将考生目录下的JIK\JIG下的文件TI.FOR和HIU\MIE目录下的文件FIG.FAI文件和并复制到考生目录下YYY\UUU\JIKJ.TXT。

4. 将考生目录下的DING\ING子目录中的EEE.DAT文件该名为DJI.JIG。

5. 将考生目录下的JIG子目录中建立一个新的子目录NEW。

6. 将考生目录下的DITD\DJI目录下的DIFF.IMP文件属性设置为系统属性和隐含属性。

答案

RD KS\EJKS

DEL GHF\FGG\TTTT.BAK

COPY JIK\JIG\TI.FOR+HIU\MIE\FIG.FAI YYY\UUU\JIKJ.TXT

RENAME DING\ING\EEE.DAT DJI.JIG

MD JIG\NEW

ATTRIB DITD\DJI\DIFF.IMP +S +H

程序修改和调试

第二部分是修改程序和调试程序。考生在自己所指定的语言环境中(比如 C语言、BASIC语言,等等),按给定的题目要求来修改、调试相应的程序。在修改调试过程中,考生一般不允许增加或删除行数(包括空行),一行只能修改或填写一个或几个地方。还有一点值得注意的是,在注释行中有***found***或***FOUND***的行考生不能删除,因为这是程序修改调试的标识行,删除或移动位置也将影响考生这部分的成绩。

要修改调试的程序已经给出,考生只要把文件拿来使用即可,在考试过程中考生可以自已判断难易程度任意挑选哪一题先做。

下面以一个实例来说明如何解答这类题型,见下面的程序。其中需要修改的程序在名为MODI1.C的文件中,其输出结果保存在名为FIRST.DAT的文件中。

程序16-1:P16-1.c

#include <stdio.h>

void main()

{

478 int i;

FILE *fp;

fp = fopen("first.dat","w");

/******found ********/ 此行不能修改

for(i = 1 ; i <= 10 ; i++)

fprintf(fp,"%d\n",&i);

fclose(fp);

}

试题分析

本题需要修改的部分在题目中已经做了标记,考生只要认真读题,应该能够分析出错误所在的地方。

首先,考生应该清楚整个程序的逻辑关系,比如本题就是要打开一个名为first.dat的文件,然后顺序读取开始的10个字符,并将它们显示在屏幕上。

清楚了这点之后就可以认真思考错误部分的语句有何问题。比如在本题中,语句:

fprintf(fp,"%d\n",&i);

是有问题的。&i是指变量i的地址,显然不能将这个地址输出,所以这是错误的,应该改成下面的形式:

fprintf(fp,"%d\n",i);

至此本题已经得到解答。接下来可以从整个题目的逻辑关系上查看一下是否解答正确。在确认无误后就可以将本题保存。

程序编制和调试

考生在自己所指定的语言环境中,按给定的题目要求编制程序。经过调试、运行,最后得到结果。考生要将所编写的程序存放到指定的文件中,程序运行结果同样输出到指定的文件中(每行输出一个结果)。一般来说,程序中的输入输出过程或函数或语句都是已经给出的,考生不必须自行编写和修改。在考试过程中考生可以自己判断难易程度任意挑选哪一题先做。

下面看一个实例。试题如下所示。

试题说明:函数fun的功能是将s所指字符串中下标为奇数的字符删除,s中剩余的字符形成一个新串放在t所指的数组中。例如,当s所指字符串为"ABCDEFGHIJK"时,t所指的数组的内容应是"ACEGIK"。注意: 部分源程序存在文件PROG1.C中。请勿改动主函数main和其他函数中的任何内容,仅在函数fun的花括号中填入你编写的若干语句。

程序如下所示。

程序16-2:P16-2.c

#include<conio.h>

#include<stdio.h>

#include<string.h>

void fun(char *s,char t[])

479 {

}

main()

{

char s[100],t[100];

clrscr();

printf("\nPlease enter string S:");

scanf("%s", s);

fun(s,t);

printf("\nThe result is: %s\n",t);

NONO();

}

NONO()

{

/* 本函数用于打开文件,输入数据,调用函数,输出数据,关闭文件。*/

char s[100],t[100];

FILE *rf,*wf ;

int i;

rf=fopen("bc01.dat","r");

wf=fopen("bc01.out","w");

for(i=0;i<10;i++>

{

fscanf(rf,"%s",s);

fun(s,t);

fprintf(wf,"%s\n",t);

}

fclose(rf);

fclose(wf);

}

试题分析

解答这种题型仍然需要对整个程序的逻辑关系做一个分析,找出语句之间的关系。对本题来说,程序的作用已经在题意中讲得很清楚。考生可以对照题意阅读程序,这样不但可以做到有的放矢,而且还可以降低难度。

对于本题来说,需要考生将fun函数补充完整,而程序的其他部分已经是写好了的。在fun函数中有两个形参,首先要清楚这两个形参的含义。在main函数中,实参s和t都是数组,形参s是指针变量,t是数组,调用fun函数后,形参s指向实参s数组的起始位置,通过条件j%2==0判断数组的下标是奇数还是偶数,若为偶数,将指针s所指的字符赋给数组t,以达到将字符串中下标为奇数的字符删除的目的。

通过分析,可以将fun函数补充完整。

参考答案

void fun(char *s,char t[])

{

int i=0,j=0;

while(*(s+j)!='\0')

{

if(j%2==0)

480 t[i]=*s;

i++;

j++;

}

t[i]='\0';

}

典 型 例 题

【例题16-1 TYPE打印输出】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

要想打印存放在当前盘当前目录上所有扩展名为.TXT 的文件内容, 应该使用的 DOS命令为( )。

A)DIR *.TXT>PRN B)TYPE *.TXT>PRN

C)COPY *.TXT PRN D)COPY *.TXT>PRN

解题方法

本题是关于DOS系统中输入输出重定向和打印输出的问题。解答这种题型,首先需要考生对这个知识点有足够的了解,在此基础上才能进行分析并解答。

知识点分析

打印输出涉及到两个命令TYPE和PRINT。这两个命令均可以用来进行打印文本内容,不过两个命令是有区别的。命令TYPE可以在接通打印机或输出转向打印机的情况下打印输出指定的文件内容。命令PRINT可以执行后台打印,即在系统执行其他任务时打印输出由该命令列举的文件内容。

看本题的选项可以知道,显然选项A、C和D均不涉及打印命令,而只有选项B有打印命令TYPE。

正确答案

B

难度提示

初级。本题所涉及到的知识点非常单一,只要考生能够对DOS一般的命令有所了解就能够解答本题。

【例题16-2 XCOPY命令的使用方法】

(全国计算机等级考试二级笔试试卷20xx年4月)

481 题干

将当前盘当前目录及其子目录中的全部文件(总量不足1.2MB)复制到一张空的A盘的根目录下,应该使用的DOS命令为( )。

A)XCOPY *.*A:\ /M B)XCOPY *.*A:\ /S

C)XCOPY *.*A:\ /P D)XCOPY *.*A:\ /A

解题方法

本题是关于DOS命令XCOPY的使用方法的一道题。对于一些常用的DOS命令,考生应该熟练掌握其用法,包括命令中所带的一些常用参数也应该熟练掌握。

知识点分析

XCOPY命令的功能是复制文件和目录树,它可以带有较多的参数。具体能够带有哪些参数以及参数的含义是什么,可以使用该命令的帮助,例如可以在命令提示行输入下面的命令XCOPY/?来查看该帮助。这也是解题的一个非常有用的技巧。

比如本题中所涉及到的参数含义如下所示。

/M:只复制有存档属性集的文件,并关闭存档属性。

/S:复制目录和子目录,除了空的以外。

/P:创建每个目标文件前提示。

/A:只复制有存档属性集的文件,但不改变属性。

正确答案

B

难度提示

中级。本题涉及到的DOS命令非常普通,但需要考生能够掌握该命令所带的参数的使用方法。

【例题16-3 PROMPT的参数】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

在C盘根目录下执行PROMPT $p$g命令之后,DOS的提示符变为( )。

A)C:> B)C:\> C)C> D)C:\

解题方法

本题是关于DOS命令PROMPT的使用方法的一道题。该题同样需要读者掌握一般命令的使用方法。

知识点分析

PROMPT命令带有较多的参数,可以通过命令帮助来查看这些参数的含义和使用方法。比如在本题中的两个参数含义如下所示。

482 $P:当前驱动器及路径;

$G:>(大于符号)。

正确答案

B

难度提示

中级。本题同样是考察DOS命令参数的含义及用法。涉及到的知识点单一,但需要考生深入掌握。

【例题16-4 输入输出改向的问题】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

DOS命令 "COPY CON DISP"中的CON代表( )。

A)子目录 B)磁盘文件 C)键盘 D)显示器

解题方法

本题考查的是DOS系统中输入输出重定向方面的问题。为了正确解答这种题型,考生需要对这个知识点有足够的了解,并且对其中的一些关键的概念有较深入的理解。

知识点分析

在涉及输入输出改向这一问题时,DOS指定了某些特定的名字作为标准设备的文件名,用来代表作为重定向对象的外部设备。比如本题中的CON即代表控制台(键盘输出或者屏幕输出)。关于这些特定的名字,考生应该熟悉较为常用的几个,例如 COM、LPT、PRN以及NUL,等等。

正确答案

C

难度提示

中级。关于输入输出改向的问题应该引起考生的注意,因为这个知识点比较生僻很容易被考生忽略。

【例题16-5 通配符的使用】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

下列各带有通配符的文件名中,能代表文件XYZ.TXT的是( )。

A)*Z.? B)X*.* C)?Z,TXT D)?.?

483 解题方法

关于通配符这一知识点,考生应该不会感到陌生。因为在上机的时候,通配符是非常有用的。

知识点分析

通配符是一个键盘字符,例如星号(*)或问号(?)。当查找文件或文件夹时,就可以使用它来代替一个或多个真正的字符。如果不知道真正字符或者不想键入完整名字时,常常使用通配符代替一个或多个字符。可以使用星号(*)代替零个或多个字符;使用问号(?)代替名称中的单个字符。

例如,如果想查找以gloss开头的一个文件,但不记得文件名的其余部分,可以键入以下字符串:gloss*。可能查到的文件如Glossary.txt、Glossary.doc和 Glossy.doc,等等。但如果键入的是以下字符串:gloss?,

则可能查到的文件是 Glossy.doc,而不可能查到诸如Glossary.txt和Glossary.doc等文件。

正确答案

B

难度提示

中级。关于通配符的知识应用非常广泛,所以对考生来说应该比较容易作答。【例题16-6 建立子目录】

题干

在考生目录下BIOS(C:\CEKS\2C\DOS>)子目录中建立一个新的子目录AMI。

解题方法

本题是关于DOS系统命令使用方法的一道题。解答这种题型需要考生有上机的经验,并且能够知道简单的DOS命令的使用方法。

知识点分析

本题要在目录BIOS 下建立一个子目录,所以首先要进入该目录。同时,考生应该清楚的是建立新的子目录的命令是MD。

正确答案

C:\CEKS\2C\DOS>cd BIOS

C:\CEKS\2C\DOS\BIOS>md AMI

难度提示

初级。本题考查的知识点应该是大多数考生都比较熟悉的,并不是生僻的命令。【例题16-7 目录操作】

题干

484 将考生目录下CMOS(C:\CEKS\2C\DOS>)子目录中的DEL子目录删除。

解题方法

本题同样考查了基本的DOS命令的使用方法。

知识点分析

本题需要用到DOS中的关于目录操作的命令。目录操作和文件操作之间是有区别的。就拿本题来说,如果题意是让考生将某一个文件删除,那么用命令DEL即可,但题目是想将某一个目录删除,显然不能再用DEL命令。这时就需要使用用于目录操作的命令。

用于删除目录的命令是RD。

正确答案

C:\CEKS\2C\DOS>cd CMOS

C:\CEKS\2C\DOS\CMOS>rd DEL

难度提示

中级。本题实际上是考查考生对文件操作和目录操作之间的区别的掌握。考生除了熟悉基本的DOS命令的使用方法之外,还要清楚这些命令之间的区别。

【例题16-8 修改程序和调试程序】

题干

给定程序MODI1.C中函数fun的功能是:把在字符串s中出现的每个字符,紧随其后重复出现一次,形成一个新串放在t中,t中字符按原字符串中字符顺序排列。例如:当s中的字符串为:"ABAABBCCDDEE"。则t中的字符串应为:"AABBCCDDEE"。

请改正函数fun中的错误,使它能得出正确的结果。注意:不要改动main函数,不得增行或删行,也不得更改程序的结构!

程序16-3:P16-3.c

#include <conio.h>

#include <stdio.h>

#include <string.h>

/************found************/

void fun (char s,char t)

{

int i,sl;

sl=strlen(s);

for(i=0;i<sl;i++)

{

t[2*i]=s[i];

t[2*i+1]=s[i];

}

/************found************/

t[2*sl]='0';

}

main()

485 {

char s[100], t[100];

clrscr();

printf("\nPlease enter string s:");

scanf("%s", s);

fun(s, t);

printf("The result is: %s\n", t);

}

解题方法

修改程序和调试程序的题目考查的是考生阅读程序,编写程序,特别是调试程序的能力。

知识点分析

阅读程序的关键在于掌握程序的逻辑结构,也就是说,需要掌握程序的功能以及它是如何实现的。在等级考试的试题中,均给出了程序的功能,或者是程序所要完成的任务,这就大大降低了阅读程序的难度。考生在阅读

程序时,要紧紧抓住这一点。将这一点利用好了就基本上掌握了题目的逻辑组织关系。

调试程序是有技巧的。比如虽然有时候编译系统给出的出错信息很多,但实际上程序中可能只有一个错误。要掌握这些技巧惟一的方法就是多上机练习。只有经过了大量的编程练习之后才能掌握调试程序的关键。

正确答案

void fun (char *s,char *t)

t[2*sl]='\0';

难度提示

高级。修改程序和调试程序在等级考试中所占的比重很大(%30),所以这部分题目做得好坏与整张试卷的成绩有很大影响。

【例题16-9 编制程序】

题干

函数fun的功能是:将n个人员的考试成绩进行分段统计,考试成绩放在a数组中,各分数段的人数存到b数组中:成绩为60~69的人数存到b[0]中,成绩为70~79的人数存到b[1],成绩为80~89的人数存到b[2],成绩为90~99的人数存到b[3],成绩为100的人数存到b[4],成绩为60分以下的人数存到b[5]中。

例如,当a数组中的数据是:93、85、77、68、59、43、94、75、98。调用该函数后,b数组中存放的数据应是:1、2、1、3、0、2。

注意:部分源程序存在文件PROG1.C中。请勿改动主函数main和其他函数中的任何内容,仅在函数fun的花括号中填入你编写的若干语句。

程序16-4:P16-4.c

#include <conio.h>

486 #include <stdio.h>

void fun(int a[], int b[], int n)

{

/*******将程序补充完整*******/

}

main()

{

int i,a[100]={93,85,77,68,59,43,94,75,98},b[6];

clrscr();

fun(a, b, 9);

printf("the result is: ");

for(i=0;i<6;i++)

printf("%d ",b[i]);

printf("\n");

NONO();

}

NONO()

{

/* 本函数用于打开文件,输入数据,调用函数,输出数据,关闭文件。*/

FILE *rf, *wf;

int a[100],b[6],i,j;

rf=fopen("in1.dat","r");

wf=fopen("bc01.dat","w");

for(i=0;i<10;i++)

{

for(j=0;j<10;j++)

fscanf(rf,"%d,",&a[j]);

fun(a,b,10);

for(j=0;j<6;j++)

fprintf(wf,"%d",b[j]);

fprintf(wf,"\n");

}

fclose(rf);

fclose(wf);

}

解题方法

解答“编制程序”的试题关键仍然在于掌握题目的逻辑关系。只有掌握了这一点才能对题目进行进一步的分析和解答。

知识点分析

阅读程序同“修改和调试程序”中的相关步骤。

编制程序要在读懂程序的基础上,掌握程序的逻辑结构,然后才能对某一部分进行编制。编制的关键在于使用合理的知识点结合程序原来的逻辑结构进行书写。在编制程序时,一定要使用自己最为熟悉的语句,不要使用自己并不是十分清楚的语句。因为一个程序可以有很多种实现方法,即便是同一个语句也可以有多种表现形式。例如,用指针编写的语句或者程序,完全可以用数组等数据类型来实现。所以选择自己熟悉的语句是非常必要的,也是完全可能的。

487 调试程序的关键与“修改和调试程序”相同。

正确答案

int i;

for(i=0;i<6;i++)

b[i]=0;

for(i=0;i<n;i++)

{

if(a[i]>=60&&a[i]<=69)

{

b[0]++;continue;

}

if(a[i]>=70&&a[i]<=79)

{

b[1]++;continue;

}

if(a[i]>=80&&a[i]<=89)

{

b[2]++;continue;

}

if(a[i]>=90&&a[i]<=99)

{

b[3]++;continue;

}

if(a[i]==100)

{

b[4]++;continue;

}

if(a[i]<60)

{

b[5]++;continue;

}

}

难度提示

高级。程序编制的题目考查的是考生的综合技能,包括对C语言的掌握,C语言编译系统的掌握以及对DOS系统的掌握。

【例题16-10 Windows窗口最小化对应用程序的影响】

(全国计算机等级考试二级笔试试卷19xx年4月)

题干

在Windows中,将一个应用程序窗口最小化之后,该应用程序( )。

A)仍在后台运行 B)暂时停止运行

C)完全停止运行 D)出错

解题方法

488 本题是关于Windows窗口最小化对应用程序的影响的问题。

知识点分析

Windows操作系统是多用户多任务系统,所谓多任务,就是说可以同时执行多项任务,当一个应用程序正在运行时,用户需要同时运行其他的程序,这时就要将其他的程序“摆到桌面上来”,可以使用最小化当前窗口,把“桌面位置让出来”,但是窗口最小化的应用程序并没有停止运行,否则,Windows操作系统就不是“多任务”系统了,该程序将在“桌面”以外的地方“默默”运行,这个地方通常称为“后台”。

正确答案

A

难度提示

初级。本题所涉及到的知识点非常单一,只要对“多任务”有所了解,确实不难解答。【例题16-11 应用程序的终止】

(全国计算机等级考试二级笔试试卷20xx年4月)

题干

在Windows中,终止应用程序执行的正确方法是( )。

A)将应用程序窗口最小化成图标

B)用鼠标双击应用程序窗口右上角的还原按钮

C)用鼠标双击应用程序窗口中的标题栏

D)用鼠标双击应用程序窗口左上角的控制菜单框

解题方法

本题是关于终止应用程序的问题。需要记忆准确,或使用排除法。

知识点分析

上面的例题已经分析,选项A所述的方法并不是终止应用程序的方法。选项B所述的“双击”还原按钮本身是一个错误的操作,因为这个按钮只接受鼠标单击操作,双击没有任何意义。而C选项描述的“双击标题栏”的效果是还原窗口。所以只有选择D答案,其实,D答案的描述也是有问题的,读者可以按该描述操作,可以很容易发现问题。

正确答案

D

难度提示

中级。干扰项不难去除,但正确项有争议,所以对于特别熟悉Windows系统的考生反而容易被迷惑。

489 本 章 练 习

【填空题】

1.为了将当前盘当前目录中的所有文本文件(扩展名为.TXT)的内容打印输出,正确的单条DOS命令为______。

2.设当前盘为C盘。为了在A盘的当前自录\USER下建立一个新的子目录data,正确的DOS命令为______。

3.把B盘BAS子目录下第三个字符为A,且没有扩展名的全部文件拷贝到C盘ABC子目录下的命令是______。

4.启动MS-DOS系统后,能自动执行的批处理文件是______.BAT。

5.设当前盘为C盘,C盘当前目录为\DOS。把JD1.FOR和JD2.FOR连接起来后存入A盘根目录下,命名为JD3.FOR,应使用命令______。

【选择题】

1.在Windows中,启动应用程序的正确方法是( )。

A)用鼠标指向该应用程序图标

B)将该应用程序窗口最小化成图标

C)将该应用程序窗口还原

D)用鼠标双击该应用程序图标

2.Windows应用环境中鼠标的拖动操作不能完成的是( )。

E)当窗口不是最大时,可以移动窗口的位置

F)当窗口最大时,可以将窗口缩小成图标

G)当窗口有滚动条时可以实现窗口内容的滚动

H)可以将一个文件移动或复制到另一个目录中去

【综合题】

设定考试目录为C:\CEKS\2C\DOS,请解答下面的1~6题。

1.将考生目录下FAT16\SPACE子目录中的文件XYZ.BAK更名为XYZ.PRG。

2.将考生目录下AB\C子目录中建立一个新的子目录MODEM。

3.将考生目录下FDISK子目录中的SPACE子目录删除。

4.将考生目录下FAT32\AB子目录中的文件TYPE.C设置成具有系统属性。

5.将考生目录下FAT32\AB子目录中的文件TYPE.BAK删除。

6.将考生目录下FAT32\AB子目录中的文件DRIVE.C和考生目录下AB\C子目录中的文件SIZE.FOR顺序合并

复制到考生目录下AB子目录中,文件名为SIZE.FOR。

490 7.修改和调试程序。给定程序MODI1.C中函数fun的功能是:把在字符串s中出现的每个字符,紧随其后重复出现一次,放在一个新串t中,t中字符按原字符串中逆排列。

例如:当s中的字符串为:"ABCDE"时,则t中的字符串应为:"EEDDCCBBAA"。请改正函数fun中的错误,使它能得出正确的结果。

注意:不要改动main函数,不得增行或删行,也不得更改程序的结构!

程序16-5:P16-5.c

#include <conio.h>

#include <stdio.h>

#include <string.h>

void fun(char *s,char *t)

{

int i,sl;

sl=strlen(s);

/************found************/

for(i=1;i<sl;i++)

{

t[2*i]=s[sl-i-1];

t[2*i+1]=s[sl-i-1];

}

/************found************/

t[2*sl]='0/';

}

main()

{

char s[100],t[100];

clrscr();

printf("\nPlease enter string s:");

scanf("%s",s);

fun(s,t);

printf("The result is: %s\n",t);

}

8.编制程序。

函数fun的功能是:统计各年龄段的人数并存到b数组中,n个人员的年龄放在a数组中。年龄为1~9岁的人数存到b[0]中,年龄为10~19岁的人数存到b[1],年龄为20~29岁的人数存到b[2],年龄为30~39岁的人数存到b[3],年龄为40~49岁的人数存到b[4],年龄为50岁以上的人数存到b[5]中。

例如,当a数组中的数据为:9、18、27、38、59、33、14、75、38。调用该函数后,b数组中存放的数据应是:1、2、1、3、0、2。

注意:部分源程序存在文件PROG1.C中。请勿改动主函数main和其他函数中的任何内容,仅在函数fun的花括号中填入你编写的若干语句。

程序16-6:P16-6.c

#include <conio.h>

#include <stdio.h>

void fun(int a[],int b[],int n)

491 {

/********将程序补充完整********/

}

main()

{

int i,a[100]={9,18,27,38,59,33,14,75,38},b[6];

clrscr();

fun(a,b,9);

printf("The result is: ");

for(i=0;i<6;i++)

printf("%d ",b[i]);

printf("\n");

NONO();

}

NONO()

{

/* 本函数用于打开文件,输入数据,调用函数,输出数据,关闭文件。*/

FILE *rf,*wf;

int a[100],b[6],i,j;

rf=fopen("in2.dat","r");

wf=fopen("bc02.dat","w") ;

for(i=0;i<10;i++)

{

for(j=0;j<9;j++)

fscanf(rf,"%d,",&a[j]);

fun(a,b,9);

for(j=0;j<6;j++)

fprintf(wf,"%d ",b[j]);

fprintf(wf,"\n");

}

fclose(rf);

fclose(wf);

}

9.编制程序。

函数fun的功能是:对a数组中n个人员的工资进行分段统计,各段的人数存到b数组中;工资为1000元以下的人数存到b[0]中,工资为1000元~1999元的人数存到b[1],工资为2000元~2999元的人数存到b[2],工资为3000元~3999元的人数存到b[3],工资为4000元~4999元的人数存到b[4],工资为5000元以上的人数存到b[5]中。

例如,当a数组中的数据为:900、1800、2700、3800、5900、3300、2400、7500、3800。调用该函数后,b中存放的数据应是:1、1、2、3、0、2。

注意:部分源程序存在文件PROG1.C中。

程序16-7:P16-7.c

#include <conio.h>

#include <stdio.h>

void fun(int a[],int b[],int n)

{

/*******在此将函数补充完整*******/

492 }

main()

{

int i,a[100]={ 900,1800,2700,3800,5900,3300,2400,7500,3800};

int b[6];

clrscr();

fun(a,b,9);

printf("The result is: ");

for(i=0;i<6;i++)

printf("%d ",b[i]);

printf("\n");

NONO();

}

NONO( )

{

/* 本函数用于打开文件,输入数据,调用函数,输出数据,关闭文件。*/

FILE *rf,*wf;

int a[100],b[6],i,j;

rf=fopen("in3.dat","r");

wf=fopen("bc03.dat","w");

for(i=0;i<10;i++)

{ for(j=0;j<9;j++)

fscanf(rf,"%d,",&a[j]);

fun(a,b,9);

for(j=0;j<6;j++)

fprintf(wf,"%d ",b[j]);

fprintf(wf,"\n");

}

fclose(rf);

fclose(wf);

}

【本章小结表】

1.你是否按计划预定时间完成了本章复习? 是 □ 否 □

2.本章复习的效果好吗? 不好 □ 还行 □

3.本章疑难问题汇总:

4.对自己说点什么吧:

493 附 录

内容大纲

附录1 全国计算机等级考试简介

附录2 全国计算机等级考试二级(C语言)考试大纲

附录3 20xx年9月全国计算机等级考试二级(C语言)笔试真题

附录4 20xx年4月全国计算机等级考试二级(C语言)笔试真题

附录5 C语言中的关键字

附录6 C语言库函数

附录7 常用字符与ASCII代码对照表 好□

附录8 练习和试题答案

附录1 全国计算机等级考试简介

一、考试性质

全国计算机等级考试是经国家教育委员会批准,由教育部考试中心主办,用于考查应试人员计算机应用知识与能力的等级水平考试。

二、考试目的

举办全国计算机等级考试的目的在于适应社会主义市场经济建设的需要,一方面是为了促进计算机知识的普及和计算机应用技术的推广,另一方面是为劳动力市场服务,即为劳动(就业)人员提供其计算机应用知识与能力的证明,为用人部门录用和考核工作人员提供一个统一、客观、公正的评价标准。

三、组织机构

教育部考试中心聘请全国计算机著名计算机专家组成的“全国计算机等级考试委员会”,负责设计考试方案,审定考试大纲,制定命题原则,指导和监督考试的实施。教育部考试中心负责实施考试,制定有关规章制度,编写考试大纲及相应的辅导材料,命制试题、答案及评分标准,研制考试必需的计算机软件,开展考试研究和宣传等。教育部考试中心在各省(自治区、直辖市)设立省级承办机构负责本地考试的宣传、推广和实施,根据规定设置考点、组织评卷、颁发合格证书等。省级承办机构下设考点负责考生的报名、纸笔考试、上机考试及相关的管理工作,发放成绩通知单和转发合格证书。

四、等级设置

根据目前社会对劳动(就业)人员的计算机应用知识和能力不同层次的需求,以及兼

494顾计算机的学科分类,该考试目前设立了四个等级。

一级:考核微型计算机基础知识和使用办公自动化软件及因特网(Internet)的基本技能。

二级:考核计算机基础知识和使用一种高级计算机语言(包括QBASIC、C、FORTRAN、FoxBASE、Visual Basic、Visual FoxPro)编写程序以及上机调试的基本技能。

三级:分为“PC技术”、“信息管理技术”、“数据库管理”和“网络技术”等四个类别。“PC技术”考核PC机硬件组成,Windows操作系统的基础知识以及PC机使用、管理、维护和应用开发的基本技能。“信息管理技术”考核计算机信息管理应用基础知识,管理信息系统项目和办公自动化系统项目开发、维护的基本技能。“数据库技术”考核数据库系统基础知识及数据库应用系统项目开发和维护的基本技能。“网络技术”考核计算机网络基础知识及计算机网络应用系统开发和管理的基础技能。

四级:考核计算机专业基本知识以及计算机应用项目的分析设计、组织实施的基本技能。

此外,教育部考试中心在部分省(自治区、直辖市)开设了一级B类考试。一级B类考试考核内容和水平与一级相当,完全采取无纸化的上机考试形式。在一级B类的合格证书上将注明“B类”字样。

五、考试形式

考试采用全国统一命题,统一考试时间,笔纸考试和上机操作考试相结合的形式。笔纸考试时间一级、二级Visual Basic、二级Visual FoxPro均为90分钟,二级QBASIC、二级C、二级FORTRAN、二级FoxBASE、三级均为120分钟,四级为180分钟;上机操作考试时间一级、二级QBASIC、二级C、二级FORTRAN、二级FoxBASE、三级、四级均为60分钟,二级Visual Basic、二级Visual FoxPro均为90分钟。一级B类实行无纸化的上机考试,时间为90分钟。

六、考试日期

全国计算机等级考试每年开考两次。上半年开考一二三级,下半年开考一二(除FORTRAN外)、三四级,二级FORTRAN每年纸在上半年开考。上半年考试时间为4月第一个星期六上午(笔试),上机考试从笔试的当天下午开始,由考点具体安排。上机考试期限原则上定为五天。

一级B类考试每年也开考两次。上半年考试时间为四月第一个星期六,下半年考试时间为9月倒数第二个星期六,考试期限原则上定为五天。

七、考生报名

考生不受年龄、职业、学历等背景的限制,任何人均可以根据自己学习和使用计算机的实际情况,选考不同等级的考试。考生一次只能报考一个等级(还笔试和上机考试)的考试。如果一个级别中有不同类别,考生只能选择其中一类。

每次考试报名的具体时间由各省(自治区、直辖市)级承办机构规定。考生按照有关规定到就近考点报名。报名时须交纳考试费。

八、合格证书

495 成绩合格者由教育部考试中心颁发考试合格证书。合格证书用中、英文两种文字书写,全国通用。笔试、上机成绩均为优秀者,合格证书上注明“优秀”字样。合格证书是持有人计算机应用知识和能力的证明,可供用人部门录用和考核工作人员参照,请妥善保存。

九、其他

关于计算机等级考试的详细情况以及考生报名、大纲教材、考试、成绩发放、证书获取等须知和规定,请咨询省级考试承办机构和考点,也可以浏览互联网(网站地址:www.)。

教育部考试中心

20xx年3月

附录2 二级(C语言)考试大纲

基本要求

1.具有计算机的基础知识。

2.了解操作系统的基本概念,掌握常用操作系统的使用。

3.掌握基本数据结构和常用算法,熟悉算法描述工具——流程图的使用。

4.能熟练地使用一种高级语言或数据库语言编写程序、调试程序。

考试内容

一、基础知识与基本操作

(一)基础知识

1.计算机系统的主要技术指标与系统配置。

2.计算机系统、硬件、软件及其相互关系。

3.微机硬件系统的基本组成,包括中央处理器(运算器与控制器)、内存储器(RAM与ROM)、外存储器(硬盘、软盘与光盘)、输入设备(键盘与鼠标)和输出设备(显示器与打印机)。

4.软件系统的组成,系统软件与应用软件;软件的基本概念,文档;程序设计语言与语言处理程序(汇编程序、编译程序、解释程序)。

5.计算机的常用数制(二进制、十六进制及其与十进制之间的转换)、数据基本单位(位、字节、字)。

6.计算机的安全操作、计算机病毒的防治。

7.计算机网络的一般知识。

8.多媒体技术的一般知识。

496 (二)DOS的基本操作

l.操作系统的基本功能与分类。

2.DOS操作系统的基本组成。

3.文件、目录、路径的基本概念。

4.常用DOS操作,包括:

初始化与启动;

文件操作(TYPE,COPY,DEL,REN,XCOPY,ATTRIB);

目录操作(DIR,MD,CD,RD,TREE,PATH);

磁盘操作(FORMAT,DISKCOPY,CHKDSK);

功能操作(VER,DATE,TIME,CLS,PROMPT,HELP);

批处理(批处理文件的建立与执行,自动批处理文件);

输入输出改向。

(三)Windows的基本操作

1.Windows的特点、基本构成及其运行环境。

2.Windows用户界面的基本元素。包括:窗口、图标、菜单、对话框、按钮、光标等。

3.Windows基本操作。包括:启动与退出、鼠标操作、窗口操作、图标操作、菜单操作、对话框操作。

二、程序设计

1.能运用结构化程序设计方法编写程序。

2.掌握基本数据结构和常用算法。

3.能熟练使用一种高级语言或一种数据库语言(共有QBASIC、FORTRAN、C以及FoxBASE+等四种语言,考生任选其中一种。各种语言的考试内容附后)。

三、上机操作

在指定的时间内使用微机完成下述操作:

1.完成指定的计算机基本操作(包括机器启动和操作命令的使用)。

2.按给定要求编写和运行程序。

3.调试程序,包括对给出的不完善的程序进行修改和补充,使之能得到正确的结果。

四、C语言程序设计

(一)C语言的结构

1.程序的构成,main函数和其他函数。

2.头文件、数据说明、函数的开始和结束标志。

3.源程序的书写格式。

4.C语言的风格。

(二)数据类型及其运算

1.C的数据类型(基本类型、构造类型、指针类型、空类型)及其定义方法。

2.C运算符的种类、运算优先级和结合性。

3.不同类型数据间的转换与运算。

497 4.C表达式类型(赋值表达式、算术表达式、关系表达式、逻辑表达式、条件表达式、逗号表达式)和求值规则。

(三)基本语句

1.表达式语句,空语句,复合语句。

2.数据的输入与输出,输入输出函数的调用。

3.复合语句。

(四)选择结构程序设计

1.用if语句实现选择结构。

2.用switch语句实现多分支选择结构。

3.选择结构的嵌套。

(五)循环结构程序设计

1.for循环结构。

2.while和do while循环结构。

3.continue语句和bread语句。

4.循环的嵌套。

(六)数组的定义和引用

1.一维数组和多维数组的定义、初始化和引用。

2.字符串与字符数组。

(七)函数

1.库函数的正确调用。

2.函数的定义方法。

3.函数的类型和返回值。

4.形式参数与实在参数,参数值的传递。

5.函数的正确调用,嵌套调用,递归调用。

6.局部变量和全局变量。

7.变量的存储类别(自动、静态、寄存器、外部),变量的作用域和生存期。

8.内部函数与外部函数。

(八)编译预处理

1.宏定义:不带参数的宏定义;带参数的宏定义。

2.“文件包含”处理。

(九)指针

1.指针与指针变量的概念,指针与地址运算符。

2.变量、数组、字符串、函数、结构的指针以及指向变量、数组、字符串、函数、结构体的指针变量。通过指针引用以上各类型数据。

3.用指针作函数。

4.返回指针值的指针函数。

5.指针数组,指向指针的指针,main函数的命令行参数。

(十)结构体(即“结构”)与共用体(即“联合”)

1.结构体和共用体类型数据的定义方法和引用方法。

498 2.用指针和结构体成链表,单向链表的建立、输出、删除与插入。

(十一)位运算

1.位运算符的含义及使用。

2.简单的位运算。

(十二)文件操作

只要求缓冲文件系统(即高级磁盘I/O系统),对非标准缓冲文件系统(即低级磁盘I/O系统)不要求。

1.文件类型指针(FILE类型指针)。

2.文件的打开与关闭(fopen,fclose)。

3.文件的读写(fputc,fgetc,fputs,fgets,freal,fwrite,fprintf,fscanf函数),文件的定位(rewind,fseek函数)。

附录3 20xx年9月全国计算机等级考试二级

(C语言)笔试真题

(考试时间120分钟,满分100分)

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

下列各题A、B、C、D四个选项中,只有一个选项是正确的,请将正确选项涂写在答题卡相应位置上,答在试卷上不得分。

(1)在32位计算机中,一个字长所占的字节数为( )。

A)1 B)2

C)4 D)8

(2)与十进制数511等值的十六进制数为( )。

A)1FF B)2FF

C)1FE D)2FE

(3)能将高级语言编写的源程序转换成目标程序的是( )。

A)编辑程序 B)编译程序

C)解释程序 D)链接程序

(4)在计算机系统中,存储一个汉字的国标码所需要的字节数为( )。

A)1 B)2

C)3 D)4

(5)下列带有通配符的文件名中,能表示文件ABC.TXT的是( )。

A)*BC.? B)A?.*

C)?BC.* D)?.?

(6)在多媒体计算机系统中,不能用以存储多媒体信息的是( )。

499 A)光缆 B)软盘

C)硬盘 D)光盘

(7)DOS命令“copy /?”的功能是( )。

A)将当前盘当前目录中的所有文件复制到当前盘的根目录下

B)将当前盘当前目录中所有以单个字符命名的文件复制到当前盘的根目录下

C)以提示方式复制文件

D)显示COPY命令的帮助信息

(8)在Windows环境下,若要将当前活动窗口存入剪贴板,则可以按( )。

A)Ctrl十Printscreen键 B)Alt+Printscreen键

C)Sift+Printscreen键 D)Printscreen键

(9)在Windows环境下,单击当前应用程序窗口中的“关闭”按钮,其功能是( )。

A)将当前应用程序转为后台运行

B)退出Windows后再关机

C)退出Windows后重新启动计算机

D)终止当前应用程序的运行

(10)在Windows环境下,粘贴按钮是( )。

A) B)

C) D)

(11)以下叙述中正确的是( )。

A)构成C程序的基本单位是函数

B)可以在一个函数中定义另一个函数

C)main()函数必须放在其它函数之前

D)所有被调用的函数一定要在调用之前进行定义

(12)以下选项中合法的实型常数是( )。

A)5E2.0 B)E-3

C).2E0 D)1.3E

(13)以下选项中合法的用户标识符是( )。

A)long B)_2Test

C)3Dmax D)A.dat

(14)已知大写字母A的ASCII码值是65,小写字母a的ASCII码是97,则用八进制表示的字符常量"\101"是( )。

A)字符A B)字符a

C)字符e D)非法的常量

(l5)以下非法的赋值语句是( )。

A)n=(i=2,++i); B)j++;

C) ++(1+1) D)x=j>0;

(16)设a和b均为double型变量,且a=5.5、b=2.5,则表达式(int)a+b/b的值是( )。

500 A)6.500000 B)6

C)5.500000 D)6.000000

(l7)己知i、j、k为int型变量,若从键盘输入:1,2,3 <回车>,使i的值为1、j的值为2、k的值为3,以下选项中正确的输入语句是( )。

A)Scanf("%2d%2d%2d",&i,&j,&k); B)scanf("%d %d %d,&i,&j,&k);

C)Scanf("%d,%d,%d",&i,&j,&k); D)scanf("i=%d,j=%d,k=%d,&i,&j,&k)k;(18)与数学式子3x/(2x-1)对应的C语言表达式是( )。n

A)3*x^n/(2*x-1) B)3*x**n/(2*x-1)

C)3*pow(x,n)*(1/(2*x-1)) D)3*pow(n,x)/(2*x-1)

(19)设有定义:long x=-123456L;,则以下能够正确输出变量x值的语句是( )。

A)printf("x=%d\n",x); B)printf("x=%ld\n",x);

C)rpintf("x=%8dl\n",x); D)printf("x=%LD\n",x);

(20)若有以下程序:

main()

{int k=2,i=2,m;

m=(k+=i*=k); printf("%d,%d\n",m,i);

执行后的输出结果是( )。

A)8,6 B)8,3

C)6,4 D)7,4

(21)已有定义:int x=3,y=4,z=5;,则表达式!(x+y)+z-1 && y+z/2的值是( )。

A)6 B)0

C)2 D)1

?1,x>0

(22)有一函数:y=?0,x=0 以下程序段中不能根据x值正确计算出y值的是( )。

?

??1,x<0

A)if(x>0) y=1; B)y=0;

else if(x==0) y=0; if(x>0)y=l;

else y=-l; else if(x<0) y=-1;

C)y=0; D)if(x>=0)

if(x>=0) if(x>0) y=1;

if (x>0) y=l; else y=0;

else y=-1; else y=-1;

(23)以下选项中,与k=n++完全等价的表达式是( )。

A)k=n,n=n+1 B)n=n+1,k=n

C)k=++n D)k+=n+1

(24)以下程序的功能是:按顺序读入10名学生4门课程的成绩,计算出每位学生的平均

分并输出,程序如下:

501 main()

{int n,k;

float score,sum,ave;

sum=0.0;

for(n=1;n<=10;n++)

{for(k=1;k<=4;k++)

{scanf("%f",&score); sum+=score;}

ave=sum/4.0;

printf("NO%d:%f\n",n,ave);

}

上述程序运行后结果不正确,调试中发现有一条语句出现在程序中的位置不正确。这条语句是(

A)sum=0.0; B)sum+=score;

C)ave=sum/4.0; D)printf("NO%d:%f\n",n,ave);

(25)有以下程序段

int n=0,p;

do {scanf("%d",&p);n++;} while(p!=12345 && n<3);

此处do-while循环的结束条件是( )。

A)P的值不等于12345并且n的值小于3

B)p的值等于12345并且n的值大于等于3

C)p的值不等于12345或者n的值小于3

D)p的值等于12345或者n的值大于等于3

。 )

(26)有以下程序

main()

{int a=15,b=21,m=0;

switch(a%3)

{case 0:m++; break;

case 1:m++;

switch(b%2)

{default:m++;

case 0:m++;break;

printf("%d\n",m);

程序运行后的输出结果是( )。

A)1 B)2

C)3 D)4

(27)C语言中,函数值类型的定义可以缺省,此时函数值的隐含类型是(

A)void B)int

C)float D)double

(28)若有说明:int n=2,*p=&n,*q=p;,则以下非法的赋值语句是( )。

502 A)p=q; B)*p=*q;

C)n=*q; D)p=n;

。 )

(29)有以下程序

float fun(int x,int y)

{return(x+y);}

main()

{int a=2,b=5,c=8;

printf("%3.0f\n",fun((int)fun(a+c,b),a-c));

}

程序运行后的输出结果是( )。

A)编译出错 B)9

C)21 D)9.0

(30)有以下程序

void fun(char *c,int d)

{*C=*c+1;d=d+1;

printf("%C,%c,",*c,d);

}

main()

{char a='A',b='a';

fun(&b,a);printf("%c,%c\n",a,b);

}

程序运行后的输出结果是( )。

A)B,a,B,a B)a,B,a,B

C)A,b,A,b D)b,B,A,b

(31)以下程序中函数 sort的功能是对a所指数组中的数据进行由大到小的排序

void sort(int a[],int n)

{ int i,j,t;

for(i=0;i<n-1;i++)

for(j=i+1;j<n;j++)

if(a[i]<a[j]){t=a[i];a[i]=a[j];a[j]=t;}

}

main()

{int aa[10]={1,2,3,4,5,6,7,8,9,10},i;

sort(&aa[3],5);

for(i=0;i<10;i++) printf("%d,",aa[i]);

printf("\n");

程序运行后的输出结果是( )。

A)1,2,3,4,5,6,7,8,9,10 B)10.9.8.7.6.5.4.3.2.1.

C)1,2,3,8,7,6,5,4,9,10, D)1,2,10,9,8,7,6,5,4,3,

(32)有以下程序

int f(int n)

503 {if (n==1) return 1;

else return f(n-1)+1;

}

main()

{int i,j=0;

for(i=1;i<3;i++) j+=f(i);

printf("%d\n",j);

}

程序运行后的输出结果是( )。

A)4 B)3

C)2 D)l

(33)有以下程序

main()

{char a[]={'a','b','c','d','e','f','g','h','\0'};int i,j;

i=sizeof(a); j=strlen(a);

printf("%d,%d\n",i,j);

}

程序运行后的输出结果是( )。

A)9,9 B)8,9

C)1,8 D)9,8

(34)以下程序中函数。reverse的功能是将a所指数组中的内容进行逆置。

void reverse(int a[],int n)

{int i,t;

for(i=0;i<n/2;i++)

{t=a[i];a[i]=a[n-1-i];a[n-1-i]=t;}

}

main()

{int b[10]={1,2,3,4,5,6,7,8,9,10}; int i,s=0;

reverse(b,8);

for(i=6;i<10;i++) s+=b[i];

printf("%d\n",s);

}

程序运行后的输出结果是( )。

A)22 B)10

C)34 D)30

(35)有以下程序

main()

{int aa[4][4]={{1,2,3,4},{5,6,7,8,},{3,9,10,2},{4,2,9,6}};

int i,s=0;

for(i=0;i<4;i++) s+=aa[i][1];

printf("%d\n",s);

}

504 程序运行后的输出结果是( )。

A)11 B)19

C)13 D)20

(36)有以下程序

#include<string.h>

main()

{char *p="abcde\0fghjik\0";

printf("%d\n",strlen(p));

}

程序运行后的输出结果是( )。

A)12 B)15

C)6 D)5

(37)程序中头文件type1.h的内容是:

#defille N 5

#define M1 N*3

程序如下:

#include "type1.h"

#define M2 N*2

main()

{int i;

i=M1+M2; printf("%d\n",i);

}

程序编译后运行的输出结果是:( )。

A)10 B)20

C)25 D)30

(38)有以下程序

#include<stdio.h>

main()

{FILE *fp; int i=20,j=30,k,n;

fp=fopen("d1.dat","w");

fprintf(fp,"%d\n",i);fprintf(fp,"%d",j);

fclose(fp);

fp=fopen("d1.dat","r");

fscanf(fp,"%d%d”,&k,&n);printf("%d%d\n",k,n);

fclose(fp);

}

程序运行后的输出结果是( )。

A)20 30 B)20 50

C)30 50 D)30 20

(39)以下叙述中错误的是( )。

A)二进制文件打开后可以先读文件的末尾,而顺序文件不可以

B)在程序结束时,应当用fclose函数关闭已打开的文件

505 C)在利用read函数从二进制文件中读数据时,可以用数组名给数组中所有元素读入

数据

D)不可以用FILE定义指向M进制文件的文件指针

(40)有以下程序

#include <string.h>

main(int argc,char *argv[])

{int i,len=0;

for(i=1;i<argc;i++) len+=strlen(argv[i]);

printf("%d\n",len);

}

程序编译连接后生成的可执行文件是exl.exe,若运行时输入带参数的命令行是:

exl abcd efg 10 <回车>

则运行的结果是:( )。

A)22 B)17

C)12 D)9

(41)有以下程序

int fa(int x)

{return x*x;}

int fb(int x)

{return x*x*x;}

int f(int (*f1)(),int (*f2)(),int x)

{return f2(x)-f1(x);}

main()

{int i;

i=f(fa,fb,2); printf("%d \n",i);

}

程序运行后的输出结果是( )。

A)-4 B)1

C)4 D)8

(42)有以下程序

int a=3;

main()

{int s=0;

{int a=5; s+=a++;}

s+=a++; printf("%d\n",s);

}

程序运行后的输出结果是( )。

A)8 B)10

C)7 D)11

(43)有以下程序

void ss(char *s,char t)

506 {while(*s)

{if(*s==t) *s=t-'a'+'A';

s++;

}

}

main()

{char str1[100]="abcddfefdbd",c='d';

ss(str1,c); printf("%s\n",str1);

}

程序运行后的输出结果是( )。

A)ABCDDEFEDBD B)abcDDfefDbD

C)abcAAfefAbA D)Abcddfefdbd

(44)有以下程序

struct STU

{char num[10]; float score[3]; };

main()

{struct STU s[3]={{"20021",90,95,85},

{"20022",95,80,75},

{"20023",100,95,90}},*p=s;

int i; float sum=0;

for (i=0;i<3;i++)

sum=sum+p->score[i];

printf("%6.2f\n",sum);

}

程序运行后的输出结果是( )。

A)260.00 B)270.00

C)280.00 D)285.00

(45)设有如下定义:

stuct sk

{int a;

float b;

}data;

int *p;

若要使p指向data中的a域,正确的赋值语句是( )。

A)P=&a; B)p=data.a;

C)p=&data.a; D)*p=data.a;

(46)有以下程序

#include <stdlib.h>

struct NODE

{int num;struct NODE *next;};

main()

{struct NODE *p,*q,*r;

p=(struct NODE *)malloc(sizeof(struct NODE));

507 q=(struct NODE *)malloc(sizeof(struct NODE));

r=(struct NODE *)malloc(sizeof(struct NODE));

P->num=10; q->num=20; r->num=30;

P->next=q; q->next=r;

printf("%d\n",p->num+q->num);

}

程序运行后的输出结果是( )。

A)10 B)20

C)30 D)40

(47)若有以下说明和定义

typdef int *INTEGER;

INTGER p,*q;

以下叙述正确的是( )。

A)p是int型变量

B)p是基类型为int的指针变量

C)q是基类型为int的指针变量

D)程序中可用INTEGER代替int类型名

(48)有以下程序

main()

{unsigned char a,b,c;

a=0x3; b=a|0x8; c=b<<1;

printf("%d %d\n",b,c);

}

程序运行后的输出结果是( )。

A)-11 12 B)-6 -13

C)12 24 D)11 22

(49)有以下程序

#include <stdlib.h>

main()

{ char *p,*q;

p=(char *)malloc(sizeof(char)*20);q=p;

scanf("%s %s",p,q); printf("%s %s\n",p,q);

}

若从键盘输入:abc def <回车>,则输出结果是:( 。 )

A)def def B)abc def

C)abc d D)d d

(50)以下程序中函数f的功能是将n个字符串按由大到小的顺序进行排序。

#include <string.h>

void f(char p[][10],int n)

{ char t[20];int i,j;

for(i=0;i<n-1;i++)

for(j=i+1;j<n;j++)

508 if(strcmp(p[i],p[j])<0)

{strcpy(t,p[i]); strcpy(p[i],p[j]);strcpy(p[j],t);}

}

main()

{ char p[][10]={"abc","aabdfg","abbd","dcdbe","cd"};int i;

f(p,5); printf("%d\n",strlen(p[0]));

}

程序运行后的输出结果是( )。

A)6 B)4

C)5 D)3

二、填空题(每空2分,共40分)

请将每一个空的正确答案写在答案卡上,答在试卷上不得分。

(1)计算机软件分为系统软件和应用软件,操作系统属于【1】。

(2)在DOS环境下,代表键盘和显示器的设备文件名为【2】。

(3)支持基本服务的协议是【3】。

(4)从Windows环境进入MS-DOS方式后,返回Windows环境的Dos命令为【4】。(5)某微型机的运算速度为2MIPS,则该微型机每秒执行【5】条指令。

(6)设有定义:int n,*k=&n;以下语句将利用指针变量k读写变量n中的内容,请将语句补充完整。

scanf("%d",【6】);

printf("%d\n",【7】);

(7)以下程序运行后的输出结果是【8】。

main()

{int x=10,y=20,t=0;

if (x==y) t=x;x=y;y=t;

printf("%d,%d\n",x,y);

}

(8)以下程序运行后的输出结果是【9】。

main()

{int x=15;

while (x>10 && x<50)

{x++;

if(x/3) {x++;break;}

else continue;

}

printf("%d\n",x);

}

(9)有以下程序:

#include <stdio.h>

main()

{char c;

while((c=getchar())!='?') putchar(--c);

509 }

程序运行时,如果从键盘输入:Y?N?<回车>,则输出结果为【10】。

(10)以下程序运行后的输出结果是【11】。

Void fun(int x,int y)

{x=x+y;y=x-y;x=x-y;

printf("%d,%d,",x,y);}

main()

{int x=2,y=3;

fun(x,y);

printf("%d,%d\n",x,y);

}

(11)以下函数的功能是计算s=1+1+1+?+1,请填空。

2! 3! n!

double fun(int n)

{double s=0.0,fac=l.0;int i;

for(i=1;i<=n;i++)

{ fac=fac【12】;

s=s+fac;

}

return s;

}

(12)fun函数的功能是:首先对a所指的N行N列的矩阵,找出各行中的最大的数,再

求这N个最大值中的最小的那个数作为函数值返回。请填空。

#include <stdio.h>

define N 100

int fun(int(*a)[N])

{int row,col,max,min;

for(row=0;row<n;row++)

{for(mac=a[row][0],col=1;col<n;col++)

if(【13】) max=a[row][col];

if(row==0) min=max;

else if(【14】) min=max;

}

return min;

}

(13)函数 sstrcmp()的功能是对两个字符串进行比较。当S所指字符串和t所指字符串相等时,返回值为0;当s所指字符串大于t所指字符串时,返回值大于0:当s所指字符串小于t所指字符串时,返回值小于0(功能等同于库函数Strcmp()。请填空。

#include <stdio.h>

int sstrcmp(char *s,char *t)

{while(*s && *t && *s==【15】)

{s++,t++;}

return 【16】;

}

510(14)下面程序的运行结果是:【17】

#define N 10

#define s(x) x*x

#define f(x) (x*x)

main()

{int i1,i2;

i1=1000/s(N); i2=1000/f(N);

printf(“%d %d\n",i1,i2);

}

(15)下面程序的运行结果是:【18】。

void swap(int *a,int *b)

{int *t;

t=a; a=b; b=t;

}

main()

{int x=3,y=5,*p=&x,*q=&y;

swap(p,q);

printf("%d%d\n",*p,*q);

}

(16)下面程序的运行结果是:【19】。

typedef union student

{char name[10];

long sno;

char sex;

float score[4];

}stu;

main()

{STU a[5];

printf("%d\n",sizeof(a));

}

(17)若fp己正确定义为一个文件指针,dl.dat为二进制文件,请填空,以便为“读”而打开此文件:fp=fopen(【20】);。

附录4 20xx年4月全国计算机等级考试二级

(C语言)笔试真题

(考试时间120分钟,满分100分)

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

下列各题A、B、C、D四个选项中,只有一个选项是正确的,请将正确选项涂写在答

511题卡相应位置上,答在试卷上不得分。

1.用8位无符号二进制数能表示的最大十进制数为( )。

A)127 B)128

C)255 D)256

2.在64位高档微机中,一个字长所占的二进制位数为( )。

A)8 B)16

C)32 D)64

3.在Windows环境下,为了复制一个对象,在用鼠标拖动该对象时应同时按住( )。

A)Alt键 B)Esc键

C)Shift键 D)Ctrl键

4.在Windows菜单中,暗淡(灰色)的命令项表示该命令( )。

A)暂时不能用 B)正在执行

C)包含下一层菜单 D)包含对话框

5.在DOS环境下,为了得到TYPE命令的帮助信息,正确的DOS命令为(

A)TYPE B)TYPE/H

C)TYPE/* D)TYPE/?

6.下列选项中,能作为合法DOS文件名的是( )。

A)ANP/A.DAT B)ABC.COM

C)ECD.BASIC D)XY+Z.TXT

7.下列叙述中正确的是( )。

A)在Windows环境下,最大化的窗口是不能移动的

B)在Windows环境下,应用程序窗口最小化后,该应用程序暂停执行

C)在Windows环境下,只有最小化的窗口才能关闭

D)在Windows环境下,不能关闭最大化的窗口

8.在Windows环境下,工具栏中的复制按钮是( )。

A) B) 。 )

C) D)

9.在Windows98环境下,若要将整个屏幕上显示的内容存入剪贴板,可以按( )。

A)Ctrl+PrintScreen键 B)Alt+PrintScree键

C)Shift+PrintScreen键 D)PrintScreen键

10.目前,计算机病毒扩散最快的途径是( )。

A)通过软件复制 B)通过网络传播

C)通过磁盘考贝 D)运行游戏软件

11.以下叙述中正确的是( )。

A)C语言比其他语言高级

B)C语言可以不用编译就能被计算机识别执行

C)C语言以接近英语国家的自然语言和数学语言作为语言的表达形式

512D)C语言出现的最晚,具有其他语言的一切优点

12.C语言中用于结构化程序设计的三种基本结构是( )。

A)顺序结构、选择结构、循环结构

B)if、switch、break

C)for、while、do-while

D)if、for、continue

13.在一个C程序中( )。

A)main函数必须出现在所有函数之前

B)main函数可以在任何地方出现

C)main函数必须出现在所有函数之后

D)main函数必须出现在固定位置

14.下列叙述中正确的是( )。

A)C语言中既有逻辑类型也有集合类型

B)C语言中没有逻辑类型但有集合类型

C)C语言中有逻辑类型但没有集合类型

D)C语言中既没有逻辑类型也没有集合类型

15.下列关于C语言用户标识符的叙述中正确的是( )。

A)用户标识符中可以出现在下划线和中划线(减号)

B)用户标识符中不可以出现中划线,但可以出现下划线

C)用户标识符中可以出现下划线,但不可以放在用户标识符的开头

D)用户标识符中可以出现在下划线和数字,它们都可以放在用户标识符的开头16.若有以下程序段(n所赋的是八进制数)

int m=32767,n=032767;

printf("%d,%o/n",m,n);

执行后输出结果是( )。

A)32767,32767 B)32767,032767

C)32767,77777 D)32767,077777

17.下列关于单目运算符++、――的叙述中正确的是( )。

A)它们的运算对象可以是任何变量和常量

B)它们的运算对象可以是char型变量和int型变量,但不能是float型变量C)它们的运算对象可以是int型变量,但不能是double型变量和float型变量D)它们的运算对象可以是char型变量、int型变量和float型变量

18.若有以下程序段

int m=0xabc, n=0xabc;

m-=n;

printf("%X\n",m);

执行后输出结果是( )。

A)0X0 B)0x0

513C)0 D)0XABC

19.有以下程序段

int m=0,n=0;

char c='a';

scanf("%d%c%d",&m,&c,&n);

printf("%d,%c,%d\n",m,c,n);

若从键盘上输入:10A10<回车>,则输出结果是(

A)10,A,10 B)10,a,10

C)10,a,0 D)10,A,0

20.有以下程序

main()

{

int i;

for(i=0;i<3;i++)

switch(i)

{

case1: printf("%d",i);

case2: printf("%d",i);

。 )

default: rintf("%d",i);

}

}

执行后输出结果是( )。

A)011122 B)012

C)012020 D)120

21.有以下程序

main()

{

int i=1,j=1,k=2;

if((j++‖k++)&&i++)

printf("%d,%d,%d\n",i,j,k);

}

执行后输出结果是( )。

A)1,1,2 B)2,2,1

C)2,2,2 D)2,2,3

22.有以下程序

main()

{

int a=5,b=4,c=3,d=2;

if(a>b>c)

printf("%d\n",d);

else if((c-1>=d)==1)

printf("%d\n",d+1);

514 else

printf("%d\n",d+2)

}

执行后输出结果是( )。

A)2 B)3

C)4 D)编译时有错,无结果

23.有以下程序

main()

{

int p[7]={11,13,14,15,16,17,18},i=0,k=0;

while(i<7 && p[i]%2)

{

k=k+p[i];

i++;

}

printf("%d\n",k);

}

执行后输出结果是( )。

A)58 B)56

C)45 D)24

24.有以下程序

main()

{

int i=0,s=0;

do{

if(i%2)

{

i++;

continue;

}

i++;

s+=i;

}

while(i<7);

printf("%d\n",s);

}

执行后输出结果是( )。

A)16 B)12

C)28 D)21

25.有以下程序

main()

{

int i=10,j=1;

printf("%d,%d\n",i--,++j);

515}

执行后输出结果是( )。

A)9,2 B)10,2

C)9,1 D)10,1

26.有以下程序

main()

{

char a,b,c,*d;

a='\'; b='\xbc';

c='\0xab' ; d="\017";

print("%c%c%c\n",a,b,c,*d);

}

编译时出现错误,以下叙述中正确的是(

A)程序中只有a='\';语句不正确

B)b='\xbc';语句不正确

C)d="\0127";语句不正确

D)a='\';和c='\0xab';语句都不正确

27.有以下程序

int fl(int x, int y)

{

。 )

return x>y?x:y;

}

int f2(int x, int y)

{

return x>y?y:x;

}

main()

{

int a=4,b=3,c=5,d,e,f;

d=f1(a,b); d=f1(d,c);

e=f2(a,b); e=f2(e,c);

f=a+b+c-d-e;

printf("%d,%d,%d\n",d,f,e);

}

执行后输出结果是( )。

A)3,4,5 B)5,3,4

C)5,4,3 D)3,5,4

28.有如下程序

void f(int x, int y)

{

int t;

if (x<y)

{

516 t=x; x=y; y=t;

}

}

main()

{

int a=4,b=3,c=5;

f (a,b); f (a,c); f (b,c);

printf("%d,%d,%d\n",a,b,c);

}

执行后输出结果是( )。

A)3,4,5 B)5,3,4

C)5,4,3 D)4,3,5

29.若有以下定义和语句

#include <stdio.h>

int a=4,b=3,*p,*q,*w;

p=&a; q=&b; w=q; q=NULL;

则以下选项中错误的语句是( )。

A)*q=0; B)w=p;

C)*p=a; D)*p=*w;

30.有以下程序

int *f(int *x, int *y)

{

if (*x<*y)

return x;

else

return y;

}

main()

{

int a=7,b=8,*p,*q,*r

p=&a; q=&b;

r=f (p,q);

printf ("%d,%d,%d\n " ,*p, *q *r);

}

执行后输出结果是( )。

A)7,8,8 B)7,8,7

C)8,7,7 D)8,7,8

31.有以下程序

main()

{

char *s[]={"one","two","three"},*p;

p=s[1];

517 printf("%c,%s\n", *(p+1), s[0]);

}

执行后输出结果是( )。

A)n,two B)t,one

C)w,one D)o,two

32.有以下程序

main( )

{

int x[8]={8,7,6,5,0,0},*s;

s=x+3;

printf("%d\n",s[2]);

}

执行后输出结果是( )。

A)随机值 B)0

C)5 D)6

33.以下能正确定义数组并正确赋初值的语句是( )。

A)int N=5,b[N][N]; B)int a[1][2]={{1},{3}};

C)int c[2][]={{1,2},{3,4}}; D)int d[3][2]={{1,2},{34}};

34.有以下程序

main()

{

int m[][3]={1,4,7,2,5,8,3,6,9}

int i,j,k=2;

for( i=0; i<3; i++ )

{

printf("%d",m[k][i]);}

}

}

执行后输出结果是( )。

A)456 B)258

C)369 D)789

35.以下函数的功能是:通过键盘输入数据,为数组中的所有元素赋值。

#define N 10

void arrin( int x[N])

{

int i=0;

while(i___)

scanf("%d",);

}

在下划线处应填入的是( )。

518A)x+I B)&x[i+1]

C)x+(i++) D)&x[++i]

36.有以下程序

main()

{

char s[]="\n123\\";

printf("%d,%d\n",strlen(s),sizeof(s));

}

执行后输出结果是( )。

A)赋初值的字符串有错 B)6,7

C)5,6 D)6,6

37.阅读以下函数

fun( char*s1, char*s2)

{

int i=0;

while(s1[i]==s2[i]&& s2[i]!='\0') i++;

return(s1[i]==&& s2{i}!=='\0');

}

此函数的功能是( )。

A)将s2所指字符串赋给s1

B)比较s1和s2所指字符串的大小,若s1比s2的大,函数值为1,否则函数值为0C)比较s1和s2所指字符串是否相等,若相等,函数值为1,否则函数值为0D)比较s1和s2所指字符串的长度,若s1比s2的长,函数值为1,否则函数值为038.以下叙述中正确的是( )。

A)全局变量的作用域一定比局部变量的作用域范围大

B)静态(static)类别变量的生存期贯穿于整个程序的运行期间

C)函数的形参都属于全局变量

D)未在定义语句中赋初值的auto变量和static变量的初值都是随机值

39.设有如下说明

typedef struct

{int n; char c; double x;} STD;

则以下选项中,能正确定义结构体数组并赋初值的语句是( )。

A)STD tt[2]={{1,'A',62},{2,'B',75}};

B)STD tt[2]={1,"A",62},2,"B",75};

C)struct tt[2]={{1,'A'},{2,'B'}};

D)struct tt[2]={{1,"A",62.5},{2,"B",75.0}};

40.有以下程序

main( )

{

519 union{

unsigned int n;

unsigned char c;

}u1;

ul.c=`A`;

printf("%c\n",u1.n);

}

执行后输出结果是( )。

A)产生语法错 B)随机值

C)A D)65

41.有以下程序

main()

{

char str[]="xyz", *ps=str;

while(*ps) ps++;

for(ps--;ps-str>=0;ps--)

puts(ps);

}

执行后的输出结果是( )。

A)yz B)z C)z D)x

xyz yz yz xy

xyz xyz

42.有以下程序

main( )

{

int a[][3]={{1,2,3},{4,5,0}}, (*pa)[3], i;

pa=a;

for( i=0; i<3; i++)

if( i<2 )

pa[1][i]=pa[1][i]-1;

else

pa[1][i]=1;

printf("%d\n",a[0][1]+a[1][1]+a[1][2]);

}

执行后输出结果是( )。

A)7 B)6

C)8 D)无确定值

43.有以下程序

void fun( int *a, int i, int j)

{

intt ;

if ( i<j )

{

520 t=a[i];a[i]=a[j];a[j]=t;

fun(a,++i,--j);

}

}

main()

{

int a[]={1,2,3,4,5,6},i;

fun(a,0,5)

for( i=0; i<6; i++)

printf("%d",a[i]);

}

执行后的输出结果是( )。

A)654321 B)432156

C)456123 D)123456

44.有以下程序

main(int arge, char *argv[])

{

int n,i=0;

while(arv[1][i]!='\0'

{

n=fun( );i++;

}

printf(%d\n", n*argc)

}

int fun()

{

static int s=0;

s+=1;

return s;

}

假设程序经编译、连接后生成可执行文件exam.exe,若键入以下命令行

exam 123<回车>

则运行结果为( )。

A)6 B)8

C)3 D)4

45.以下程序段中,能够通过调用函数fun,使main函数中的指针变量p指向一个合法的整型单元的是( )。

A)main() B)main

{ int*p; { int*p;

fun(p); fun(&p);

??? ???

} }

int fun(int *p) int fun(int **p)

521 { int s; p=&s; } {int s; *p=&s; }

C)#include <stdlib.h> D)#include <stdlib.h>

main() main()

{ int *p; { int *p;

fun(&p); fun(p);

??? ???

} }

int fun(int **p) int fun(int *p)

{ *p=(int*)malloc(2);} {p=(int*)malloc(sizeo(int));}

46.若要说明一个类型名STP,使得定义语句STP s等价于char *s,以下选项中正确的是(

A)typedef STP char *s; B)typedef *char STP;

C)typedef STP *char; D)typedef char* STP;

47.设有如下定义

struct ss

{ char name[10];

。 )

int age;

char sex;

}std[3],*p=std;

下面各输入语句中错误的是( )。

A)scanf("%d",&(*p).age); B)scanf("%s",&std.name);

C)scanf("%c",&std[0].sex) D)scanf("%c",&(p->sex));

48.设char型变量x中的值为10100111,则表达式(2+x)^ (~3)的值是( )。

A)10101001 B)10101000 C)11111101 D)01010101

49.以下叙述中不正确的是( )。

A)C语言中的文本文件以ASCⅡ码形式存储数据

B)C语言中对二进制文件的访问速度比文本文件快

C)C语言中,随机读写方式不适用于文本文件

D)C语言中,顺序读写方式不适用于二进制文件

50.以下程序企图把从终端输入的字符输出到名为abc.txt的文件中,直到从终端读入字符#号时结束输入和输出操作,但程序有错。

#include <stdio.h>

main()

{ FILE *fout; char ch;

fout=fopen('abc.txt' , 'w');

ch=fgetc(stdin);

while (ch!='#')

{ fputc (ch,fout);

ch=fgetc(stdin);

}

522 fclose (fout);

}

出错的原因是( )。

A)函数fopen调用形式错误 B)输入文件没有关闭

C)函数fgetc调用形式错误 D)文件指针stdin没有定义

二、填空题(每空2分,共40分)

请将每一个空的正确答案写在答案卡上,答在试卷上不得分。

1.用十六进制绘存储器中的字节地址进行编号,若地址编号从000到FFFF,则该存诸器的容量为【1】KB。

2.假设在当前盘的当前目录下有两个文件A.TXT和B.TXT,现要将文件B.TXT合并连接到文件A.TXT的后面。若使用COPY命令,则完整的命令为【2】。

3.E-mail地址由用户和域名两部分组成,这两部分的分隔符为【3】。

4.假设在当前盘当前目录下有一个可执行程序USER.EXE。现要执行该程序,并要求在执行过程中将显示输出的结果信息存入当前盘当前目录的文件 OUT.DAT 中,则完整的DOS命令为【4】。

5.在DOS环境下,表示打印机的设备文件名为【5】。

6.若有语句

int i=-19, j=i%4;

printf("%d\n",j);

则输出结果是【6】。

7.若有程序

main()

{

int i, j;

scanf("i=%d,j=%d";&i,&j);

printf("i=%d,j=%d\n",i,j);

}

要求给i赋10,给j赋20,则应该从键盘输入【7】。

8.若有以下程序

main()

{

int p,a=5;

if(p=a!=0)

printf("%d\n",p);

else

printf("%d\n",p+2);

}

执行后输出结果是【8】。

9.若有以下程序

523main()

{

int a=4, b=3, c=5, t=0;

if(a<b) t=a; a=b; b=t;

if(a<c) t=a; a=c; c=t;

printf("%d%d%d\n",a,b,c);

}

执行后输出结果是【9】。

10.若有以下程序

main()

{

int

a[4][4]={{1,2,-3,-4},{0,-12,-13,14},{-21,23,0,-24},{-31,32,-33,0}};

int i, j, s=0;

for( i=0; i<4; i++ )

{

for( j=0; j<4; j++ )

{

if (a[i][j]<0) continue;

if (a[i][j]==0) break;

s+=a[i][j];

}

}

printf("%d\n",s);

}

执行后输出的结果是【10】 。

11.若有以下程序

main()

{

char a;

a='H' - 'A' + '0' ;

print("%c\n",a);

}

执行后的输出结果是【11】。

12.若有以下程序

int f(int x, int y)

{

return (y-x)*x;

}

main()

{

int a=3,b=4,c=5,d;

d=f(f(3,4),f(3,5));

printf("%d\n",d);

}

524 执行后输出的结果是【12】。

13.函数YangHui的功能是把杨辉三角形的数据赋给二维数组的下半三角,形式如下

1

1 1

1 2 1

1 3 3 1

1 4 6 4 1

其构成规律是:第0列元素和主对角线无素均为1其余元素为其左上方和正上方元素之和数据的个数每行递增1。请将程序补充完整。

#defint N 6

void YangHui( int*[N][N])

{

int i,j;

x[0][0]=1

for(i=1; i<N; i++)

{

x[i][0]=【13】=1

for(j=1; j<i; j++ )

x[i][j]=【14】;

}

}

14.以下函数的功能是删除字符串s中的所有数字字符。请填空。

void dele (char *s)

{

int n=0,i;

for (i=0, s[i]; i++)

if (【15】)

s[n++]=s[i];

s[n]=【16】;

}

15.设函数findbig已定义为求3个数中的最大值。以下程序将利用函数指针调用findbig函数。请填空。

main()

{

int findbig(int, int, int);

int(*f)(), x, yz, z, big;

f=【17】;

scanf("%d%d%d", &x, &y, &z);

big=(*f)(x,y,z);

printf("big=%d\n",big);

}

16.以下程序的输出结果是【18】

#defint MCRA(m) 2*m

525 #define MCRB(n,m) 2*MCRA(n)+m

main()

{

int i=2, j=3;

printf("%d\n",MCRB(j,MCRA(i)));

}

17.设有以下定义

struct ss

{ int info; struct ss *link; }x, y, z;

且已建立如下图所示链表结构:

请写出删除点y的赋值语句【19】。

18.已有文本文件test.txt,其中的内容为:Hello,everyone !。以下程序中,文件test.txt已正确为"读"而打开,由文件指针fr指向该文件,则程序的输出结果是【20】。

#include <stdio.h>

main()

{

FILE *fr ; char str[40];

??

fgets(str,5,fr);

printf("%s\n",str);

fclose(fr);

}

附录5 C语言中的关键字

auto break case char constcontinue default do double else

enum extern float for goto

if int long register returnshort signed sizeof static struct

switch typedef union unsigned void

volatile while

526 附录6 C语言库函数

首先应该说明的是,库函数并不是C语言的一部分,它是由人们根据需要编制并提供给用户使用的。由于每一个C编译系统都提供了一批库函数,不同的编译系统所提供的库函数的数目和函数名以及函数功能是不完全相同的。ANSI C标准提出了一批建议提供的标准库函数。C语言的库函数的种类和数目很多,这里只列出最基本的库函数。读者在编制C程序时如果用到其他的库函数可以查阅所使用的C编译系统的手册。

一、数学函数

使用数学函数时,需要在该源文件中使用文件包含命令:

#include "math.h"

表10-1 数学函数

函数名 函数类型和形参类型 功 能 返回值 说 明abs int abs(i) 计算整型参数i的绝对值 计算结果

int i;

double acos(x) -1

计算反余弦cos(x)值 计算结果 x~(-1, 1)acos double x;

double asin(x) 计算结果

计算反正弦cos(x)值 -1 x~(-1, 1)asin double x;

double atan(x) 计算结果

计算反正切tan(x)值-1

atan double x;

double atan2(x,y) 计算结果

atan2 -1

double x, y; 计算y/x的反正切tan(x)值

double cos(x) 计算结果

cos double x; 计算x的余弦cos(x)值 x为弧度cosh double cosh(x) 计算x的双曲余弦cosh(x)值 计算结果

double x;

double exp(x) x 计算结果

exp double x; 计算e 的值

double fabs(x) 计算结果

fabs double x; 计算双精度参数x的绝对值

double floor(x) 计算结果

floor 计算不大于x的最大整数

double x;

double fmod(x, y) 计算结果

fmod double x, y; 计算x/y的余数

(续表)函数名 函数类型和形参类型 功 能

double frexp(val, eptr)

frexp double val; 将双精度数val分成尾数和阶

int *eptr;

527log char strchr(const char 计算lnx的值

*s, int c);

double log10(x) 求log x大的值 计算结果

log10 10

double x;

double modf(val, iptr)

modf double val; 将双精度数val分解成尾数和阶

double *iptr;

double pow(x,y) y 计算结果 返回值 计算结果 计算结果计算结果 说 明

pow 计算x的值

double x, y;

double sin(x) 计算结果

sin double x; 计算x的正弦sin(x)值

double sinh(x) 计算x的双曲正弦sinh(x)值 计算结果

sinh double x;

double sqrt(x) 计算结果

sqrt double x; 对x进行开方

double tan(x) 计算x的正切tan(x)值 计算结果

tan double x;

double tanh(x) 计算结果

tanh double x; 计算x的双曲正切tanh(x)值

二、字符函数和字符串函数

ANSI C标准规定在使用字符串函数时要包含头文件“string.h”,在使用字符函数时要包含头文件“ctype.h”。如果所用的C编译系统不遵循ANSI C标准而用了其他名称的头文件,可以在使用时查阅有关的手册。

表10-2 字符函数和字符串函数

函数名 函数类型和形参类型 功 能 返回值 包含文件

int isalnum(ch) 是字母或数字返

isalnum 检查ch是否是字母或数字 回1;否则返回0 ctype.h

int ch;

int isalpha(ch) 是,返回1;不是

isalpha 检查ch是否是字母 ctype.h

int ch; 则返回0

int iscntrl 是,返回1;不是

iscntrl (ch) 检查ch是否控制字符 则返回0 ctype.h

int ch;

(续表)函数名 函数类型和形参类型 功 能 返回值 包含文件

int isdigit 是,返回1;不是

(ch) 检查ch是否数字 则返回0 ctype.hisdigit

int ch;

528 int isgraph 是,返回1;不是

检查ch是否可打印字符,不包括 则返回0

isgraph (ch) 空格 ctype.h

int ch;

int islower(ch) 检查ch是否小写字母 是,返回1;不是

islower int ch; 则返回0 ctype.hisprint int isprint(ch) 检查ch是否可打印字符,包括空 是,返回1;不是 ctype.h

int ch; 格 则返回0

int ispunct(ch) 检查ch是否标点字符,即除字母、 是,返回1;不是

ispunct int ch; 数字和空格以外的所有可打印字 则返回0 ctype.h

检查ch是否空格、跳格符或换行 是,返回1;不是 ctype.hisspace int isspace(ch)

int ch; 符 则返回0

int isupper(ch) 是,返回1;不是

isupper int ch; 检查ch是否大写字母 ctype.h

则返回0

int isxdigit(ch) 检查ch是否一个十六进制数学字 是,返回1;不是

isxdigit int ch; ctype.h

符 则返回0

char *strcat str1

strcat (str1,str2) 把字符串str2接到str1后面,str1 string.h

char * str1, 最后面的'\0'被取消

* str2;

char *strchr 返回指向该位置

(str,ch) 找出str指向的字符串中第一次出

strchr 的指针,如找不 string.h

char *str; 现字符ch的位置 到,则返回空指针

int ch;

int strcmp str1<str2,返回负

(str1,str2) 数

strcmp char * str1, 比较两个字符串str1、str2 str1=str2,返回0 string.h

* str2; str1>str2,返回正

char *strcpy

(str1,str2) 把str2指向的字符串复制到str1中

strcpy char * str1, 去 返回str1 string.h

* str2;

(续表)函数名 函数类型和形参类型 功 能 返回值 包含文件

unsigned int

strlen strlen(str) 统计字符串str中字符的个数 返回字符个数 string.h

char *str;

int tolower(ch) 返回ch所代表的

tolower int ch; 将ch字符转换为小写字母 ctype.h

字符的小写字母

int toupper(ch) 与ch相应的大写

toupper int ch; 将ch字符转换为大写字母 ctype.h

字母

三、输入输出函数

在使用输入输出函数时要求#include"stdio.h"将头文件stdio.h包含到源程序中。

表10-8 输入输出函数

函数名 函数类型和形参类型 功 能 返回值 说 明clearerr void clearerr(fp) 清楚文件指针错误 无

529 FILE *fp; 指示器

关闭成功返回0, 非 ANSI

int close(fp) 关闭文件

close 不成功返回-1

int fp; 标准

int creat(filename,mode) 以mode所指定的方式建立文 成功则返回整数,

*filename;

int mode; 件 否则返回-1 标准 非 ANSIcreat char

int eof(fp) 检查文件是否结束 遇文件结束,返回 非 ANSI

eof int fd; 1;否则返回0 标准

int fclose(fp) 关闭fp所指向的文件,释放 有错则返回非0,

fclose 否则返回0

FILE *fp; 文件缓冲区

int feof(fp) 遇文件结束符返回

feof 检查文件是否结束

FILE *fp; 非0值,否则返回0

int fgetc(fp) 从fp所指向的文件中取得下 返回所得到的字

fgetc FILE *fp; 一个字符 符。若读入出错,

返回EOF

char *fgets 返回地址buf,若遇

(buf,n,fp) 从fp指向的文件读取一个长 文件结束或出错,

fgets char *buf; 度为(n?1)的字符串,存入 返回NULL

int n; 起始地址为buf的空间

FILE *fp;

FILE *fopen 成功则返回一个文

fopen (filename,mode) 以mode指定的方式打开名为 件指针,即文件信

char *filename, filename的文件 息区的起始地址,

*mode; 否则返回0

(续表)函数名 函数类型和形参类型 功 能 返回值 说 明

int fprintf 将args的值以format指定的

(fp,format,args,...) 返回实际输出的字

fprintf 格式输出到fp所指向的文件

FILE *fp; 中 符数

char format;

int fputc 成功则返回该字符

(ch,fp) 将字符ch输出到fp指向的文 ch,否则返回EOF

fputc 件中

char ch;

FILE *fp;

int fputs 成功则返回0,如

(str,fp) 将str指向的字符串输出到fp 果出错则返回非0

fputs char *str; 所指向的文件中

FILE *fp;

fread int fread 从fp所指定的文件中读取长 返回所读的数据项

530 (pt,size,n,fp) 度为size的n个数据项,保存 的个数,如果遇到

char *pt; 到pt所指向的内存区 文件结束或者出错

unsigned size; 则返回0

unsigned n;

FILE *fp;

int fscanf 从fp指定的文件中按format

fscanf (fp,format,args,...) 给定的格式将输入数据送到 返回已经输入的数

FILE *fp; args所指向的内存单元中。其 据个数

char format; 中args是指针

int fseek 将fp所指向的文件的位置指

(fp,offset,base) 针移到以base所指出的位置 返回当前的位置,

fseek FILE *fp;

long offset; 为基准、以offset为位移量的 否则返回-1

int base; 位置

ftell long ftell(fp) 返回fp所指向的文件中的读 返回fp所指向的文

FILE *fp; 写位置 件中的读写位置

int fwrite

(ptr,size,n,fp)

fwrite char *ptr; 把ptr所指向的n*size个字节 写到fp文件中的数

unsigned size; 输出到fp所指向的文件中 据项的个数

unsigned n;

FILE *fp;

(续表)函数名 函数类型和形参类型 功 能 返回值 说 明

int fgetc(fp) 从fp所指向的文件中取得下 返回所得到的字

getc FILE *fp; 符。若读入出错,

一个字符 返回EOF

从标准输入设备读取下一个 所读字符。若文件

getchar int getchar() 结束或出错,则返

字符 回-1

int getw(fp) 从fp所指向的文件读取下一 输入的整数。若文 非 ANSIgetw FILE *fp; 件

结束或出错,返

个字 回-1 标准

int open 返回文件号(正

以mode指定的方式打开已存 数)。若打开失败, 非 ANSIopen (filename,mode)

在的名为filename的文件 返回-1 标准

char *filename,

*mode;

int printf 将输出表列args的值输出到 输出字符的个数。

printf (format,args,...)

标准输出设备 若出错,返回负数

char *format;

531 int putc

(ch,fp) 将字符ch输出到fp指向的文 成功则返回该字符

putc 件中 ch,否则返回EOF

int ch;

FILE *fp;

int putchar(ch) 把字符ch输出到标准输出设 输出的字符ch。若

putchar 出错,返回EOF

char ch; 备

int puts(str) 把str指向的字符串输出到标 返回换行符。若失

puts 准输出设备,将'\0'转换为回车 败,返回EOF

char *str; 换行

int putw(w,fp) 返回输出的整数。

putw 将一个正数w写到fp指向的 若出错,返回EOF。

int w; 文件中。

FILE *fp;

void rewind(fp) 将fp指示的文件中的位置指

rewind 针置于文件开头位置,并清除 无

FILE *fp; 文件结束标志和错误标志

int scanf 从标准输入设备按format指 读入并赋给args的

scanf 向的格式字符串规定的格式, 数据个数。遇文件

(format,args,...) 输入数据给args所指向的单 结束返回EOF,出

char *format; 元 错返回0

四、动态存储分配函数

ANSI C标准建议设4个有关的动态存储分配函数:calloc()、malloc()、free()、realloc()。并且建议在"stdlib.h"头文件中包含有关的信息。

ANSI C标准要求动态分配系统返回void指针。Void指针具有一般性,它可以指向任何类型的数据,但目前的绝大多数C编译所提供的这类函数都返回char指针。无论何种情况,都需要用强制类型转换方法把char指针转换成所需的类型。

表10-6 动态存储分配函数

函数名 函数类型和形参类型 功 能 返回值

分配n个数据项的内存连续空间,每 分配内存单元的calloc void *calloc(n,size) 起始地址。如不成

unsigned size; 个数据项的大小为size 功,返回0

void *malloc(size) 所分配的内存区malloc unsigned size; 分配size个字节的存储区 地址,如内存不

够,返回0

void free(ptr) 释放ptr所指的内存区 无free void *ptr;

void *realloc(p,size) 将p所指出的已分配的内存区的大小 返回指向该内存

void *p; 改为size。size可比原来的分配空间 区的指针realloc unsigned size; 大或小

532 附录7 常用字符与ASCII代码对照表

c 字符 代码 字符 代码 字符 代码 字符128 € 160 [空格] 192 ? 224 ?129 ? 161 ? 193 ? 225 ?130 ° 162 ? 194 ? 226 ?131 ? 163 ? 195 ? 227 à132 á 164 ¤ 196 ? 228 á133 ? 165 ¤ 197 ? 229 ?134 ? 166 ? 198 ? 230 ?135 ? 167 § 199 ? 231 ?136 ± 168 ¨ 200 ? 231 ?137 ? 169 ? 201 ? 232 ?138 ? 170 § 202 ? 233 °139 ? 171 ¨ 203 ? 234 ±140 ? 172 ? 204 ? 235 ?

(续表)

c 字符 代码 字符 代码 字符 代码 字符141 ? 173 205 ? 236 ?142 ? 174 (R) 206 ? 237 ?143 ? 175 ? 207 ? 238 ?144 ? 176 ? 208 ? 239 ∩145 ? 177 ? 209 ? 240 ≡146 ? 178 ? 210 ? 241 ?147 ? 179 ? 211 ? 242 ?148 ? 180 ? 212 ? 243 ?149 ? 181 ∞ 213 ? 244 ?150 ? 182 ? 214 ? 245 ?151 ù 183 ? 215 ? 246 ÷152 ? 184 ? 216 × 247 ≈153 ? 185 ? 217 ? 248 ?154 ? 186 ? 218 ? 249 ?155 ? 187 ? 219 ? 250 .156 ? 188 ? 220 ? 251 √157 ¤ 189 ? 221 ? 252 Π158 Pt 190 ? 222 ? 253 Ζ159 ? 191 ? 223 ? 254 ?

附录8 练习和习题答案

第2章答案

【选择题】

1.C 2.C 3.A 4.D 5.B

6.A 7.B 8.C 9.D 10.B

533 11.D 12.D 13.C 14.D 15.C

16.C 17.C 18.D 19.B 20.A

21.A 22.B 23.D 24.D

【填空题】

1.处理器(中央处理器)存储器 输入设备 输出设备

2.系统软件和应用软件

3.VGA

4.中央处理器 内存储器

5.只读存储器 随机存储器

6.软盘 软盘驱动器 控制器适配卡

7.1.44MB

8.1024

9.格式化

10.高

11.键盘 鼠标

12.激光打印机

13.开机、关机

14.4月26日

15.引导区

16.电子布告栏系统

17.局域网、广域网 环行网、星形网、总线型 带网、宽带网

第3章答案 双绞线网、同轴电缆网、光纤网、卫星网基

【选择题】

1.D 2.C

【填空题】

1. .EXE 2. .C .OBJ .EXE 3.顺序结构、选择结构、循环结构

第4章答案

1.短整型 无符号型 2.十进制数 指数

3.算术 逻辑 条件 指针 求字节数

4.C 5.65,89 6.C

534第5章答案

【选择题】

1.C 2.B 3.A 4.D 5.C

6.C 7.C 8.C 9.A 10.C

11.A 12.D 13.B 14.C 15.C

16.A 17.B 18.D 19.A

【填空题】

1.(1)4 (2)5

2.5.0,4,c=3

3.1234□5

4.65

第6章答案

【选择题】

1.C 2.C 3.C 4.B 5.A

6.B 7.A 8.C 9.A 10.A

【填空题】

1.if(a<=B){x=1;printf("####x=%d\n",x);}

else {y=2;printf("****y=%d\n",y);}

2.2

3.20

4.a>0&&a<b

第7章答案

【选择题】

1.B 2.A 3.B 4.D 5.A

6.A 7.C 8.B 9.C 10.B

11.D 12.C 13.B 14.B 15.C

16.B 17.D 18.B 19.B 20.C

【填空题】

1.s=3

2.(1) 1 (2) y=1,x=0

3.3,1,?1,3,1,?1

535 4.(1) x>=0.0或x>=0或!(x<0)或!(x<0.0)

(2) x<amin或x<=amin

第8章答案

【选择题】

1.C 2.C 3.D 4.A 5.A

6.A 7.C 8.B 9.D 10.A

11.A 12.D 13.A 14.C 15.A

16.B 17.C 18.C

【填空题】

1.?850,2,0

2.QuickC

3.9,0

4.12

5.18

第9章答案

【选择题】

1.A 2.B 3.B 4.D 5.D

6.B 7.A 8.C 9.A 10.A

11.B 12.D 13.B 14.B 15.B

【填空题】

1.前者是调用一个函数的过程,而后者是直接或间接的调用函数自己。

2. x=8,y=5

x=8,y=6

3. i=7,j=6,x=7

i=2,j=7,x=5

4.(1)a[i-1] (2)a[9-i]

5. void fun(int s[])

void fun(int s[M])

void fun(int *s) 三者可以任意次序

第10章答案

1.宏定义 文件包含

536 2.6 6

3.9 13

第11章答案

1.指针变量,指针类型

2.起始地址,数组元素的地址,指向数组的指针变量

3.指针类型,字符串的首地址,字符数组的首地址

4.入口地址

5.指命令行中参数的个数,指针数组

6.下标法,指针法

7.字符数组,字符指针

8.*a,*b,*c,*min a,b,c *a,*b,*c *min=*b *b *min=*c

9.HOW how do you

10.ip=name+i

11.10

第12章答案

1.构造 分量 表

2.共用体 枚举

3.struct st 或ex

4.struct list * q

5.9

第13章答案

1.二目 整型 符型

2.结构体 位数

第14章答案

1.ASCII文件 二进制文件 普通文件 备文件 缓冲文件系统

2.fprintf函数 fscanf函数 磁盘文件 ewind函数 fseek函数

3.3 !feof(f1)或feof(f1)==0

模拟试卷一答案

537 一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

1.C 2.C 3.C 4.B 5.B

6.D 7.A 8.C 9.C 10.D

11.C 12.B 13.C 14.A 15.D

16.B 17.D 18.C 19.D 20.B

21.B 22.C 23.C 24.D 25.A

26.B 27.C 28.A 29.A 30.D

31.C 32.D 33.D 34.A 35.A

36.D 37.A 38.A 39.C 40.B

41.D 42.C 43.C 44.B 45.B

46.B 47.B 48.C 49.D 50.A

二、填空题(每空2分,共40分)

1.COPY *.TXT>PRN

2.CD A:

3.ABC>XYZ.DAT 或 ABC>>XYZ.DAT

4.广域网

5.120160

6.?32768~32767

7.表示跳过它相应的数据

8.10,30,40

9.Q

10.60

11.600

12.max=q;

13.Px,py

14.2

15.'\0'

16.s

17.*p++

18.lett.dat

19.4

20.6*sizeof(char)

模拟试卷二答案

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

538 1.B 2.C 3.D 4.D 5.B

6.D 7.A 8.B 9.D 10.A

11.A 12.D 13.D 14.A 15.A

16.C 17.C 18.D 19.C 20.D

21.B 22.C 23.D 24.C 25.C

26.A 27.D 28.D 29.A 30.D

31.B 32.A 33.C 34.C 35.D

36.C 37.C 38.C 39.B 40.C

41.C 42.C 43.B 44.A 45.D

46.C 47.B 48.B 49.D 50.A

二、填空题(每空2分,共40分)

1.AUTOEXEC.BAT

2.外部

3.COPY A:*.DAT>PRN

4.RENXYZ\*.BAK *.FOR 或RENAME XYZ\*.BAK*.FOR

5.ATTRIB+R WST.TXT

6.1.0或1

7.10

8.60

9.8,17

10.void fun(int *q); 或 void fun(q) int *q;

11.void fun(int q[]); void fun(q) int q[];

12.void fun(int q[M]); void fun(q) int q[M];

13.6354

14.p++ 或 ++p 或 p=p+1 或p+=1或p=1+p

15.w[i?1]或*(w+i?1)

16.pc,pb 或pb,pc

17.pc,pa或pa,pc

18.pb,pa或pa,pb

19.2

20.2

模拟试卷三答案

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

1.A 2.C 3.D 4.C 5.D

6.D 7.C 8.D 9.D 10.A

539 11.A 12.D 13.D 14.A 15.A

16.C 17.C 18.A 19.B 20.D

21.B 22.C 23.D 24.C 25.A

26.B 27.D 28.D 29.A 30.D

31.A 32.D 33.D 34.C 35.A

36.C 37.C 38.C 39.D 40.D

41.C 42.C 43.B 44.C 45.C

46.C 47.B 48.D 49.D 50.A

二、填空题(每空2分,共40分)

1.顺序

2.COPY *.TXT>PRN 或XCOPY *.TXT>PRN

3.COPY A:*.DAT>PRN

4.MD A:\USER\X MD A:X

5.退出(或关闭)

6.80

7.fp=fopen("f.dat","rb");

8.60

9.177777

10.x==0||x==1

11.?1

12.*sn

13.6354

14.p++或++p或p=p+1或p+=1或p=1+p

15.32768

16.y>>=1

17.y>0

18.Fname,w

19.exit(0)

20.Ch,fp

20xx年9月全国计算机等级考试二级(C语言)笔试真题答案

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

(1) C (2)A (3)B (4)B (5) C

(6) A (7) D (8) D (9) D (10) D

(11) A (12) D (13) B (14) A (15) C

540 (16) D (17) C (18) C (19) B (20) C

(21) D (22) C (23) A (24) A (25) D

(26) A (27) B (28) D (29) B (30) D

(31) C (32) B (33) D (34) A (35) B

(36) D (37) C (38) A (39) D (40) D

(41) C (42) A (43) B (44) B (45) C

(46) D (47) B (48) D (49) A (50) C

二、填空题(每空2分,共40分)

1.【1】 系统软件

2.【2】 CON 或 CON:

3.【3】 TCP/IP 或者 传输控制协议/网际协议

4.【4】 EXIT 或者 exit

5.【5】 二百万 或者 两百万 或者2,000,000或者2百万 或者 200万 或者 2000000

6.【6】 k,【7】 *k

7.【8】20,0

8.【9】17

9.【10】X

10.【11】3,2,2,3

11.【12】 /i 或者 *1.0/i 或者 *1/i 或者 *(1.0/ i) 或者 /(double)i

12.【13】max<a[row][col],【14】min>max

13.【15】*t,【16】*s-*t

14.【17】1000 10

15.【18】3 5

16.【19】80

17.【20】"d1.dat","rb"

20xx年4月全国计算机等级考试二级(C语言)笔试真题答案

一、选择题(1~40题每个选项1分,41~50题每个选项2分,共60分)

1.C 2.C 3.C 4.C 5.C

6.B 7.A 8.D 9.D 10.C

11.C 12.C 13.A 14.B 15.C

16.D 17.B 18.D 19.A 20.C

21.C 22.B 23.B 24.D 25.A

26.C 27.C 28.D 29.C 30.C

31.A 32.C 33.B 34.C 35.B

36.B 37.B 38.C 39.B 40.C

541 41.A 42.D 43.A 44.D 45.A

46.B 47.C 48.C 49.B 50.B

二、填空题(每空2分,共40分)

1.字节

2.AUTOEXEC.BAT

3.COPY *.TXT PRN

4.拖动

5.图标或按钮

6.2.1

7.6.6

8.Malloc(11) 或malloc(sizeof(char)*11)

9.Hello

10.passwarm

11.Void(*p)() 或void(*p)(int *, int *)

12.0

13.10

14.Struct st 或 ex

15.”bi.dat”,”w” 或 “bi.dat”,”wt” 或 ”bi.dat”,”w+t”

16.5

17.4

18.P=j

19.X[i][p]

20.LineMax(x)

第16章答案

【填空题】

1.type *.txt

2.md a:\user\data

3.copy b:\bas\??a* c:\abc

4.autoexec

5.copy jd1.for + jd2.for a:\jd3.for

【选择题】

1.D 2.B

综合题答案略

542 参 考 文 献

1. 谭浩强.C程序设计.北京:清华大学出版社,1999

2. 杜开珍,黄边明.C语言教程.北京:科学出版社,1995

3. 李智渊.C语言.成都:电子科技大学出版社,1994

4. 梁光春,罗中函.C程序设计等级考试过关指导.成都:电子科技大学出版社,19985. 谭浩强,田淑清.PASCAL语言程序设计.北京:高等教育出版社,19886. Herbert Schildt.C:The Complete Reference.McGraw?Hill

C语言大全.北京:电子工业出版社,1990

1 考网读者卡

感谢您购买考网和清华出版社联合出版的全国计算机等级考试辅导丛书,您可以将本卡寄回考网,要求开通指定的账户,获得相应积分,即时享受“在线答疑”等考网的增值服务,常见问题列举如下,具体事宜请查阅考网会员专区中的有关说明。

1,什么是考网读者卡?

考网读者卡是考网网友及其读者的双重身份证明,考网的网友指在注册成为会员(级别不限)的自然人,考网读者是指成功从考网考试书店购买书籍或购买考网版书籍的自然人。

2,考网读者卡有什么意义?

具有“网友”和“读者”两种身份的人将获得考网一些增值服务,考网读者卡的功能就是验证这种双重身份。特别提醒:考网读者卡只对具有“网友”和“读者”双重身份的用户有用,如果用户不具备经常上网的条件,将无法通过网络获得考网提供的增值服务,也就没有必要邮寄考网读者卡。

3,为什么要寄回考网读者卡?

因为考网读者卡是识别“网友”和“读者”两种身份的凭证,如果不将读者卡寄回考网,我们就无法识别用户身份,用户也就无法享受其可以享受的增值服务。

4,与考网读者卡有关的增值服务到底有哪些呢?

索取最新等级考试试题及答案

优惠注册考网付费会员,获得更多增值服务(如笔试练习和模拟试题的分析)

参与考前在线综合模拟测试(1次)

5,如何填写读者卡,我的个人信息安全吗?

考网读者卡中的所有内容均须详细、真实的填写,不潦草,能辨认,如果部分项目无法填写,如没有手机号,可以填写“无”,但是不能空着。考网只确认完全填写的读者卡,未完全填写的读者卡作废。

未得到用户授权以前,考网承诺为用户保密,所有个人信息决不复制、不传播、不公开,仅用于身份确认。

6,如何确保邮寄成功?

如果书籍通过考网购买,附在书后的考网读者卡将被打上“注销”的字样,用户可以登录考网填写读者卡有关信息(需提供网上订购订单号)。

如果书籍通过其他途径购买,用户正确填写附在书后的考网读者卡后,沿裁剪线剪下该页,并寄回考网,通信地址:北京市东城区安定路20号安贞写字楼A座226室(100029),收件人填写“考网读者卡”。注意:考网用户名和密码是在用户注册成为考网会员的时候获得的,即邮寄读者卡前必须在考网注册成为会员(级别不限)。

收到用户返回的“考网读者卡”后,考网将进行email和考网用户名的对应确认,经过确认的信息才是有效的。考网将在网站上公布有效读者卡的考网用户名。

2 考网用户名:__________________ 密码:___________________

真实姓名 性别 年龄

职务 月收入 行业

电话 手机 Email

证件名称 证件号码

通信地址(邮编)

报名点地址 电话

所购书籍名称

购买理由

本书缺陷及改进

意见

您在哪些方面使 □软件编程 □网络编程 □数据库编程 □三维动画 □flash 动画计算机发挥作用 □广告美工设计 □网络美工设计 □出版业应用 □办公应用(可多选) □音乐 □游戏 □上网冲浪 □学习 □科研 □其他____________附赠软件质量 □优 □良 □中 □差

软件改进建议

对考网满意吗 □满意 □比较满意 □一般 □较差

希望考网在什么

地方改进

愿意成为考网高 □不想 □很想成为会员 □想,但不知道如何成为会员

级会员吗? □想,但不知道成为会员后有什么权益 □Sorry,不知道有考网会员为什么选择(或

不选择)考网高

级会员服务

希望考网提供的 □在线答疑 □上机辅导 □笔试辅导 □提供模拟试题等级考试方面的 □在线培训 □培训班 □考试指导 □代办报名服务有(可多选) □代办查分 □其他________________

如果考网提供在线培 □愿意,只要价格合适――>能接受的价格是______。

训,您愿意接受吗 □不愿意——>为什么?_________________________。如果考网开办线下培 □愿意,只要价格合适――>能接受的价格是______。

训班,您愿意参加吗 □不愿意——>为什么?_________________________。除目前的业务外,还希望考

网提供哪些方面的服务

注意:考网读者卡复印无效,严禁伪造,有效期至20xx年6月。

3

更多相关推荐:
计算机二级正确的复习方法

正确的复习方法其实计算机二级并不难大多为记忆性的知识需要灵活运用的很少所以备考二级不宜太早以考前一个半月开始准备为宜所谓万变不离其宗因此教材应至少通读两遍把该记住如变量函数等的都记牢并不断复习巩固参考资料则不宜...

全国计算机等级考试二级C语言考试复习资料及复习计划以及考试要点

全国计算机等级考试二级C语言考试复习资料及复习计划一C语言的特点C语言是近年来非常流行的语言很多人宁愿放弃已经熟悉的其他语言而改用C语言概括起来C语言程序具有如下的风格C语言程序的函数具体模块结构风格使得程序整...

全国计算机等级二级visual fox pro数据库程序设计复习计划

全国计算机等级二级visualfoxpro数据库程序设计复习计划

计算机考研院校选择及复习计划

第一章计算机科学与技术考研现状及发展11细分专业介绍111计算机软件与理论112计算机系统结构113计算机应用技术12计算机科学与技术专业著名导师介绍121清华大学122北京大学123上海交通大学124浙江大学...

二级C语言考前复习计划

二级C语言考前复习计划发表于20xx年9月7日22时51分54秒来源阅读418评论3举报本文链接httpuser80118114blog1220xx9114二级C语言考前复习计划从九月七日开始到九月十九日天南大...

五年级数学上册复习计划

五年级数学上册复习计划复习目标1使学生进一步掌握小数乘除法的计算方法能正确口算笔算相应的小数乘除法式题会按运算顺序正确计算小数四则混合运算能运用运算定律和性质进行小数的简便运算能应用学过的小数四则计算解决一些简...

学计算机二级VFP孟帅旗视频课,99分你也可以做到

学计算机二级VFP孟帅旗视频课99分你也可以做到很高兴能够通过梦想网校这个平台和大家一起分享我的学习心得在这次考试中我取得了99分的高分我觉得这其中有一部分是运气还有一部分是自己的努力而在这一部分的努力中不得不...

计算机科学与技术的考研经验

不管写得好不好对不对请众20xx的考生担待点能够从理性的角度看待我的帖子我也不是为了增人气说实话考上之后这些论坛对我们来讲没什么用了也很少人来逛了完全出于对论坛感谢也绝无炫耀等之意先讲讲自己吧本人山东人在一所山...

408计算机考研参考书

一数据结构1教材数据结构严蔚敏清华大学出版社清华大学严蔚敏的这本数据结构的教材是国内数据结构教材的权威也是国内使用最广其广度远远超越其他同类教材计算机考研专业课命题必定以它为蓝本这一本数据结构是20xx年的最新...

考研计算机专业知名院校推荐

凯程考研集训营为学生引路为学员服务考研计算机专业知名院校推荐一清华大学清华大学计算机科学与技术系成立于19xx年曾取得过许多影响深远的成果为我国计算机事业的发展作出了重大贡献现已发展成为我国计算机学科领域内影响...

一篇关于计算机考研的文章供大家参考

找到一篇关于计算机考研的文章供大家参考过来人分享计算机专业考研成功经验谈刚开始准备考研的时候我整整两个月就一直看专业课因为大学的时候对计算机感兴趣所以我对专业课还是比较熟悉的后来到了5月我就开始看数学了一直到考...

383分南京大学计算机的考研经验

当看到南大CS主页上拟录取名单中出现我的名字时虽然意料之中但是还是有种不错的感觉却不是之前复习时YY的狂喜或怎样的同学们的祝贺很多我反而比他们淡定了只觉得是一种release人生迈向了下一崭新的页复试完了见完导...

计算机二级复习计划(8篇)