2.4 任务四:“小学生数学选题系统”的编码实现
2.4.1 子任务一:界面设计
任务描述
设计小学生数学选题系统界面,根据用户要求确定界面的风格,分组绘制界面草图,编码实现各模块界面的设计。
具体要求如下:
(1)界面清晰明了、各界面之间上下衔接自然。
(2)根据系统概要设计的功能模块图确定各界面之间的关系。
任务分析与设计
分析小学生数学选题系统共需要几个独立界面。
(1)分别定义各界面的函数。
(2)具体设计各界面的风格。
(3)做好各界面的衔接。
(4)运行调试。
任务实现
(1)根据系统概要设计,初步分析小学数学选题系统共计5个界面:登录界面、主控界面、四则题库界面、题量设置界面和使用说明界面。
(2)根据函数关系图确定各函数名称分别为login() 、showMainMenu()、selectProblemMenu()、systemSetting()、helpMenu()。
(3)各小组画出界面风格草图。
(4)编码实现。
1.登录界面设计
#include<stdio.h> /*引用预处理命令中的stdio.h头文件*/ //登录界面代码 main() /*主函数*/ { login(); /*调用login()函数*/ } void login() { printf("\n\n"); printf("\t\t****************************************************\n"); printf("\t\t 选题系统登录 \n"); printf("\t\t****************************************************\n"); printf("\n\n"); printf("\t\t\t\t请输入用户名:" ); printf("\n\n"); printf("\t\t\t\t请输入密码:"); }
运行结果如图2.8所示。
图2.8 登录界面
2.主控界面设计
//主控界面代码 void showMainMenu() { int num; printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 小学生数学选题系统 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); printf("\t\t\t\t 1. 题 量 设 置\n\n"); printf("\t\t\t\t 2. 四 则 题 库\n\n"); printf("\t\t\t\t 3. 评 分 系 统 \n\n"); printf("\t\t\t\t 4. 使 用 说 明\n\n"); printf("\t\t\t\t 0. 退出\n\n\n\n"); do{ printf("\t\t请 选 择 操 作(0-4):"); scanf("%d",&num); }while(num>4||num<0);//只能选择0-4,否则一直要求重新操作 switch(num) { case 1:systemSetting();break; //题量设置 case 2:selectProblemMenu();break; //四则题库选择 case 3:gradeSystem()break;; //评分系统 case 4:helpInfo();break; //使用说明 case 0:exit(0);break; default: printf("\n\t\请按任意键重新选择!!!"); getchar(); system("cls"); showMainMenu(); } }
运行结果如图2.9所示。
图2.9 主控模块界面
3.四则题库界面设计
// 四则题库界面代码 void selectProblemMenu() { int num; system("cls"); printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 四则题库 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); printf("\t\t\t\t 1. 加 法 题 库\n\n"); printf("\t\t\t\t 2. 减 法 题 库\n\n"); printf("\t\t\t\t 3. 乘 法 题 库\n\n"); printf("\t\t\t\t 4. 除 法 题 库\n\n"); printf("\t\t\t\t 0. 返回主控界面\n\n"); printf("\n\t\t请 选 择 操 作:"); scanf("%d",&num); }
运行结果如图2.10所示。
图2.10 四则题库界面
4.题量设置界面设计
//题量设置界面代码 void systemSetting() { system("cls"); printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 题量设置 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); printf("\t\t当前默认题量为:5"); printf("%d\n\n",problemNumber); printf("\t\t请输入你选择的题量(5的倍数):"); scanf("%d",&problemNumber); system("cls"); showMainMenu(); }
运行结果如图2.11所示。
图2.11 题量设置界面
5.使用说明界面设计
//使用说明界面代码 void helpInfo() { getchar(); system("cls"); printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 使用说明 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); printf("\t1. 本系统共分为使用说明、四则题库、评分系统三个模块。\n\n"); printf("\t2. 四则题库分为加、减、乘、除运算,每种运算各有100道题供练习者使用,\n\n"); printf("\t 在练习过程中,每练习5道题后可选择是否退出训练。\n\n"); printf("\t3. 评分系统的主要功能是显示每次训练的题数及正误数。\n\n"); printf("\t\t按任意键返回主菜单"); getchar(); system("cls"); showMainMenu(); }
运行结果如图2.12所示。
图2.12 使用说明界面
引导文献
1.人机界面设计
目前,友好的“人机界面”(Human Computer Interface,HCI)已成为基于计算机的系统的一项重要的技术指标。因此,开发人机界面的人机工程已经成为开发基于计算机的系统的一个重要组成部分。
要设计出高质量的人机界面,不仅涉及计算机技术,还涉及美学、心理学等人文科学知识。
2.界面设计的重要性
每个新的系统对用户来说都是一次新的学习过程,如果界面风格经常变化,不保持统一,无疑会增加用户的学习难度,也许会导致用户的厌烦。要想实际解决这个问题,主要依靠界面设计这一关键环节,一个友好、易用的界面是令用户满意的界面。因此,我们在设计界面时不但要考虑功能和美观,还要考虑用户的可操作性,并非每个用户都非常熟悉计算机,我们应以令没有计算机基础的客户满意为标准,遵循客户至上的原则。
3.人机界面开发过程的主要5个步骤
(1)活动分析。即分析人机交互的所有过程,标志该过程中人的活动并据此确定需要计算机执行的任务。
(2)动作定义和设计。
(3)动作的实现。用特定的人机交互语言的语句和命令去实现每一个动作,进而实现各个人机界面的交互活动。如有必要,可以设计人机交互语言,精确地定义语言的语法和语义,并实现语言中的每一个动作和命令。
(4)用户环境的设计。要设计高质量的人机界面,必须考虑将支撑人机界面的软件和硬件集成后,构成集成的用户环境的整体设计效果,还应考虑空间、光线、温度等环境因素。
(5)原型设计。软件工程师根据以上4个步骤的结果设计原型,并请用户对原型进行评价和审查。根据用户提出的意见修改原型,这是一个迭代过程,直至通过用户的评审。软件工程师以通过评审的原型为基础设计人机界面,就可以设计出令用户满意的高质量的人机界面。
4.初识C语言
本教材的代码实现是在VC++环境下实现TC的功能,主要好处是,让学生在比较友好的界面下,完成C语言的学习。
1)C语言程序简介
为了认识VC++环境,说明C语言源程序结构的特点,下面先介绍几个程序。这几个程序由易到难,充分体现了C语言源程序在组成结构上的特点。虽然还未介绍有关内容,但可以从这些例子中了解组成一个C语言源程序的基本部分和书写格式。
【例2.1】
(1)选择“开始”→“程序”→“Microsoft Visual C++ 6.0/ Microsoft Visual C++6.0”。
(2)选择“文件”→“新建”命令,打开如图2.13所示的“新建”对话框,选择“文件”选项卡中的“Win32 Console Application”选项,输入工程名称及所在位置。
图2.13 “新建”对话框
(3)单击“确定”按钮,弹出如图2.14所示的对话框。
图2.14 创建控制台程序
(4)选择“一个空工程”选项后,单击“完成”按钮,弹出如图2.15所示的对话框。
图2.15 新建工程骨架
(5)单击“确定”按钮后,弹出如图2.16所示对话框。在“文件”选项卡中,选择“C++Source File”,在右侧的“文件名”框中输入“hello.c”。
图2.16 新建hello.c文件
(6)单击“确定”按钮后,弹出如图2.17所示的窗口。
图2.17 编码实现
(7)按图所示输入【例2.2】所示的程序后,选择“组建”→“编译”命令,运行结果如图2.18所示。
【例2.2】
#include<stdio.h> main() { printf("世界,您好\n"); }
图2.18 运行结果
说明:
(1)include称为文件包含命令。
(2)扩展名为.h的文件称为头文件。
(3)main是主函数的函数名,表示这是一个主函数。
(4)每一个C语言源程序都必须有且只能有一个主函数(main函数)。
(5)printf函数是一个由系统定义的标准函数,可在程序中直接调用。
(6)printf函数的功能是,把要输出的内容送到显示器显示。
程序的功能是在显示器上输出“世界,您好”。在main()之前的语句称为预处理命令。预处理命令还有其他几种,这里的include称为文件包含命令,其意义是把尖括号<>或引号""内指定的文件包含到本程序来,成为本程序的一部分。被包含的文件通常是由系统提供的,其扩展名为.h,因此也称为头文件或首部文件。C语言的头文件中包括了各个标准库函数的函数原型。因此,凡是在程序中调用一个库函数时,都必须包含该函数原型所在的头文件。在本例中,使用了一个库函数:输出函数printf,因此在程序的主函数前用include命令包含了stdio.h文件。
需要说明的是,C语言规定对标准输入函数scanf和标准输出函数printf可以省去对其头文件的包含命令。因此,在本例中也可以删去包含命令#include<stdio.h>。
【例2.2】的主函数体分为两部分:一部分为说明部分,另一部分为执行部分。
说明是指变量的类型说明。在【例2.2】中,未使用任何变量,因此无说明部分。C语言规定,源程序中所有用到的变量都必须先说明,后使用,否则将会出错。这是编译型高级程序设计语言的一个特点,与解释型的BASIC语言不同。说明部分是C语言源程序结构中很重要的组成部分。
说明部分后的程序行为执行语句部分,用以完成程序的功能。在【例2.2】中,执行部分是一条输出语句,调用printf函数在显示器上输出字符串。
2)输入和输出函数
在【例2.2】中,用到了输出函数printf,在以后的程序设计过程中还将经常用到输入函数scanf,这里先简单介绍它们的格式,以便下面使用。
scanf和printf这两个函数分别称为格式输入函数和格式输出函数,其意义是按指定的格式输入/输出值。因此,这两个函数在括号中的参数表都由以下两部分组成:
“格式控制串”,参数表。
格式控制串是一个字符串,必须用双引号括起来,它表示了输入/输出量的数据类型。各种类型的格式表示法可参阅附录E。在printf函数中,还可以在格式控制串内出现非格式控制字符,这时在显示屏幕上将原样输出。参数表中给出了输入或输出的量。当有多个量时,用逗号间隔。例如:
printf("a=,b=",a,b); scanf("%d%d",&x,&y);
【例2.3】
int max(int a,int b); /*函数说明*/ main() /*主函数*/ { int x,y,z; /*变量说明*/ int max(int a,int b); /*函数说明*/ printf("请输入两个数:\n"); scanf("%d%d",&x,&y); /*输入x,y值*/ z=max(x,y); /*调用max函数*/ printf("大数是:=%d",z); /*输出*/ } int max(int a,int b) /*定义max函数*/ { if(a>b)return a; else return b; /*把结果返回主调函数*/ }
在【例2.3】中,程序的功能是由用户输入两个整数,程序执行后输出其中的大数。本程序由两个函数(主函数和max函数)组成。函数之间是并列关系。可从主函数中调用其他函数。max函数的功能是比较两个数,然后把大数返回给主函数。max函数是一个用户自定义函数。因此,在主函数中要给出说明。可见,在程序的说明部分中,不仅可以有变量说明,还可以有函数说明。在程序的每行后用/*和*/括起来的内容为注释部分,程序不执行注释部分。
在【例2.3】中,程序的执行过程是,首先在屏幕上显示提示串,请用户输入两个数,回车后由scanf函数语句接收这两个数送入变量x和y中,然后调用max函数,并把x和y的值传送给max函数的参数a和b。在max函数中,比较a和b的大小,把大者返回给主函数的变量z,最后在屏幕上输出z的值。
3)清屏
格式:system("cls")
该函数包含在头文件 #include <stdlib.h>中,作用是清除屏幕上的显示内容。
4)C语言源程序的结构特点
(1)一个C语言源程序可以由一个或多个源文件组成。
(2)每个源文件可由一个或多个函数组成。
(3)一个源程序不论由多少个文件组成,都有一个且只能有一个main函数,即主函数。
(4)源程序中可以有预处理命令(include命令仅为其中的一种),预处理命令通常放在源文件或源程序的最前面。
(5)每一个说明、每一个语句都必须以分号结尾,但预处理命令、函数头和花括号“}”之后不能加分号。
(6)标志符、关键字之间必须至少加一个空格以示间隔。若已有明显的间隔符,也可不再加空格来间隔。
5)书写程序时应遵循的规则
从书写清晰,便于阅读、理解、维护的角度出发,在书写程序时应遵循以下规则。
(1)一个说明或一个语句占一行。
(2)用{} 括起来的部分,通常表示了程序的某一层次结构。{}一般与该结构语句的第一个字母对齐,并单独占一行。
(3)低一层次的语句或说明可比高一层次的语句或说明缩进若干格后书写,以便看起来更加清晰,增加程序的可读性。
在编程时应力求遵循这些规则,以养成良好的编程风格。
6)C语言字符集
字符是组成语言的最基本的元素。C语言字符集由字母、数字、空白符、标点和特殊字符组成。在字符常量、字符串常量和注释中,还可以使用汉字或其他可表示的图形符号。
(1)字母:
① 小写字母a~z共26个。
② 大写字母A~Z共26个。
(2)数字:0~9共10个。
(3)空白符。空格符、制表符、换行符等统称为空白符。空白符只在字符常量和字符串常量中起作用。在其他地方出现时,只起间隔作用,编译程序对它们忽略不计。因此,在程序中使用空白符与否,对程序的编译不发生影响,但在程序中适当的地方使用空白符将增加程序的清晰性和可读性。
(4)标点和特殊字符。
7)C语言词汇
在C语言中使用的词汇分为6类:标志符、关键字、运算符、分隔符、常量、注释符。
(1)标志符。在程序中使用的变量名、函数名、标号等统称为标志符。除库函数的函数名由系统定义外,其余都由用户自定义。C语言规定,标志符只能是字母(A~Z,a~z)、数字(0~9)、下画线(_)组成的字符串,并且其第一个字符必须是字母或下画线。
以下标志符是合法的: z、y、c6、Name_1、total5等。
以下标志符是非法的。
① 4d:以数字开头。
② x*y:出现非法字符*。
③ -8x:以减号开头。
在使用标志符时,还必须注意以下事项。
① C语言不限制标志符的长度,但它受各种版本的C语言编译系统的限制,同时也受具体机器的限制。例如,在某版本的C语言中规定标志符的前8位有效,当两个标志符的前8位相同时,则被认为是同一个标志符。
② 在标志符中,大小写是有区别的。例如,name和NAME是两个不同的标志符。
③ 标志符虽然可由程序员随意定义,但标志符是用于标志某个量的符号。因此,命名应尽量有相应的意义,以便于阅读理解,实现“见名知义”。
(2)关键字。关键字是由C语言规定的具有特定意义的字符串,通常也称为保留字。用户定义的标志符不应与关键字相同。C语言的关键字分为以下3类。
① 类型说明符:用于定义、说明变量、函数或其他数据结构的类型,如【例2.3】中用到的int。
② 语句定义符:用于表示一个语句的功能,如【例2.3】中用到的if else就是条件语句的语句定义符。
③ 预处理命令:用于表示一个预处理命令,如前面各例中用到的include。
(3)运算符。C语言中含有相当丰富的运算符。运算符与变量、函数一起组成表达式,表示各种运算功能。运算符由一个或多个字符组成。
(4)分隔符。在C语言中采用的分隔符有逗号和空格两种。逗号主要用在类型说明和函数参数表中,分隔各个变量。空格多用于语句各单词之间,作为间隔符。在关键字、标志符之间必须有一个以上的空格符做间隔,否则将会出现语法错误,例如把char a写成chara,C语言编译器会把chara当成一个标志符处理,其结果必然出错。
(5)常量。C语言中使用的常量可分为数字常量、字符常量、字符串常量、符号常量、转义字符常量等多种,将在后面章节中给予专门介绍。
(6)注释符。C语言的注释符是以“/*”开头并以“*/”结尾的串。在“/*”和“*/”之间的即为注释。程序编译时,不对注释做任何处理。注释可出现在程序中的任何位置。注释用来向用户提示或解释程序的意义。在调试程序中对暂不使用的语句也可用注释符括起来,使编译跳过不做处理,待调试结束后再去掉注释符。
即时训练
(1)用scanf函数通过键盘向变量A、B、C输入数据,其中A为整型变量,B为浮点型变量,C为字符型变量,写出实现的代码。
(2)已知圆的半径R=2.5,求圆的周长及面积。用scanf函数输入数据,用printf函数在显示器上输出结果。
(3)优化“小学生选题系统”界面,可适当对功能模块进行调整,使其更加友好、易用。
建议教学时以组为单位选出代表对本组优秀作品进行阐述并演示,最后选择出最佳设计1~2件,作为最后的界面设计方案。
拓展任务
设计体育彩票销售系统的界面。要求有登录模块、体育彩票服务站模块、自行投注模块、添加顾客模块,具体界面设计如图2.19至图2.22所示。
图2.19 体育彩票销售系统登录界面
图2.20 体育彩票销售系统主界面
图2.21 自行投注界面
图2.22 添加顾客界面
2.4.2 子任务二:登录模块的功能实现
任务描述
在登录模块界面设计的基础上,具体实现登录模块的功能,即当用户输入用户名和密码后,系统进行判断。如果用户名和密码正确,则进入主界面;如果错误,则要求重新输入;如果三次错误,则退出系统。
任务分析与设计
1.基本流程
登录模块的基本流程如表2.24所示。
表2.24 登录模块的基本流程
2.自然语言描述
S1:定义用户名、密码、累计用户名与密码错误次数的变量分别为username、 password 、number,初始化number的值为0。
S2:接收从键盘输入的用户名和密码。
S3:用户明确自己的用户名和密码,如可以分别为8888。
S4:判断用户名、密码与自己已确定的用户名、密码是否相同。如果相同,则执行S5,否则执行S6。
S5:调用主界面showMainMenu()函数。
S6:清屏。
S7:累计用户名与密码错误次数的变量number,判断变量number是否小于3。如果小于3,则执行S2,否则执行S8。
S8:退出系统。
3.设计流程图
登录模块的设计流程图如图2.23所示。
图2.23 登录模块的设计流程图
任务实现
void login(); /*声明login函数*/ void login() /*定义login函数*/ { int username,password; /*定义用户名username和密码password两个整型变量*/ //long j; printf("\n\n"); /*\n为换行符*/ printf("\t\t****************************************************\n"); printf("\t\t 选题系统登录 \n"); printf("\t\t****************************************************\n"); printf("\n\n"); printf("\t\t\t\t请输入用户名:" ); scanf("%d",&username); printf("\n\n"); printf("\t\t\t\t请输入密码:"); scanf("%d",&password); printf("\n\n"); printf("\n\n"); if (username==8888 && password==8888) { system("cls"); showMainMenu(); } else { number++; if(number<3) { system("cls"); /*清除屏幕上的内容*/ login(); } exit(0); else } }
引导文献
1.数据类型
数据是程序处理的对象。程序设计的过程就是数据加工的过程。一种语言支持的数据类型越丰富,其功能越强、应用范围越广。C语言规定在程序中使用的每个数据都必须属于某种类型,它还提供了丰富的数据类型,主要有基本数据类型、构造数据类型、指针类型、空类型四大类。基本数据类型有整型、实型(单精度型、双精度型)、字符串型、指针型等。本节重点介绍以下5种常用的数据类型。
1)整型(int)
int代表整型,如123、-567、75、90000等。
2)实型
带小数点的数据就是实型,根据数的范围可为分float、double两种类型。
(1)float(单精度型)。float代表的是单精度数据类型,如7.78、-556.444等。
(2)double(双精度型)。double代表的是双精度数据类型,数的范围比float类型大,如1.22554545、222555.0155等。
3)字符型(char)
char代表的是字符型,用一对单引号引起的一个数据就是char类型,如'311'、'A'、'b'等。
4)字符串型
用双引号引起来的数据类型就是字符串型,如"王丹"、"stud"、"F435"等。注意:在C语言中,系统没有提供字符串这种数据类型,一般用数组来表示,关于字符串的详细内容将在后面介绍。
5)指针型
内存单元的地址编号是一种非常特殊的数据类型,称为指针型,简单地说,内存单元的地址就是指针。关于指针的详细内容将在后面介绍。
2.常量与变量
对于基本数据类型,按其取值是否可改变又分为常量和变量两种。在程序执行过程中,其值不发生改变的量称为常量,其值可变的量称为变量。它们可与数据类型结合起来分类。例如,可分为整型常量、整型变量、浮点常量、浮点变量、字符常量、字符变量、枚举常量、枚举变量。在程序中,常量是可以不经说明而直接引用的,而变量则必须先定义后使用。
整型变量包括整型常量、整型变量。
1)常量和符号常量
在程序执行过程中,其值不发生改变的量称为常量。
(1)直接常量(字面常量)。
① 整型常量:34、0、-13。
② 实型常量:14.7、-4.23。
③ 字符常量:'x'、'y'。
(2)标志符:用来标志变量名、符号常量名、函数名、数组名、类型名、文件名的有效字符序列。
(3)符号常量:用标示符代表一个常量。在C语言中,可以用一个标志符来表示一个常量,称为符号常量。
符号常量在使用之前必须先定义,其一般形式为:
#define标志符 常量
其中,#define也是一条预处理命令(预处理命令都以"#"开头),称为宏定义命令(在后面预处理程序中将进一步介绍),其功能是把该标志符定义为其后的常量值。一经定义,以后在程序中所有出现该标志符的地方均以该常量值代之。
(4)习惯上,符号常量的标志符用大写字母,变量标志符用小写字母,以示区别。
例如:符号常量的使用。
【例2.4】
#define PRICE 50 main() { int num,sum; num=20; sum=num* PRICE; printf("sum=%d",sum); }
说明:
(1)用标志符代表一个常量,称为符号常量。
(2)符号常量与变量不同,它的值在其作用域内不能改变,也不能再被赋值。
(3)使用符号常量的好处是:含义清楚;能做到“一改全改”。
2)变量
其值可以改变的量称为变量。一个变量应该有一个名字,在内存中占据一定的存储单元,如图2.24所示。变量定义必须放在变量使用之前。一般放在函数体的开头部分。区分变量名和变量值是两个不同的概念:
图2.24 变量与变量名
变量定义的一般形式为:
类型说明符 变量名标志符,变量名标志符,…;
例如:
int x,y,z; (x,y,z为整型变量) long x1,y1; (x1,y1为长整型变量) unsigned x2,y2; (x2,y2为无符号整型变量)
在书写变量定义时,应注意以下几点:
(1)允许在一个类型说明符后,定义多个相同类型的变量。各变量名之间用逗号间隔。类型说明符与变量名之间至少用一个空格间隔。
(2)最后一个变量名之后必须以“;”号结尾。
(3)变量定义必须放在变量使用之前。一般放在函数体的开头部分。
变量赋初值:在程序中常常需要对变量赋初值,以便使用变量。语言程序中可有多种方法为变量提供初值。本小节先介绍在做变量定义的同时给变量赋以初值的方法。这种方法称为初始化。在变量定义中赋初值的一般形式为:
类型说明符 变量1=值1,变量2=值2,…;
【例2.5】
int a=13; int b,c=15; float x=4.2,y=6,z=10.1; char ch1='T',ch2='H';
应注意,在定义中不允许连续赋值,如a=b=c=5是不合法的。
3.字符型数据
1)字符常量
用一对单引号括起来的单个字符,称为字符常量,例如,'A'、'2'、'+'、'%'等都是有效的字符型常量。一个字符常量的值是该字符集中对应的编码值,例如在ASCII字符集中,字符常量0~9的ASCII编码值是48~57。
2)字符变量
字符变量的类型关键字为char,一般占用1字节内存单元。字符变量用来存储字符常量。将一个字符常量存储到一个字符变量中,实际上是将该字符的ASCII码值(无符号整数)存储到内存单元中。字符数据在内存中存储的是字符的ASCII码——一个无符号整数,其形式与整数的存储形式一样,所以C语言允许字符型数据与整型数据之间通用。
字符变量具有以下特点。
(1)一个字符型数据,既可以字符形式输出,也可以整数形式输出。
【例2.6】
//用字符形式和整数形式输出字符变量 main() { char ch1,ch2; ch1='a'; ch2='b'; printf("ch1=%c,ch2=%c\n",ch1,ch2); printf("ch1=%d,ch2=%d\n",ch1,ch2); }
程序运行结果:
ch1=a,ch2=b ch1=97,ch2=98
(2)允许对字符数据进行算术运算,此时就是对它们的ASCII码值进行算术运算。
【例2.7】
//字符数据的算术运算 main() { char ch1,ch2; ch1='a'; ch2='B'; printf("ch1=%c,ch2=%c\n",ch1-32,ch2+32); printf("ch1+100=%d\n", ch1+100); printf("ch1+256=%c\n", ch1+256); }
程序运行结果:
ch1=A,ch2=b ch1+100=197 ch1+256=a
3)字符串常量
字符串常量是用一对双引号括起来的若干字符序列。字符串中字符的个数称为字符串长度。长度为0的字符串(即一个字符都没有的字符串)称为空串,表示为" "(一对紧连的双引号)。
例如,"How are you"、"student"等,都是字符串常量,其长度分别为11和7(空格也是一个字符)。
C语言规定:在存储字符串常量时,由系统在字符串的末尾自动加一个'\0'作为字符串的结束标志。
字符常量与字符串常量的区别是:
(1)定界符不同:字符常量使用单引号,而字符串常量使用双引号。
(2)长度不同:字符常量的长度固定为1,而字符串常量的长度,可以是0,也可以是某个整数。
(3)存储要求不同:字符常量存储的是字符的ASCII码值,而字符串常量,除了要存储有效的字符外,还要存储一个结束标志'\0'。
4.转义字符
转义字符是一种特殊的字符常量。转义字符以反斜线“\”开头,后跟一个或几个字符。转义字符具有特定的含义,不同于字符原有的意义,故称“转义”字符。例如,在前面各例题printf函数的格式串中用到的“\n”就是一个转义字符,其意义是“回车换行”。转义字符主要用来表示那些用一般字符不便于表示的控制代码。常用的转义字符及其含义如表2.25所示。
表2.25 常用的转义字符及其含义
广义地讲,C语言字符集中的任何一个字符均可用转义字符来表示。表中的\ddd和\xhh正是为此而提出的。ddd和hh分别为八进制和十六进制的ASCII代码。例如,\101表示字母“A”,\102表示字母“B”,\134表示反斜线,\XOA表示换行等。
例如:转义字符的使用。
【例2.8】
main() { int a,b,c; a=5; b=6; c=7; printf("abc\tde\rf\n"); printf("hijk\tL\bM\n"); }
5.运算符和表达式
C语言的运算符和表达式数量之多,在高级语言中是少见的。丰富的运算符和表达式使C语言功能更加完善。这也是C语言的主要特点之一。
C语言的运算符不仅具有不同的优先级,而且还有一个特点,就是它的结合性。在表达式中,各运算量参与运算的先后顺序不仅要遵守运算符优先级别的规定,还要受运算符结合性的制约,以便确定是自左向右进行运算还是自右向左进行运算。这种结合性是其他高级语言的运算符所没有的,因此也增加了C语言的复杂性。
1)C语言的运算符
C语言的运算符可分为以下几类。
(1)算术运算符:用于各类数值运算,包括加(+)、减(-)、乘(*)、除(/)、求余(或称模运算,%)、自增(++)、自减(--)共7种。
(2)关系运算符:用于比较运算,包括大于(>)、小于(<)、等于(=)、大于等于(>=)、小于等于(<=)和不等于(≠)共6种。
(3)逻辑运算符:用于逻辑运算,包括与(&&)、或(||)、非(!)共三种。
(4)赋值运算符:用于赋值运算,分为简单赋值(=)、复合算术赋值(+=,-=,*=,/=,%=)和复合位运算赋值(&=,|=,^=,>>=,<<=)三类共11种。
(5)条件运算符:这是一个三目运算符,用于条件求值(?:)。
(6)指针运算符:用于取内容(*)和取地址(&)两种运算。
(7)求字节数运算符:用于计算数据类型所占的字节数(sizeof)。
(8)特殊运算符:有括号(),下标[],成员(→)等几种。
2)算术运算符和算术表达式
(1)基本的算术运算符。
① 加法运算符“+”:加法运算符为双目运算符,即应有两个量参与加法运算,如x+y、2+3等,具有右结合性。
② 减法运算符“-”:减法运算符为双目运算符。但“-”也可作负值运算符,此时为单目运算,如-a,-15等,具有左结合性。
③ 乘法运算符“*”:双目运算,具有左结合性。
④ 除法运算符“/”:双目运算,具有左结合性。参与运算量均为整型时,结果也为整型,舍去小数。如果运算量中有一个是实型,则结果为双精度实型。
【例2.9】
main(){ printf("\n\n%d,%d\n",30/7,-30/7); printf("%f,%f\n",30.0/7,-30.0/7); }
说明:在本例中,30/7、-30/7的结果均为整型,小数全部舍去;而30.0/7和-30.0/7由于有实数参与运算,因此结果也为实型。
⑤ 求余运算符(模运算符)“%”:双目运算,具有左结合性。要求参与运算的量均为整型。求余运算的结果等于两数相除后的余数。
【例2.10】
main(){ printf("%d\n",200%3); }
(2)算术表达式、运算符的优先级和结合性。表达式是由常量、变量、函数和运算符组合起来的式子。一个表达式有一个值及其类型,它们等于计算表达式所得结果的值和类型。表达式求值按运算符的优先级和结合性规定的顺序进行。单个的常量、变量、函数可以看成是表达式的特例。
算术表达式是由算术运算符和括号连接起来的式子。
① 算术表达式:用算术运算符和括号将运算对象(也称操作数)连接起来的、符合C语法规则的式子。
以下是算术表达式的例子:
x+y (a*2)/b (x+y)*5-(a+b)/3 ++I sin(x)+sin(y) (++i)-(j++)
② 运算符的优先级:在C语言中,运算符的运算优先级共分为15级。1级最高,15级最低。在表达式中,优先级较高的先于优先级较低的进行运算。在一个运算量两侧的运算符优先级相同时,则按运算符的结合性所规定的结合方向处理。
③ 运算符的结合性:在C语言中,各运算符的结合性分为两种,即左结合性(自左至右)和右结合性(自右至左)。例如,算术运算符的结合性是自左至右,即先左后右。若有表达式x-y+z,则y应先与“-”符号结合,执行x-y运算,然后再执行+z的运算。这种自左至右的结合方向就称为“左结合性”;而自右至左的结合方向称为“右结合性”。最典型的右结合性运算符是赋值运算符。例如x=y=z,由于“=”的右结合性,应先执行y=z,再执行x=(y=z)运算。C语言运算符中有不少为右结合性,应注意区别,以避免理解错误。
④ 强制类型转换运算符:其一般形式如下。
(类型说明符)(表达式)
它的功能是把表达式的运算结果强制转换成类型说明符所表示的类型。例如:
(float)z 把z转换为实型 (int)(a+b) 把a+b的结果转换为整型
(3)自增、自减运算符。自增1、自减1运算符:自增1运算符记为“++”,其功能是使变量的值自增1。自减1运算符记为“--”,其功能是使变量值自减1。
自增1、自减1运算符均为单目运算,都具有右结合性。可有以下4种形式。
① ++i:i自增1后再参与其他用运算。
② --i:i自减1后再参与其他用运算。
③ i++:i参与运算后,i的值再自增1。
④ i--:i参与运算后,i的值再自减1。
在理解和使用上容易出错的是i++和i--。特别是,当它们出在较复杂的表达式或语句中时,常常难以弄清,因此应仔细分析。
【例2.11】
main(){ int i=2; printf("%d\n",++i); printf("%d\n",--i); printf("%d\n",i++); printf("%d\n",i--); }
3)赋值运算符和赋值表达式
(1)赋值运算符。简单赋值运算符和赋值表达式:简单赋值运算符记为“=”。由“=”连接的式子称为赋值表达式。一般形式为:
变量=表达式
例如:
x=a+b y=i++
赋值表达式的功能是计算表达式的值再赋予左边的变量。赋值运算符具有右结合性。因此
x=y=z=8
可理解为
x=(y=(z=8))
在其他用高级语言中,赋值构成了一个语句,称为赋值语句;而在C语言中,把“=”定义为运算符,从而组成赋值表达式。凡是表达式可以出现的地方均可出现赋值表达式。
例如,下面的式子
x=(a=5)+(b=8)
是合法的。它的意义是把5赋予a,把8赋予b,再把a与b相加,把和赋予x,故x应等于13。
在C语言中也可以组成赋值语句,按照C语言规定,任何表达式在其末尾加上分号就构成为语句。因此如下式子
x=8;a=b=c=5;
是赋值语句,在前面各例中已使用过了。
(2)类型转换。如果赋值运算符两边的数据类型不相同,系统将自动进行类型转换,即把赋值号右边的类型换成左边的类型。具体规定如下:
① 实型赋予整型,舍去小数部分。前面的例子已经说明了这种情况。
② 整型赋予实型,数值不变,但将以浮点形式存放,即增加小数部分(小数部分的值为0)。
③ 字符型赋予整型,由于字符型为一个字节,而整型为两个字节,故将字符的ASCII码值放到整型量的低8位中,高8位为0。整型赋予字符型,只把低8位赋予字符量。
【例2.12】
main(){ int a,b=15; float x,y=1.86; char z1='p',z2; a=y; x=b; a=z1; z2=b; printf("%d,%f,%d,%c",a,x,a,c2); }
本例表明了上述赋值运算中类型转换的规则。a为整型,赋予实型量y值1.86后只取整数1。x为实型,赋予整型量b值15。字符型量z1赋予a变为整型,整型量b赋予z2后取其低8位成为字符型(b的低8位为01000010,即十进制66,按ASCII码对应于字符B)。
(3)复合的赋值运算符。在赋值符“=”之前加上其他用二目运算符可构成复合赋值符,如+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=。
构成复合赋值表达式的一般形式为:
变量 双目运算符=表达式
它等效于
变量=变量 运算符 表达式
例如:
p+=6 等价于p=p+6 x*=y+8 等价于x=x*(y+8) a%=b 等价于a=a%b
对于复合赋值符这种写法,初学者可能不习惯,但十分有利于编译处理,能提高编译效率并产生质量较高的目标代码。
6.简单分支语句——if介绍
用if语句可以构成分支结构。它根据给定的条件进行判断,以决定执行某个分支程序段。
1)第一种形式为基本形式:if
if(表达式) 语句
其语义是:如果表达式的值为真,则执行其后的语句,否则不执行该语句。第一种if语句的执行流程如图2.25所示。
图2.25 第一种if语句的执行流程
【例2.13】
main() { int x,y,max; printf("\n请输入两个数:"); scanf("%d%d",&x,&y); max=x; if (max<y) max=y; printf("max=%d",max); }
在本例程序中,输入x和y两个数。把x先赋予变量max,再用if语句判别max和y的大小。若max小于y,则把y赋予max。因此,max中总是大数,最后输出max的值。
2)第二种形式为:if-else
if(表达式) 语句1; else 语句2;
语义是:如果表达式的值为真,则执行语句1,否则执行语句2。
第二种if语句的执行流程如图2.26所示。
图2.26 第二种if语句的执行流程
【例2.14】
main() { int x,y; printf("请输入两个数:"); scanf("%d%d",&x,&y); if(x>y) printf("max=%d\n",x); else printf("max=%d\n",y); }
本例输入两个整数,输出其中的大数。
即时训练
(1)输入A、B、C三个数,要求按由小到大的顺序输出。
(2)判断用户输入的数是奇数还是偶数,然后在屏幕上显示出相应的信息。
(3)如果将原来代码中的判断条件由if(number<3)变为if(number>3),则重新编写代码实现登录模块。
建议完成代码后,将更改前后的程序进行比较,看哪种实现方法用的代码更少,更能体现结构化设计的优势。
拓展任务
实现体育彩票销售系统的登录模块。
要求:
(1)用户名和密码都为字符型数据。
(2)密码错误给出提示。
(3)密码正确显示主界面。
2.4.3 子任务三:题量设置模块的功能实现
任务描述
题量设置模块主要是实现在四则题库训练选择题量的功能,每次训练前可以按照自己的要求设置本次练习的题量(要求所输入的题量为5的倍数),系统会根据所设置的题量,自动出题。如果不设置题量,则系统默认为5。
任务分析与设计
(1)题量设置模块的基本流程如表2.26所示。
表2.26 题量设置模块的基本流程
(2)自然语言描述。
S1:显示当前默认题量为5。
S2:显示提示信息“请输入你选择的题量(5的倍数)”。
S3:用户输入新设置的题量。
S4:判断题量是否为5的倍数。如果是,则更新题量设置,否则执行S2。
S5:调用主界面showMainMenu()函数。
S6:退出系统。
(3)设计流程图如图2.27所示。
图2.27 题量设置模块的设计流程图
任务实现
(1)根据系统设计,题量设置需要定义全局变量amount。
(2)根据函数关系图确定题量设置函数名称systemSetting()。
(3)实现函数功能。
(4)编码实现。
题量设置设计:
void systemSetting();//函数声明 //定义题量设置函数 void systemSetting() { system("cls"); printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 题量设置 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); printf("\t\t当前默认题量为:"); printf("%d\n\n",problemNumber); do { printf("\t\t请输入你选择的题量(5的倍数):"); scanf("%d",&problemNumber); }while(problemNumber%5!=0); system("cls"); showMainMenu(); }
引导文献
1.局部变量和全局变量
1)局部变量
局部变量也称为内部变量。局部变量是在函数内做定义说明的,其作用域仅限于函数内,离开该函数后再使用这种变量是非法的。
说明:
(1)主函数中定义的变量也只能在主函数中使用,不能在其他用函数中使用。因为主函数也是一个函数,它与其他函数是平行关系。这一点与其他语言不同,应予以注意。
(2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。
(3)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。
(4)在复合语句中也可定义变量,其作用域只在复合语句范围内。
2)全局变量
全局变量是定义在函数外面的变量,即在所有{}外面,其作用范围是整个程序,也就是说,用户可以在任何函数内直接使用,不必每次都定义;但是,如果在一个函数内,局部变量与全局变量名相同,使用这个变量时,以定义的局部变量的值为准,但不改变全局变量的值!局部变量是定义在一个函数里的变量,即在某一个{}里面,只能在这个函数范围内使用。
例如:
int a=3;/* 变量a就是全局变量*/ main() { int a=10; /*局部与全局变量名相同,在本函数中以局部值为准,但不改变全局变量值*/ printf("%d\n",fun(5)*a); /*这里的a值是10 / } fun(int b) { if(b==0) return(a); /*这里的a还是全局变量值*/ return(fun(b-1)*b); }
程序运行的结果是3600。
首先,上述程序是要求我们输出fun(5)*a,这里的a的值应该是主函数里定义的10,而不是外面的全局变量3(变量的作用域);然后程序去调用子函数fun()来计算fun(5),根据题目,即返回fun(4)*5,…,递归调用,相当于fun(0)*1*2*3*4*5,而到fun(0)时应返回a,这里的a就是全局变量3。因为该fun()中没有再次定义a这个变量,所以fun(5)最后的结果应该是3*1*2*3*4*5=360。因此,最终结果为fun(5)*10=3600。
2.变量的存储类别
变量的存储方式可分为“静态存储”和“动态存储”两种。
1)静态存储变量
静态存储变量通常是在变量定义时就分配存储单元并一直保持不变,直至整个程序结束。
2)动态存储变量
动态存储变量是在程序执行过程中,使用它时才分配存储单元,使用完毕立即释放。
典型的例子是函数的形式参数,在函数定义时并不给形参分配存储单元,只是在函数被调用时,才予以分配,调用函数完毕立即释放。如果一个函数被多次调用,则反复地分配、释放形参变量的存储单元。从以上分析可知,静态存储变量是一直存在的,而动态存储变量则时而存在时而消失。我们又把这种由于变量存储方式不同而产生的特性称为变量的生存期。生存期表示了变量存在的时间。生存期和作用域从时间和空间这两个不同的角度来描述变量的特性,这两者既有联系,又有区别。一个变量究竟属于哪一种存储方式,并不能仅从其作用域来判断,还应有明确的存储类型说明。
在C语言中,对变量的存储类型说明有以下4种。
(1)auto:自动变量。
(2)register:寄存器变量。
(3)extern:外部变量。
(4)static:静态变量。
自动变量和寄存器变量属于动态存储方式,外部变量和静态变量属于静态存储方式。在介绍变量的存储类型之后,可以知道对一个变量的说明不仅应说明其数据类型,还应说明其存储类型。因此,变量说明的完整形式应为:
存储类型说明符 数据类型说明符 变量名,变量名…;
【例2.15】
static int x,y; 说明x,y为静态类型变量 auto char c1,c2; 说明c1,c2为自动字符变量 static int s[5]={1,2,3,4,5}; 说明s为静态整型数组 extern int a,b; 说明a,b为外部整型变量
下面介绍自动变量的存储类型。
自动变量的存储类型说明符为auto。这种存储类型是C语言程序中使用最广泛的一种类型。C语言规定,函数内凡未加存储类型说明的变量均视为自动变量,也就是说,自动变量可省去说明符auto。在前面各章的程序中所定义的变量凡未加存储类型说明符的都是自动变量。
【例2.16】
{ int i,j,k; char c; …… }等价于: {auto int i,j,k; auto char c; …… }
自动变量具有以下特点:自动变量的作用域仅限于定义该变量的个体内。在函数中定义的自动变量,只在该函数内有效。在复合语句中定义的自动变量只在该复合语句中有效。
【例2.17】
int kv(int a) { auto int x,y; { auto char c; } /*c的作用域*/ …… } /*a,x,y的作用域*/
自动变量属于动态存储方式,只有在使用它,即定义该变量的函数被调用时才给它分配存储单元,开始它的生存期。函数调用结束,释放存储单元,结束生存期。因此,函数调用结束之后,自动变量的值不能保留。在复合语句中定义的自动变量,在退出复合语句后也不能再使用,否则将引起错误。
【例2.18】
main() { auto int a,s,p; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0){ s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); } { auto int a; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0){ auto int s,p; s=a+a; p=a*a; } printf("s=%d p=%d\n",s,p); }
s、p是在复合语句内定义的自动变量,只能在该复合语句内有效。上面程序的第9行却是退出复合语句之后用printf语句输出s、p的值,这显然会引起错误。
由于自动变量的作用域和生存期都局限于定义它的个体内(函数或复合语句内),因此不同的个体中允许使用同名的变量而不会混淆。即使在函数内定义的自动变量也可与该函数内部的复合语句中定义的自动变量同名。
【例2.19】
main() { auto int a,s=100,p=100; printf("\ninput a number:\n"); scanf("%d",&a); if(a>0) { auto int s,p; s=a+a; p=a*a; printf("s=%d p=%d\n",s,p); } printf("s=%d p=%d\n",s,p); }
本程序在main函数中和复合语句内两次定义了变量s、p为自动变量。按照C语言的规定,在复合语句内,应由复合语句中定义的s、p起作用,故s的值应为a+a,p的值为a*a。退出复合语句后的s、p应为main所定义的s、p,其值在初始化时给定,均为100。从输出结果可以分析出两个s和两个p虽变量名相同,但却是两个不同的变量。
对构造类型的自动变量(如数组等),不可做初始化赋值。
3.while循环语句
一般形式为:
while(表达式)语句
其中表达式是循环条件,语句为循环体。
该循环语句的执行过程是:计算表达式的值,当值为真(非0)时,执行循环体语句。while循环语句的执行过程如图2.28所示。
图2.28 while循环语句的执行过程
【例2.20】 用while循环语句求。
用while循环语句求 的算法流程图如图2.29 所示。
图 2.29 用while循环语句求 的算法流程图
main() { int i,sum=0; i=1; while(i<=100) { sum=sum+i; i++; } printf("%d\n",sum); }
【例2.21】 统计从键盘输入一行字符的个数。
#include <stdio.h> main(){ int n=0; printf("请输入一行字符(回车结束):\n"); while(getchar()!='\n') n++; printf("%d",n); }
本例程序中的循环条件为getchar()!='\n',其意义是,只要从键盘输入的字符不是回车就继续循环。循环体n++完成对输入字符的个数计数。该程序实现了对输入一行字符的个数计数。
说明:
(1)循环体如果包含一个以上的语句,则必须用花括号括起来,以复合语句的形式出现,否则while语句范围只到while后面第一个分号处。
(2)在循环体中应有使循环趋向于结束的语句,即设置修改循环条件的语句。
(3)while语句先判断表达式的值,然后执行循环体中的语句。如果表达式的值一开始为假(值为0),则不执行循环体,直接执行循环体以外的语句。表达式是控制循环的条件,它可以是任何类型的表达式。
4.do while循环语句
一般形式为:
do 语句 while(表达式);
该循环语句的执行过程是:先执行循环中的语句,然后再判断表达式是否为真。如果为真,则继续循环;如果为假,则终止循环。因此,do while循环至少要执行一次循环语句。do while循环语句的执行过程如图2.30所示。
图2.30 do while循环语句的执行过程
【例2.22】 用dowhile循环语句求 。
用dowhile循环语句求 的算法流程图如图2.31 所示。
图 2.31 用dowhile循环语句求 的算法流程图
//求1到100的累加和 void main() { int i,sum=0; i=1; do { sum=sum+i; i++; } while(i<=100); printf("%d\n",sum); }
当有许多语句参加循环时,要用“{”和“}”把它们括起来。
说明:
(1)do while语句的表达式是任意表达式,是控制循环的条件。
(2)先执行后判断。因此,循环体至少执行一次,直到表达式为“假”时才退出循环。因此,在循环体语句中一定要有改变表达式的值的操作,最终使其表达式的值变为0,结束循环。否则将成为“死”循环。
(3)如果do while语句的循环体部分是由多个语句组成的,则必须用左、右花括号括起来,使其形成复合语句。
(4)在关键字while的小括号的后面,一定要加分号“;”,千万不能忘记,它表示do while语句到此结束。
【例2.23】 while和do while循环语句的比较。
(1)用while循环求1-100的和 main() {int sum=0,i; scanf("%d",&i); while(i<=100) {sum=sum+i; i++; } printf("sum=%d",sum); } (2)用do-while循环求1-100的和 main() {int sum=0,i; scanf("d",&i); do {sum=sum+i; i++; }while(i<=100); printf("sum=%d",sum); }
5.switch case语句
switch(num) { case 0: system("cls"); showMainMenu(); break; case 1: system("cls"); addition(); //加法题库 break; case 2: system("cls"); subtration(); //减法题库 break; case 3: system("cls"); multiplication(); //乘法题库 break; case 4: system("cls"); division(); //除法题库 break; default: printf("\n\t\t请按任意键重新选择!!!"); getchar(); system("cls"); selectProblemMenu(); }
即时训练
设计红、绿、灯程序,输入R显示红灯;输入G显示绿灯;输入Y显示黄灯。
拓展任务
设计体育彩票销售系统自行投注模块,添加顾客后,顾客自选投注号码,具体效果如图2.32和图2.33所示。
图2.32 自行投注界面
图2.33 产生投注号码
2.4.4 子任务四:四则题库模块的功能实现
任务描述
(1)具体实现四则题库模块的题型选择功能,即显示题库选择界面,当用户选择题型后,系统进行判断,如果选择的是加法题库,进入加法题库界面,以此类推;如果选择的是返回操作,则返回主控界面;如果输入其他值,则返回题库选择界面要求重新输入选择。
(2)在四则题库模块界面设计的基础上,实现加法模块的功能。
加法题库:每次训练的题量为系统全局变量problem的值,结束后可以选择“继续”或者“取消”,如果选择“继续”选项,系统会再出一套题继续训练;否则进行其他操作。
(3)在四则题库模块界面设计的基础上,实现减法模块的功能。
减法题库:输出算式时要求操作数x1>x2,即结果为大于或等于0的结果。
(4)在四则题库模块界面设计的基础上,实现乘法模块的功能。
(5)在四则题库模块界面设计的基础上,实现除法模块的功能。
除法题库:输出的操作数x1、x2做整除运算,并且除数x2不能为0。
任务分析与设计
1.四则题库模块
(1)基本流程如表2.27所示。
表2.27 四则题库模块的基本流程
(2)自然语言描述。
S1:输出四则题库界面。
S2:输入选择给变量num。
S3:判断num的值,为0,调用主控函数;为1,调用加法题库;为2,调用减法题库;为3,调用乘法题库;为4,调用除法题库;输入其他任意值,返回四则题库界面重新选择。
S4:退出系统。
(3)四则题库模块的设计流程图如图2.34所示。
图2.34 四则题库模块的设计流程图
2.加法题库模块
(1)基本流程如表2.28所示。
表2.28 加法题库模块的基本流程
图2.35 加法题库操作界面
(2)自然语言描述。
S1:定义整型变量。i为循环控制变量,x1、x2为随机产生1~20的操作数,sumsys为正确答案,sumuser为用户答案,num为是否继续操作的控制变量。
S2:变量i初始化为1。
S3:判断i是否小于或等于题量设置problemNumber。若不符合,则执行S9。
S4:产生1~20之间的随机整数分别赋给x1、x2。
S5:输出加法算式。
S6:用户输入答案。
S7:判断用户输入答案与正确答案是否相同。若相同,则输出正确的提示信息,同时全局变量tnum自加;否则输出错误的提示信息和正确答案,全局变量fnum自加。
S8:i自加,然后执行S3。
S9:输出是否继续的提示信息。
S10:用户输入选择的操作。
S11:判断用户输入的信息,如果为继续(1),则调用加法函数addition;如果为取消(2),则返回四则题库函数selectProblemMenu();否则执行S8。
(3)设计流程图如图2.36所示。
图2.36 加法题库模块的设计流程图
3.减法题库模块
(1)基本流程如表2.29所示。
表2.29 减法题库模块的基本流程
(2)自然语言描述。
S1:定义整型变量。i为循环控制变量,x1、x2为随机产生1~20的操作数,sumsys为正确答案,sumuser为用户答案,num为是否继续操作的控制变量。
S2:变量i初始化为1。
S3:判断i是否小于或等于题量设置problemNumber。若不符合,则执行S10。
S4:产生1~20之间的随机整数分别赋给x1、x2。
S5:判断是否x1<x2,若符合,则互换两变量的值。
S6:输出算式。
S7:用户输入答案。
S8:判断用户输入答案与正确答案是否相同。若相同,则输出正确的提示信息,同时全局变量tnum自加;否则输出错误的提示信息和正确答案,全局变量fnum自加。
S9:是i自加,执行S3。
S10:输出是否继续的提示信息。
S11:用户输入选择的操作。
S12:判断用户输入的信息。如果为继续,则调用减法函数subtration;如果为取消,则返回四则题库函数selectProblemMenu();否则执行S10。
(3)设计流程图如图2.37所示。
图2.37 减法题库模块的设计流程图
4.乘法题库模块
(1)基本流程如表2.30所示。
表2.30 乘法题库模块的基本流程
(2)自然语言描述。
S1:定义整型变量。i为循环控制变量,x1、x2为随机产生1~20的操作数,sumsys为正确答案,sumuser为用户答案,num为是否继续操作的控制变量。
S2:变量i初始化为1。
S3:判断i是否小于或等于题量设置problemNumber。若不符合,则执行S9。
S4:产生1~20之间的随机整数分别赋给x1、x2。
S5:输出算式。
S6:用户输入答案。
S7:判断用户输入答案与正确答案是否相同。若相同,则输出正确的提示信息,同时全局变量tnum自加;否则输出错误的提示信息和正确答案,全局变量fnum自加。
S8:i自加,执行S3。
S9:输出是否继续的提示信息。
S10:用户输入选择的操作。
S11:判断用户输入的信息。如果为继续,则调用乘法函数multiplication;如果为取消,则返回四则题库函数selectProblemMenu();否则执行S9。
(3)设计流程图如图2.38所示。
图2.38 乘法题库模块的设计流程图
5.除法题库模块
(1)基本流程如表2.31所示。
表2.31 除法题库模块的基本流程
(2)自然语言描述。
S1:定义整型变量。i为循环控制变量,x1、x2为随机产生操作数,sumsys为正确答案,sumuser为用户答案;num为是否继续操作的控制变量。
S2:变量i初始化为1。
S3: 判断i是否小于或等于题量设置problemNumber。若不符合,则执行S10。
S4:产生随机整数分别赋给sumsys、x2(不为零)。
S5:给变量x1赋值为sumsys*x2的值。
S6:输出算式x1÷x2=。
S7:用户输入答案。
S8:判断用户输入答案与正确答案是否相同。若相同,则输出正确的提示信息,同时全局变量tnum自加;否则输出错误的提示信息和正确答案,同时全局变量fnum自加。
S9:i自加,执行S3。
S10:输出是否继续的提示信息。
S11:用户输入选择的操作。
S12:判断用户输入的信息。如果为继续,则调用除法函数division;如果为取消,则返回四则题库函数selectProblemMenu();否则执行S10。
(3)设计流程图如图2.39所示。
图2.39 除法题库模块的设计流程图
任务实现
void selectProblemMenu(); //声明void selectProblemMenu函数 void addition() //加法函数的声明 void subtration() //减法函数的声明 void multiplication() //乘法函数的声明 void division() //除法函数的声明 //四则题库界面代码 void selectProblemMenu() { int num; system("cls"); printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 四则题库 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); printf("\t\t\t\t 1. 加 法 题 库\n\n"); printf("\t\t\t\t 2. 减 法 题 库\n\n"); printf("\t\t\t\t 3. 乘 法 题 库\n\n"); printf("\t\t\t\t 4. 除 法 题 库\n\n"); printf("\t\t\t\t 0. 返回主控界面\n\n"); printf("\n\t\t请 选 择 操 作(0-4):"); scanf("%d",&num); switch(num) { case 0: system("cls"); showMainMenu(); break; case 1: system("cls"); addition(); //加法题库 break; case 2: system("cls"); subtration(); //减法题库 break; case 3: system("cls"); multiplication(); //乘法题库 break; case 4: system("cls"); division(); //除法题库 break; default: printf("\n\t\t请按任意键重新选择!!!"); getchar(); system("cls"); selectProblemMenu(); } } //加法题库 void addition() { int i; //控制变量 int num; //判断变量 int x=1,y=10; //1-10 int x1,x2; //操作数 int sumsys,sumuser; //sumsys为正确答案,sumuser为用户答案 srand(time(0)); for(i=1;i<=problemNumber;i++) { x1=x+rand()%(y-x+10); x2=x+rand()%(y-x+10); sumsys=x1+x2; printf("\n\t\t\t%d+%d=",x1,x2); scanf("%d",&sumuser); if(sumsys==sumuser) { printf("\t\t\t\t\t你答对了,真棒!\n"); tnum1++; } else { printf("\t\t\t\t\t答错了,太可惜啦!正确答案是: %d\n",sumsys); fnum1++; } }//循环结束 do { printf("\n\n\t\t\t===========================\n"); printf("\t\t\t 1.继续 2.取消\n"); printf("\t\t\t===========================\n"); printf("请选择(1或2):"); scanf("%d",&num); }while(num!=1&&num!=2); if(num==2) { system("cls"); selectProblemMenu(); } else { system("cls"); addition(); } system("cls"); } //减法题库 void subtration() { int i; //控制变量 int num; int x=1,y=10; int x1,x2,t; //操作数 int sumsys,sumuser; //sumsys为正确答案,sumuser为用户答案 srand(time(0)); for(i=1;i<=problemNumber;i++) { x1=x+rand()%(y-x+10); x2=x+rand()%(y-x+10); if(x1<x2) { t=x1; x1=x2; x2=t; } sumsys=x1-x2; printf("\n\t\t\t%d-%d=",x1,x2); scanf("%d",&sumuser); if(sumsys==sumuser) { printf("\t\t\t\t\t你答对了,真棒!\n"); tnum2++; } else { printf("\t\t\t\t\t答错了,太可惜啦!正确答案是: %d\n",sumsys); fnum2++; } }//循环结束 do { printf("\n\n\t\t\t===========================\n"); printf("\t\t\t 1.继续 2.取消\n"); printf("\t\t\t===========================\n"); printf("请选择(1或2):"); scanf("%d",&num); }while(num!=1&&num!=2); if(num==2) { system("cls"); selectProblemMenu(); } else { system("cls"); subtration(); } system("cls"); } //乘法题库 void multiplication() { int i; //控制变量 int num; int x=1,y=10; int x1,x2; //操作数 int sumsys,sumuser; //sumsys为正确答案,sumuser为用户答案 srand(time(0)); for(i=1;i<=5;i++) { x1=x+rand()%(y-x+1); x2=x+rand()%(y-x+1); sumsys=x1*x2; printf("\n\t\t\t%d×%d=",x1,x2); scanf("%d",&sumuser); if(sumsys==sumuser) { printf("\t\t\t\t\t你答对了,真棒!\n"); tnum3++; } else { printf("\t\t\t\t\t答错了,太可惜啦!正确答案是: %d\n",sumsys); fnum3++; } }//循环结束 do { printf("\n\n\t\t\t===========================\n"); printf("\t\t\t 1.继续 2.取消\n"); printf("\t\t\t===========================\n"); printf("请选择(1或2):"); scanf("%d",&num); }while(num!=1&&num!=2); if(num==2) { system("cls"); selectProblemMenu(); } else { system("cls"); multiplication(); } system("cls"); } //除法题库 void division() { int i; int num; //控制变量 int x=1,y=10; int x1,x2; //操作数 int sumsys,sumuser; //sumsys为正确答案,sumuser为用户答案 srand(time(0)); for(i=1;i<=problemNumber;i++) { printf("\n"); //实现整除 sumsys=x+rand()%(y-x+10);//商 x2=x+rand()%(y-x+1); //除数不为0 x1=x2*sumsys; //被除数=除数*商 printf("\n\t\t\t%d÷%d=",x1,x2); scanf("%d",&sumuser); if(sumsys==sumuser) { printf("\t\t\t\t\t你答对了,真棒!\n"); tnum4++; } else { printf("\t\t\t\t\t答错了,太可惜啦!正确答案是: %d\n",sumsys); fnum4++; } }//循环结束 do{ printf("\n\n\t\t\t===========================\n"); printf("\t\t\t 1.继续 2.取消\n"); printf("\t\t\t===========================\n"); printf("请选择(1或2):"); scanf("%d",&num); }while(num!=1&&num!=2); if(num==2) { system("cls"); selectProblemMenu();} else{ system("cls"); division();} system("cls"); }
引导文献
1.逻辑运算符和逻辑表达式
1)逻辑运算符及其优先次序
C语言中提供下列三种逻辑运算符。
(1)&&:与运算。
(2)||:或运算。
(3)!:非运算。
与运算符&&和或运算符||均为双目运算符,具有左结合性。非运算符!为单目运算符,具有右结合性。逻辑运算符与其他运算符的优先级关系可表示如下:
!(非)→算术运算符→关系运算符→&&和 ||→赋值运算符
由上可知,非、与、或的优先顺序为:!(非)→&&(与)→||(或)。
2)逻辑运算的值
逻辑运算的值也为“真”和“假”两种,用“1”和“0”来表示,其求值规则如下。
与运算 &&:参与运算的两个量都为真时,结果才为真,否则为假。
例如:15>0 && 14>4。
由于15>0为真,14>4也为真,相与的结果也为真。
或运算||:参与运算的两个量只要有一个为真,结果就为真。两个量都为假时,结果为假。
例如:15>0||15>5。
由于15>0为真,相或的结果也就为真。
非运算!:参与运算量为真时,结果为假;参与运算量为假时,结果为真。
例如:!(15>0)。
由于15>0为真,非运算后的结果也就为假。
虽然C编译在给出逻辑运算值时,以“1”代表“真”,以“0”代表“假”,但反过来,在判断一个量是为“真”还是为“假”时,以“0”代表“假”,以非“0”的数值作为“真”。
例如:15&&5。
由于15和5均为非“0”,因此表达式“15&&5”的值为“真”,即为1。
又如:15||0。
由于15为非“0”,因此表达式“15||0”的值为“真”,即为1。
通过以上的分析,我们总结出如表2.32所示的逻辑运算的真值表,用它表示当a和b为不同组合时,各种逻辑运算所得到的值。
表2.32 逻辑运算的真值表
3)逻辑表达式
逻辑表达式是指用逻辑运算符将1个或多个表达式连接起来,进行逻辑运算的式子。在C语言中,用逻辑表达式表示多个条件的组合。
逻辑表达式的一般形式为:
表达式 逻辑运算符 表达式
其中的表达式可以又是逻辑表达式,从而组成了嵌套的情形。
例如:(a&&b)&&c。
根据逻辑运算符的左结合性,上式也可写为:a&&b&&c。
逻辑表达式的值是式中各种逻辑运算的最后值,以“1”和“0”分别代表“真”和“假”。
例如,下面的表达式都是逻辑表达式:
(x>=0)&&(x<210) (x<9)||(x>3) !(x==0) (year%4==0)&&(year%100!=0)||(year%400==0)
【例2.24】
main(){ char c='h'; int i=10,j=20,h=30; float a=5e+3,b=0.68; printf("%d,%d\n",!a*!b,!!!a); printf("%d,%d\n",a||i&&j-3,i<j&&a<b); printf("%d,%d\n",i==28&&c&&(j=29),a+b||i+j+h); }
在本例中,因!a和!b分别为0,!a*!b也为0,故其输出值为0。因a为非0,故!!!a的逻辑值为0。对a|| i && j-3式,先计算j-3的值为非0,再求i && j-3的逻辑值为1,故a||i&&j-3的逻辑值为1。对于i<j&&a<b式,由于i<j的值为1,而a<b为0,故表达式的值为1,0相与,最后为0;对于i=28&&c&&(j=29)式,由于i=28为假,即值为0,该表达式由两个与运算组成,所以整个表达式的值为0。对于a+b||i+j+h式,由于a+b的值为非0,故整个或表达式的值为1。
2.多分支结构
1)if-else-if语句
一般形式为:
if(表达式1) 〈语句1〉; else if(表达式2) 〈语句2〉; else if(表达式3) 〈语句3〉; … else if(表达式m) 〈语句m〉; else 〈语句n〉;
该语句的执行过程是:依次判断表达式的值,当出现某个值为真时,则执行其对应的语句;然后跳到整个if语句之外继续执行程序。如果所有的表达式均为假,则执行语句n;然后继续执行后续程序。if-else-if语句的执行过程如图2.40所示。
图2.40 if-else-if语句的执行过程
【例2.25】
#include"stdio.h" main() { char s; printf("请输入一个字符:"); s=getchar(); if(s<32) printf("这是一个控制符\n"); else if(s>='0'&&s<='9') printf("这是一个数\n"); else if(s>='A'&&s<='Z') printf("这是一个大写字母\n"); else if(s>='a'&&s<='z') printf("这是一个小写字母\n"); else printf("这是一个其他形式的字符\n"); }
本例的功能是判断从键盘上输入字符的类别。可以根据输入字符的ASCII码来判断其类型。由ASCII码表可知,ASCII值小于32的为控制字符。在“0”和“9”之间为数字,在“A”和“Z”之间为大写字母,在“a”和“z”之间为小写字母,其余则为其他用字符。这是一个多分支选择的问题,用if-else-if语句编程,判断输入字符ASCII码所在的范围,分别给出不同的输出。例如,输入为“A”,输出显示它为大写字符。
说明:
(1)在三种形式的if语句中,if关键字之后均为表达式。该表达式通常是逻辑表达式或关系表达式,但也可以是其他用表达式,如赋值表达式等,甚至也可以是一个变量。
例如:
if(x=3) 语句; if(y) 语句;
都是允许的。只要表达式的值为非0,即为“真”。
例如在if(a=5);语句中,因为表达式的值永远为非0,所以其后的语句总是要执行的。当然,这种情况在程序中不一定会出现,但在语法上是合法的。
(2)在if语句中,条件判断表达式必须用括号括起来,在语句之后必须加分号。
(3)在if语句的三种形式中,所有的语句应为单个语句。如果想在满足条件时执行一组(多个)语句,则必须把这一组语句用{}括起来组成一个复合语句。但是,要注意在}之后不能加分号。
例如:
if(x>y) { x=x-10; y=y+10; } else { x=x+10; y=y-10; }
2)switch语句
一般形式为:
switch(表达式) { case 常量表达式1:语句组;[break;] case 常量表达式2:语句组;[break;] … case 常量表达式n:语句组;[break;] [default:语句组;[break;]] }
switch语句的执行过程是:
(1)当switch后面“表达式”的值,与某个case后面的“常量表达式”的值相同时,就执行该case后面的语句(组);当执行到break语句时,跳出switch语句,转向执行switch语句的下一条。
(2)如果没有任何一个case后面的“常量表达式”的值与“表达式”的值匹配,则执行default后面的语句(组);然后,再执行switch语句的下一条。
switch语句的执行过程如图2.41所示。
图2.41 switch语句的执行过程
说明:
(1)switch后面的“表达式”,可以是int、char和枚举型中的一种。
(2)每个case后面“常量表达式”的值,必须各不相同,否则会出现相互矛盾的现象(即对表达式的同一值,有两种或两种以上的执行方案)。
(3)case后面的常量表达式仅起语句标号作用,并不进行条件判断。因为系统一旦找到入口标号,就从此标号开始执行,不再进行标号判断,所以必须加上break语句,以便结束switch语句。
(4)在case后,允许有多个语句,可以不用{}括起来。
(5)各case和default子句的先后顺序可以变动,而不会影响程序执行结果。
(6)default子句可以省略不用。
【例2.26】
main() { int month; printf("请输入一个月份值(1-12)"); scanf("% d",&month); switch(month) /*根据month的当前取值,做出多分支选择*/ { case 1:printf("January\n");break; case 2:printf("February\n");break; case 3:printf("March\n");break; case 4:printf("April\n");break; case 5:printf("May\n");break; case 6:printf("June\n");break; case 7:printf("July\n");break; case 8:printf("August\n");break; case 9:printf("Septembert\n");break; case 10:printf("October\n");break; case 11:printf("November\n");break; case 12:printf("December\n");break; default:printf("Data Error!\n");break; } }
该程序的功能是从键盘输入一个月份(1~12),并显示该月份的英文名称。
3.随机函数和随机种子
1)sand()函数
功能:用于产生一个伪随机数。
例如:x+rand()%(y-x+1)可以产生x~y的随机数,其中x和y为整型数据。
2)srand()函数
功能:srand(seed)函数用于给rand()函数设定随机种子。
说明:
(1)rand()函数产生的是伪随机数,不是真正意义上的随机数,这个伪随机数是根据一个公式算出来的,每次运行程序,产生的伪随机数都一样。
(2)要想产生真正意义上的随机数,应将srand()和rand()函数配合使用,srand()函数用来设置随机数的种子,一般以时间作为种子。
(3)srand()和rand()函数应该组合使用。一般来说,srand()函数是对rand()函数进行设置。
例如:
srand(time(0)); //以系统时间作为随机种子
4.for循环结构
一般形式为:
for(表达式 1;表达式 2;表达式 3) 〈语句〉
该语句的执行过程是:
(1)先求解表达式1。
(2)求解表达式2,若其值为真(非0),则执行for语句中指定的内嵌语句,然后执行下面第(3)步;若其值为假(0),则结束循环,转到第(5)步。
图2.42 for循环执行过程
(3)求解表达式3。
(4)转回上面第(2)步继续执行。
(5)循环结束,执行for语句下面的一个语句。
for循环执行过程如图2.42所示。
for语句最简单的应用形式也是最容易理解的形式如下:
for(循环变量赋初值;循环条件;循环变量增量)语句
循环变量赋初值总是一个赋值语句,它用来给循环控制变量赋初值;循环条件是一个关系表达式,它决定什么时候退出循环;循环变量增量,定义循环控制变量每循环一次后按什么方式变化。这三个部分之间用“;”分开。
例如:
for(i=1;i<=50;i++)sum=sum+i;
先给i赋初值1,判断i是否小于等于100。若是,则执行语句,之后值增加1。再重新判断,直到条件为假,即i>50时,结束循环。
相当于:
i=1; while(i<=50) {sum=sum+i; i++; }
对于for循环中语句的一般形式,就是如下的while循环形式:
表达式1; while(表达式2) {语句 表达式3; }
说明:
(1)在for语句中,条件测试总是在循环开始时进行;如果循环体部分是由多个语句组成的,则必须用左、右花括号括起来,使其成为一个复合语句。
(2)在for语句中,表达式1通常是为循环变量赋初值的表达式;表达式2是控制循环的表达式,一般是关系表达式或逻辑表达式,但也可是数值表达式或字符表达式,只要其值是非0,就执行循环体;表达式3通常是改变循环变量值的表达式。表达式1和表达式3既可以是一个简单的表达式,也可以是逗号表达式。
例如:
for(sum=0;i<=50;i++)sum=sum+i; for(sum=0,i=1;i<=100;i++)sum=sum+i; for(i=0;(c=getchar())!='\n';i+=c); for(;(c=getchar())!='\n';) printf("%c",c);
(3)for语句的表示形式相当灵活,可以部分或全部省略,但“;”不能省略,如for(;;)。
例如:
for(;;)语句
相当于:
while(1)语句
即时训练
(1)随时产生1~7的整数,输出相应星期的英文表示。
(2)使用for循环打印九九表。
(3)优化“四则题库模块”的算法,大胆用自己的想法修改该模块的功能。
建议教学时以组为单位选出代表对本组优秀作品进行阐述并演示,最后选择出最佳设计1~2件,作为最后的四则题库算法。
拓展任务
设计体育彩票销售系统随机投注模块,添加顾客后,自动产生7位的投注号码,具体效果如图2.43和图2.44所示。
图2.43 随机投注界面
图2.44 产生投注号码
2.4.5 子任务五:评分系统模块的功能实现
任务描述
在主控模块界面设计的基础上,实现评分系统模块的功能。评分系统模块的主要功能是,实现学生进行题库训练后,显示当次训练的成绩清单,即训练的总题数、正确的题数和错误的题数,根据答题情况给出相应分数。该模块的目标是,让使用者了解自身对各题型掌握的情况,以便根据自己的实际情况选择训练的强度,从而提高四则运算的技能。
任务分析与设计
(1)基本流程如表2.33所示。
表2.33 评分系统模块的基本流程
图2.45 评分系统模块
(2)自然语言描述。
S1:定义整型变量——sum为题目总数量,tsum为正确题目总数量,fsum为错误题目总数量,num为是否继续操作的控制变量;定义字符型变量——choi为是否退出系统操作的控制变量。
S2:计算sum、tsum、fsum。
S3:输出评分系统的结果。
S4:输出显示当前答题总量、正确题目数量、错误题目数量和得分(tsum*100/sum)。
S5:输出是否“退出”的提示信息。
S6:用户输入选择。
S7:判断用户输入的信息。如果为1,则返回主控界面;如果为2,则执行S8,否则执行S5。
S8:输出是否“确认退出”的提示信息。
S9:判断用户输入的信息。如果为'Y'或'y',则退出系统;如果为'N'或'n',则返回主控界面,否则执行S8。
(3)设计流程图如图2.46所示。
图2.46 评分模块的设计流程图
任务实现
void gradeSystem();//评分系统函数的声明 void gradeSystem() //评分系统函数的定义 { int sum,num,tsum,fsum; char choi; sum=tnum1+fnum1+tnum2+fnum2+tnum3+fnum3+tnum4+fnum4; tsum=tnum1+tnum2+tnum3+tnum4; fsum=fnum1+fnum2+fnum3+fnum4; system("cls"); printf("\n\n"); printf("\t\t****************************************************\n\n"); printf("\t\t 评分系统 \n\n"); printf("\t\t****************************************************\n\n"); printf("\n\n"); if(sum==0) { printf("您还没有开始进行答题,按任意键回到主控界面"); getchar(); getchar(); system("cls"); showMainMenu(); } printf("\t\t\t本次训练总共完成 %d \n\n",sum); printf("\t\t\t正确:%d题 ",tsum); printf("错误:%d题 ",fsum); printf("成绩:%d分 ",tsum*100/sum); do { printf("\n\n\n\n\n\t\t\t===========================\n"); printf("\t\t\t 1.返回主控界面 2.退出\n"); printf("\t\t\t===========================\n"); printf("请选择(1或2):"); scanf("%d",&num); }while(num!=1&&num!=2); if(num==1) { system("cls"); showMainMenu(); } else { do { printf("\t\t确认要退出吗?(Y/N):"); scanf("%c",&choi); }while(choi!='Y'&&choi!='y'&&choi!='N'&&choi!='n'); if(choi=='Y'||choi=='y') exit(0);//退出 else { system("cls"); showMainMenu(); } } }
引导文献
1.条件运算符和条件表达式
在条件语句中,只执行单个的赋值语句时,可使用条件表达式来实现,不但使程序简洁,也提高了运行效率。条件运算符为?和:,它是一个三目运算符,即有三个参与运算的量。
1)一般形式
表达式1? 表达式2:表达式3
2)用法与运算规则
如果表达式1的值为真,则以表达式2的值作为条件表达式的值,否则以表达式2的值作为整个条件表达式的值。
例如条件语句
if(a>b) max=a; else max=b;
可用条件表达式写为:
max=(a>b)?a:b;
执行该语句的语义是:
如a>b为真,则把a赋予max,否则把b赋予max。
3)说明
(1)条件运算符的运算优先级低于关系运算符和算术运算符,但高于赋值符。因此,对于max=(a>b)?a:b可以去掉括号而写为max=a>b?a:b。
(2)条件运算符?和:是一对运算符,不能分开单独使用。
(3)条件运算符的结合方向是自右至左。
例如:
a>b?a:c>d?c:d
应理解为:
a>b?a:(c>d?c:d)
这也就是条件表达式嵌套的情形,即其中的表达式3又是一个条件表达式。
【例2.27】
//输入两个数,并将中较大者显示出来 main() { int a,b,max; printf("\n请输入两个数: "); scanf("%d%d",&a,&b); max=a>b?a:b; printf("max=%d",max); }
用条件表达式对上例重新编程,输出两个数中的大数。
2.自增(++)、自减(--)运算
1)作用
自增运算使单个变量的值增1,自减运算使单个变量的值减1。
2)用法与运算规则
(1)前置运算——运算符放在变量之前:++变量、--变量。
先使变量的值增(或减)1,然后再以变化后的值参与其他用运算,即先增减、后运算。(2)后置运算——运算符放在变量之后:变量++、变量--。
变量先参与其他用运算,然后再使变量的值增(或减)1,即先运算、后增减。
【例2.28】
//自增、自减运算符的用法与运算规则示例 main() {int x=6,y; printf("x=%d\n",x); y=++x; printf("y=++x:x=%d,y=%d\n",x,y); y=x--; printf("y=x--:x=%d,y=%d\n",x,y); }
程序运行结果:
x=6 y=++x:x=7,y=7 y=x--:x=6,y=7
3)说明
(1)自增、自减运算,常用于循环语句中,使循环控制变量加(或减)1,以及指针变量中,使指针指向下(或上)一个地址。
(2)自增、自减运算符,不能用于常量和表达式。
例如,9++、--(x+y)等都是非法的。
(3)在表达式中,因为连续使同一变量进行自增或自减运算时,很容易出错,所以最好避免这种用法。
3.逗号运算(,)及其表达式
C语言提供一种用逗号运算符“,”连接起来的式子,称为逗号表达式。逗号运算符又称顺序求值运算符。
1)一般形式
表达式1, 表达式2,…, 表达式n
2)用法与运算规则
自左至右,依次计算各表达式的值,“表达式n”的值即为整个逗号表达式的值。
例如,逗号表达式“a=2*5,a*3”的值等于30的求解过程是:
先求解a=2*5,得a=10;再求a*3=30,逗号表达式的值等于30。
再例如,逗号表达式“(a=2*5,a*3),a+5”的值等于15的求解过程是:
先求解a=2*5,得a=10;再求a*3=30;最后求解a+5=15,逗号表达式的值等于15。
3)说明
并不是任何地方出现的逗号,都是逗号运算符。要分析是它是作用是用做分隔符还是运算符。
4.If语句嵌套
当if语句中的执行语句又是if语句时,就构成了if语句嵌套。共有以下三种嵌套形式。
(1)嵌套形式1为:
if(表达式1) if(表达式2)语句 1; else 语句 2; else 语句 3;
该语句的执行过程是:执行流程如图2.47所示,在if-else语句中包含另一个if-else语句,即第一个else与第二个if结合,而最后一个else与第一个if结合。
图2.47 嵌套形式1的执行流程
(2)嵌套形式2为:
if(表达式 1) {if(表达式2)语句 1;} else语句 2;
该语句的执行过程是:执行流程如图2.48所示,在if-else语句中包含一个单分支结构复合语句,即else与第一个if结合。因为第二个if在复合语句中,复合语句是一条语句,不能与复合语句外的else结合。如果把{}去掉,则else与第二个if结合。
图2.48 嵌套形式2的执行流程
(3)嵌套形式3为:
if(表达式 1)语句 1; else if(表达式2)语句2; else语句 3;
该语句的执行过程是:执行流程如图2.49所示,在if-else语句的else后紧跟另一个if-else语句。C语言规定:else总是与它前面最近的同一复合语句内的不带else的if结合。在if语句嵌套形式2中可以看到,else与if在同一复合语句内才能结合。
图2.49 嵌套形式3的执行流程
【例2.29】
//比较两个数的大小关系示例 main(){ int A,B; printf("请输入A,B:"); scanf("%d%d",&A,&B); if(A==B)printf("A=B\n"); else if(A>B) printf("A>B\n"); else printf("A<B\n"); }
本例中采用了if语句的嵌套结构。采用嵌套结构实质上是为了进行多分支选择,实际上有三种选择,即A>B、A<B或A=B。这种问题用if-else-if语句也可以完成,而且程序更加清晰。因此,在一般情况下较少使用if语句的嵌套结构,以使程序更便于阅读理解。
【例2.30】
//判断男士与女士因退休标准不同而采用的嵌套分支结构示例 #include<stdio.h> main() { char sex; int age; printf("请输入性别"); printf("(男士用M,女士用F):"); scanf("%c",&sex); printf("请输入年龄:"); scanf("%d",&age); if(sex=='M'||sex=='m')/*是男士*/ { if(age>=60) { printf("已退休\n"); } else { printf("在工作\n"); } } else/*是女士*/ { if(age>=55) { printf("已退休\n"); } else { printf("在工作\n"); } } }
即时训练
(1)编程实现任意输入三个整数,将其按从小到大的顺序输出。
(2)编程实现输入一个4位的整数,将其反向输出。
(3)求解方程ax2+bx+c=0。
(4)学校进行成绩分级管理,取消分数制,改为成绩分级评定。
具体办法是:90分(含90)以上为A类,80(含80)~90分为B类,70(含70)~80分为C类,60(含60)~70分为D类,60分以下为E类。设计一个程序,对输入的成绩进行等级划分。
(5)自行设计“评分系统模块”,假设你是一名小学生,你最希望在进行了百题训练后想看到的评价,并将其设计出来。
拓展任务
对“体育彩票销售系统”的投注总数量、自行投注数量和随机投注数量进行统计。