slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
课程教学内容 第 1 章 C 语言概述 第 2 章 C 语言程序基础 第 3 章 程序控制结构 第 4 章 数组 第 5 章 函数(递归难) 第 6 章 复杂数据类型 PowerPoint Presentation
Download Presentation
课程教学内容 第 1 章 C 语言概述 第 2 章 C 语言程序基础 第 3 章 程序控制结构 第 4 章 数组 第 5 章 函数(递归难) 第 6 章 复杂数据类型

Loading in 2 Seconds...

play fullscreen
1 / 252

课程教学内容 第 1 章 C 语言概述 第 2 章 C 语言程序基础 第 3 章 程序控制结构 第 4 章 数组 第 5 章 函数(递归难) 第 6 章 复杂数据类型 - PowerPoint PPT Presentation


  • 111 Views
  • Uploaded on

课程教学内容 第 1 章 C 语言概述 第 2 章 C 语言程序基础 第 3 章 程序控制结构 第 4 章 数组 第 5 章 函数(递归难) 第 6 章 复杂数据类型 第 7 章 链表和树 (难). 第一章 C 语言概述 本章重点 ①程序与算法的关系及算法描述工具 ②编写 C 程序的过程 ③ C 程序的基本结构和组成 ④ C 程序的编程风格. 1 . 1 初步认识简单的 C 程序 1 . 1 . 1 实例 【 例 1.1】 下列程序的功能是什么? #define PI 3.14159 // 编译预处理命令

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '课程教学内容 第 1 章 C 语言概述 第 2 章 C 语言程序基础 第 3 章 程序控制结构 第 4 章 数组 第 5 章 函数(递归难) 第 6 章 复杂数据类型' - virote


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
slide1
课程教学内容
  • 第1章 C语言概述
  • 第2章 C语言程序基础
  • 第3章 程序控制结构
  • 第4章 数组
  • 第5章 函数(递归难)
  • 第6章 复杂数据类型
  • 第7章 链表和树 (难)
slide2
第一章 C语言概述
  • 本章重点
  • ①程序与算法的关系及算法描述工具
  • ②编写C程序的过程
  • ③ C程序的基本结构和组成
  • ④ C程序的编程风格
slide3
1.1初步认识简单的C程序
  • 1.1.1 实例
  • 【例1.1】下列程序的功能是什么?
  • #define PI 3.14159 // 编译预处理命令
  • #include "stdio.h" // 编译预处理命令
  • void main() //主函数
  • { double r,c,s; //变量类型定义
  • printf("请输入圆的半径:");//输入提示
  • scanf("%lf",&r); //输入圆的半径
  • c=2*PI*r; //计算?
  • s=PI*r*r; //计算?
  • printf(“圆的周长是:%f\n”,c); //输出?
  • printf(“圆的面积是:%f\n”,s); //输出?
  • }
slide5
【例1.2】下列程序的功能是什么?
  • #include "stdio.h" /* 编译预处理命令*/
  • int min(int m,int n)/*自定义函数*/
  • { int z;/*定义变量*/
  • if(m>n) z=n;/*分支结构*/
  • else z=m;
  • return(z);/*返回值*/
  • }
  • void main() //主函数
  • { int x,y; //定义变量
  • scanf("%d,%d",&x,&y);//输入两个整数
  • y=min(x,y);//调用max函数后返回最大值
  • printf("min=%d\n", y);//输出最大值
  • }
slide6
1.1.2 C程序的构成
  • (1)一个C程序是由一个或多个函数构成,但有且仅有一个主函数。因此,函数是C程序的基本语法单位。每个函数中又可调用系统中提供的库函数,也可以是用户自己定义的函数。这一特点,使得C程序更容易实现模块化。因此一个简单C程序的基本结构为:

预编译命令

若干函数

slide7
(2) 一个函数由两部分组成。即函数的首部和函数体,
  • 如主函数的首部为 :void main( )
  • 而自定义函数的首部为:int min(int m,int n )
  • 函数体包括声明部分和执行部分,用大括号{ }括起来构成一
  • 个整体。声明部分是用来定义程序中所用到的变量和对所调用函数
  • 的声明。
  • 变量的定义是对数据的描述。
  • 如:int z; 定义一个整型变量z。
  • void main() //主函数
  • { int min(int m,int n) ;//函数声明
  • int x,y; //定义变量
  • scanf("%d,%d",&x,&y);//输入两个整数
  • y=min(x,y);//调用max函数后返回最大值
  • printf("min=%d\n", y);//输出最大值
  • }
  • 执行部分是对操作的描述,也就是算法的描述。
slide8
(3) C程序书写格式自由,一行内可以写几个语句, 一个语句可以分写在多行上。
  • (4) C程序总是从main函数开始执行的,与main函数的位置无关。在【例1.2】中也可将自定义函数min放在主函数main之后,此时应该在主函数的声明部分增加函数的原型声明。
  • (5) 每个语句和数据声明的最后必须有一个分号。
slide9
1.1.3算术表达式、赋值表达式、关系表达式及表达式语句的初步认识1.1.3算术表达式、赋值表达式、关系表达式及表达式语句的初步认识
  • 由+(加)、—(减)、*(乘)、/(除)、 %(求余)等算术运算符与运算对象组成的表达式,称为算术表达式,这些运算符的优先级次序为:优先级最高是*、%、/,然后是+、-.如: 2/5的值为0,2.0/5的值为0.4,2%5的值是2,3%5*2-2/5+2.0/5的值是6.4。
  • 由赋值符号(如“=”)构成的表达式,称为赋值表达式。如赋值表达式x=(-b+sqrt(b*b-4*a*c))/(2*a)的作用是将赋值“=”右边表达式的值赋给x。
  • 由大于(>)、小于(<)、大于等于(>=)、小于等于( <=)、 等于(==)和不等(!=)等关系运算符与运算对象组成的式子,称为关系表达式。其中>、<、>=、<=优先级高于==、!=。如x>=3表示x大于或等于3,若x的值为21,则结果为真(用1表示)。
slide10
1.1.4 输入输出函数的初步认识
  • 格式输入函数sanf的一般形式:

scanf(“格式控制”, 输出表列);

①格式说明符:以%开头;

②普通字符(含转义字符):原样输入。

用“,”间隔变量地址表

例如:scanf("%d,%d",&x,&y);

slide11

printf(“格式控制”, 输出表列);

①格式说明符:以%开头;

②普通字符(含转义字符):原样输出。

用“,”间隔 表达式、常量和变量

  • 格式输出函数printf的一般形式:

转义字符见2.2表2-3。

例如:printf("min=%d\n", y);

slide12
1.1.5预处理命令的初步认识
  • 在C语言中,将程序的编译过程分为预处理和正式编译两个阶段。预处理阶段的任务是把C程序中的以“#”开头的命令按照各自的功能进行处理。即用#include命令中头文件的实际代码替代该命令,用#define命令所指定的字符串替代程序中所有符号常量,经过预处理之后的C程序不再含有“#”开头的命令。正式编译阶段的任务是对经过预处理之后的代码进行语法和词法分析、生成中间代码、优化代码,最终生成目标代码程序。
slide13
1.1.6变量与数据类型的初步认识
  • 在程序执行过程中,其值可以改变的量称为变量。为区别不同的变量,每个变量都有名字,这个名字叫变量名。变量名是按照标识符的命名规则取名。
  • 所谓标识符就是用来对变量、宏名、函数、数组等数据对象命名的有效字符序列。C语言的标识符必须是以字母或下划线开头,由字母、数字和下划线组成的字符序列。
  • 例如:sum,_total, month, Student_name,lotus_1_2_3,BASIC, li_ling是正确的标识符,
  • 而M.D.John, $123,3D64,a>b是不正确的标识符。
slide14
在C程序中所有的变量都必须遵循“先定义后使用”的原则,即任何变量都必须属于某数据类型,其中作用是为程序中的变量分配存储单元,并决定数据的存放形式。在C程序中所有的变量都必须遵循“先定义后使用”的原则,即任何变量都必须属于某数据类型,其中作用是为程序中的变量分配存储单元,并决定数据的存放形式。
  • 变量定义的一般格式为:
  • 类型名称 变量名列表;
slide15

选择描述方法

利用C语言描述算法中的操作

分析问题

构建算法

描述算法

根据C程序结构完整C程序代码

编写C程序步骤

  • 1.2 初写简单的C程序
  • 1.2.1编写一个简单C程序的步骤
  • 通过对C程序的模仿、改写、调试,也许能够逐步读懂简单C程序,在此基础上,可按照如图所示的步骤来编写C程序。
slide16
1.2.2简单C程序编写实例
  • 【例1.3】若含Fe2O3 75%的赤铁矿石为x吨,可炼出含杂质4%的生铁为y吨,编写程序完成其功能。
  • 〖问题分析〗
  • 由题意:x吨赤铁矿石中含纯Fe2O3的质量为:x吨×75%
  • 根据化学方程式:
  • 高温
  • Fe2O3+3CO==== 2Fe+3CO2
  • 160112
  • x吨×75%(1-4%)y
  • 由此可得到方程式:160*(1-4%)*y=112*x*75%
slide18
〖算法描述〗
  • 算法描述方法很多,常用的有自然语言、流程图、N-S图等描述方法。例1.3的算法可描述为:
  • 〖描述方法1〗用自然语言表示
  • 步骤1:输入含Fe2O3 75%的赤铁矿石的质量x。
  • 步骤2:按照公式计算y的值。
  • 步骤3:输出可炼出含杂质4%的生铁质量y的值。
slide19

开始

输入x

根据公式计算y

输出y

结束

流程图

  • 〖描述方法2〗 用流程图表示如图所示。
slide21
1.2.3 算法及描述方法
  • 1.算法的概念及特征
  • 要编写一个问题的程序,必须首先构建该问题的算法。所谓算法(Algorithm)就是对解决某问题所采用的方法和步骤的描述。
slide22
【例1.6】输入3个数,找出其中的最大值并输出。【例1.6】输入3个数,找出其中的最大值并输出。
  • 分析:假如这三个数分别用a,b,c表示,其中的最大值用max表示。由于计算机一次只能比较两个数,我们首先可以将a和b进行比较,把其中大的数放入max中,再将max与c比较,又将大的数放入max中。最后将max输出,此时max中的值就是a,b,c中的最大值。由此可以将求解这一问题的算法描述为:
  • 第1步:输入a,b,c。
  • 第2步:a,b中大的一个数放入max中。
  • 第3步:将c与max中大的数放入max中。
  • 第4步:输出max。
slide23

开始

输入a,b,c

输入a,b,c

将a,b中大的数放在ma中

将c,max中大的数放在max

输出max

将a,b中大的数放在max中

将c,max中大的数放在max中

输出max

(b)

结束

(a)

图1.18 求三个数最大值的算法流程图

slide24
2.三种基本结构的表示
  • 1966年,Bohra和Jacopini提出了程序的三种基本结构:顺序结构、选择(或分支)结构、循环结构,它们构成了实现一个算法的基本单元。
slide25
顺序结构
  • 顺序结构是一种最基本、最简单的程序结构。如图所示,先执行A,再执行B,A与B是按照顺序执行。

A

B

A

B

(b)N-S图

(a)流程图

slide26

N

Y

P

T F

A B

条件p

A

B

  • 选择(或分支)结构
  • 根据条件是否成立而去执行不同的程序模块。在如图中,当条件P为真时,执行A,否则执行B,即要么执行A,要么执行B。

(b)N-S图

(a)流程图

slide27

条件P

A

  • 循环结构
  • 循环结构是指重复执行某些操作,重复执行的部分称为循环体。如图所示为当型循环结构,当条件P为真时,反复执行A,直到条件P为假时才终止循环。其中A就是循环体,A被重复执行的次数称为循环次数。循环结构除当型循环外,还有直到型循环。

N

条件P

Y

A

(a)流程图

(b)N-S图

slide28
1.3 C语言的编程风格
  • 1.确保应用程序与用户交互性
  • 程序应该给予相应的提示信息
  • 输出数据时,应该让用户知道输出数据的含义
  • 2.确保程序可读性
  • 格式(如每行最好写一个语句)、注释
  • 3.确保程序结构清晰
  • 清晰的程序结构可通过多层次缩进、大括号的位置对齐、程序的模块化等来体现。大括号的位置有两种。
  • 作业:
  • 1.完成所有课后作业
  • 2.实践教材习题
slide29
第2章 C语言程序基础
  • 本章重点
  • ① 常量与变量的概念
  • ② C程序数据类型
  • ③ 运算符和表达式
  • ④ 数据类型的转换
  • ⑤ 数据的输入输出
  • ⑥ 预处理
  • C语言中将数据类型分为基本数据类型、指针数据类型和复杂数据类型等。
slide30
2.1 基本数据类型
  • 【例2.1】比较基本数据类型之间的异同。
  • 〖编写程序〗
  • #include"stdio.h"
  • void main()
  • {
  • int x=10;
  • int y=0x10;
  • short int z=010;
  • long a=-1u;
  • char c='A';
  • float r=1.5111111;
  • double s=1.5111111;
  • printf("测试基本类型所占空间数(字节):\n");
  • printf("%10s%10s%10s%10s%10s%10s\n","int","short","long","float","double","char");
  • //输出表头
  • printf("%10d%10d%10d%10d%10d%10d\n", sizeof(int),sizeof(short),sizeof(long),sizeof(float),sizeof(double),sizeof(char));
slide31
printf("%10s%10s%10s%10s%10s%10s\n","进制数","x","y","z","a","c");//输出表头printf("%10s%10s%10s%10s%10s%10s\n","进制数","x","y","z","a","c");//输出表头
  • printf("%10s%10d%10d%10d%10d%10d\n","十进制",x,y,z,a,c);
  • printf("%10s%10o%10o%10o%10o%10o\n","八进制",x,y,z,a,c);
  • printf("%10s%10x%10x%10x%10x%10x\n","十六进制",x,y,z,a,c);
  • printf("r=%4.2f\n",r);
  • printf("r=%20.16f\n",r);
  • printf("s=%4.2lf\n",s);
  • printf("s=%20.16lf\n",s);
  • }
slide33
2.2常量和变量
  • 据数据在程序执行过程中值是否改变,将数据分为常量和变量。在程序执行过程中,值不能改变的量称为常量,值可以改变的量称为变量。
slide37
变量占用的内存空间的第一个字节的地址就是变量的地址。如图所示,整型变量x,它占用了4个字节的存储空间,它的第一个字节的地址为1001,则变量x的地址为1001。变量占用的内存空间的第一个字节的地址就是变量的地址。如图所示,整型变量x,它占用了4个字节的存储空间,它的第一个字节的地址为1001,则变量x的地址为1001。

int x=10

x的地址为1001

10

slide38
2.3 运算符和表达式
  • C语言中的运算符和表达式非常丰富,运算符包括算术运算符、赋值运算符、条件运算符、逗号运算符和位运算符等,由运算符将运算对象(操作数)连接起来的式子就称为表达式。常量、变量和函数都可看作是最简单的表达式。
slide39
2.3.1 算术运算符
  • 【例2.2】将组成一个三位正整数的百位、十位、个位数字按逆序输出。
  • 〖问题分析〗
  • 根据题意,只要将一个三位正整数进行分解,得到百位、十位、个位数字后,即可实现逆序输出。
slide40
〖编写程序〗
  • #include <stdio.h>
  • void main( )
  • {
  • int bai,shi,ge,a;
  • printf("Please input an integer(3):");
  • scanf("%d",&a); // 输入一个3位正整数
  • bai=a/100; // 分解百位数字
  • shi=(a%100)/10; // 分解十位数字
  • ge=a%10; // 分解个位数字
  • printf("\n%d %d %d\n",ge,shi,bai); // 输出结果
  • }
slide43
2.3.2 关系运算符

关系运算符及其含义

由关系运算符将两个运算对象连接起来的表达式称为关系表达式。

关系表达式的运算结果是一个逻辑值。通常用1表示逻辑“真”,用0表示逻辑“假”。

slide44
2.3.3 逻辑运算符
  • && 逻辑“与”运算符,当两个运算对象都是非0时,运算结果为“真”。
  • || 逻辑“或”运算符,当两个运算对象都是0时,运算结果为“假”。
  • ! 逻辑“非”运算符,当运算对象为0时,运算结果为“真”。
slide45
2.3.4 赋值运算符
  • C语言中将符号“=”称为赋值运算符,其左边必须是变量。赋值运算符分为简单赋值运算符和复合赋值运算符。
  • 1.简单赋值运算符
  • 一般形式为:
  • 变量名=表达式
  • 其功能是将 “=”右边表达式的值赋予左边的变量。
  • 2.复合赋值运算符
  • 在赋值运算符“=”左边加上其他二目算术运算符或位运算符构成复合赋值运算符。例如,x+=8;表示将变量x的值加8后再赋予变量x,即等效于x=x+8。
slide46
2.3.5条件运算符
  • 条件表达式的一般形式为:
  • 表达式1?表达式2:表达式3
  • 其功能是先计算表达式1的值,若值为真(非0),则将表达式2的值作为条件表达式的值;否则将表达式3的值作为条件表达式的值。
  • 例如,min=(a<b)?a:b; 其功能是判断a<b是否成立,如果成立,将变量a的值赋予变量min;如果不成立,则将变量b的值赋予变量min。
slide47
2.3.6 逗号运算符
  • C语言中的符号“,”是一个特殊的符号,既可作为分隔符,也可以作为逗号运算符。在变量的说明定义和函数的参数中,作为分隔符。“,”作为逗号运算符时,是一个双目运算符,逗号表达式的一般形式为:
  • 表达式1,表达式2,…,表达式n
  • 其功能是从左至右依次计算表达式1的值,表达式2 的值…,最后计算表达式n的值,并将表达式n的值作为逗号表达式的值。
  • x=(a=1),(b=2),(a+b) ; 执行该语句后,变量x的值为3。
slide48
2.3.7 位运算与位表达式
  • 1.位运算符 C语言中常用的位运算符见表所示。

(1)除“~”运算符是单目运算符外,其余位运算符均为双目运算符。

(2)如果双目位运算的运算对象的位数不同,系统将自动将其右对齐,不足的位上用符号位补齐,即正数用“0”补齐,负数用“1”补齐。

slide49
2.按位“与”运算
  • 【例2.3】将整数a的高八位清0,保留低八位。
  • 〖问题分析〗
  • 根据按位“与”运算的运算规则:任何值“与”1结果还是其本身,任何值“与”0结果为0。根据题意,只要将变量a“与”0000000011111111(127),即可将a的高八位清0,保留低八位。
slide50
〖编写程序〗
  • #include <stdio.h>
  • void main( )
  • {
  • int a;
  • printf("Please input a:");
  • scanf("%d",&a);
  • a=a&127;
  • printf("%d\n",a);
  • }
slide51
3.按位“或”运算
  • 【例2.4】将整数a的高八位不变,低八位置1。
  • 4.按位“异或”运算
  • 按位“异或”运算(^)将运算对象所对应的二进制位进行逻辑“异或”。通常用按位“异或”运算实现对运算对象的某些位求反。
  • 【例2.5】将整数26的高四位不变,低四位求反。
  • 〖问题分析〗
  • 根据按位“异或”运算的运算规则:任何值“异或”0结果还是其本身,任何值“异或”1结果取反。根据题意,只要将整数26“异或”00001111(15),即可将26的高四位不变,低四位取反。
slide52
5. 按位取反运算
  • 6.左移运算
  • 左移运算(<<)将运算对象所对应的二进制位全部左移“<<”运算符右边的数所指定的位数,移出的高位丢掉,空出的低位补0。
  • 例如:a=01100011;a<<4;
  • 执行左移后a的值为00110000。
  • 7.右移运算
  • 右移运算(>>)将运算对象所对应的二进制位全部右移“>>”运算符右边的数所指定的位数,移出的低位丢掉,空出的高位补0(无符号数和正整数)或补1(负整数)。
  • 例如:a=01100011;a>>2;右移后a的值为00011000。
  • a=11110101;a>>2;右移后a的值为11111101。
slide53
2.3.8 运算符的优先级
  • 一般情况下,单目运算符的优先级高于其他运算符。算术运算符的优先级较高,关系运算符、逻辑运算符和条件运算符的优先级依次降低,赋值运算符的优先级最低。
slide54
2.4 类型转换

在C语言中,不同数据类型的数据可以混合运算,但需要进行类型转换,将不同类型的数据转换成相同类型后再进行运算。这种类型转换分为隐式类型转换和强制类型转换。

slide55
2.4.1 隐式类型转换
  • # include <stdio.h>
  • void main( )
  • { int a = 5;
  • char c = 'a';
  • float f = 5.3;
  • double m = 12.65;
  • printf("a + c = %d\n", a + c);
  • printf("a + c = %c\n", a + c);
  • printf("f + m = %f\n", f + m);
  • a=m;
  • printf("a = %d\n", a );
  • printf("c + f = %f\n", c + f);
  • }
slide56

double

float

long

Unsigned long

int

char, short

  • 隐式数据类型转换,一般将短类型数据转换为长类型,遵循的转换规则如图所示。
slide57
2.4.2强制类型转换
  • 一般形式为:
  • (数据类型名) 表达式
  • 例如,(int)2.56将2.56强制转换为整数2。
  • C语言中的类型转换都是为运算而进行,因此,无论隐式类型转换和强制类型转换,其作用的范围只局限于本次转换,而不会影响源数据的类型。
slide58
2.5 指针数据类型

【例2.7】用指针输出变量a的值。

〖编写程序〗

#include "stdio.h"

void main()

{

int a=3;

int *p;

p=&a;

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

printf("*p=%d\n",*p);

}

slide59
地址和指针
  • 计算机中的数据都被存放在内存单元中,不同的数据类型在内存中所占用的单元数是不同的。为了标识不同的内存单元,每一个内存单元都有一个编号,称为“地址”,如例2.9变量a的地址为&a。根据地址就可访问到所需的数据,因此形象的将这个地址称为指针。

a的值为3

a的地址为1001

a

3

p的值为1001

a的地址为2008

p

slide60
2.指针变量
  • (1)指针变量的定义
  • 一般形式为:变量类型名 *指针变量名;
  • 例如:
  • int *p; //定义指针变量p指向整型变量
  • float *p,*q; //定义指针变量p和q均指向浮点型变量
  • (2)指针变量的运算
  • C语言中提供了指针变量的取地址运算和间接访问运算。符号“&”是取地址运算符,符号“*”是间接访问运算符,“*” 通常也被称为指针运算符。
  • 例2.7中,指针变量p的值为变量a的地址值,即&a,*p为p指向的地址空间中的值,即变量a的值为3。
slide61
2.6 数据的输入与输出
  • 在使用标准输入输出库函数时在源文件开头使用预编译命令:
  • #include< stdio.h >或#include ”stdio.h”
  • 2.6.1 输入输出函数
  • 1. 格式输入函数
  • scanf 函数的一般形式为:
  • scanf(参数1,参数2,参数3,……,参数n)
  • 其中参数1——格式控制;
slide62
参数2,参数3,……,参数n——地址表列。
  • 例如
  • char a,b;
  • scanf("%c%c",&a,&b);
  • 格式控制是用双引号括起来的字符串,称“转换控制字符串”。
  • %c—表示输入单个字符;
  • 地址表列是由若干个地址组成的表列。&a表示变量a的地址,&b表示变量b的地址,语句“scanf("%c%c",&a,&b);” 表示将从键盘输入的两个字符分别按照变量a和b的地址接收存入变量a,b中。
  • %d—输入有符号的十进制整数;
  • %u—输入无符号的十进制整数;
  • %f—输入实数;
  • %s—输入字符串,将该字符串存入一个字符数组中。
slide63
2. 格式输出函数
  • printf 函数的一般形式为:
  • printf(参数1,参数2,参数3,……,参数n)
  • 其中参数1——格式控制;
  • 参数2,参数3,……,参数n——输出表列。
  • 如: float a;
  • int b;
  • a = 85.56;
  • b = 100;
  • printf("%f %d", a, b);
  • %f─第一个输出表列的格式说明,以小数形式输出单、双精度实数,隐含输出6位小数;
  • %d─第二个输出表列的格式说明,以带有符号的十进制形式输出整数(正数不输出符号);显然,%f控制实数变量a的输出格式;%d控制整数变量b的输出格式。
slide64
为了醒目,格式控制项中除格式说明之外可以有普通字符。为了醒目,格式控制项中除格式说明之外可以有普通字符。
  • 如: printf("a=%f b=%d", a, b); // 加提示符以及空格
  • 输出结果为:
  • a=85.560000 b=100
  • 如: printf("a=%f\nb=%d",a,b); // 加换行符
  • 输出结果为:
  • a=85.560000
  • b=100
  • 如:printf("%5.3f",a); // 加域宽和精度
  • 输出结果为:
  • a=85.560
slide65
3. 字符输入输出函数
  • getchar()函数和putchar()函数是C语言标准函数中的字符输入函数和输出函数。getchar()函数的功能是从键盘输入一个字符,putchar()函数的功能是向终端输出一个字符。
slide66
2.6.2 文件输入输出
  • 前面所讲的输入输出函数用于实现输入输出数据量不大的情况,当输入输出数据量很大或输入输出频率很高时,可通过C语言提供的文件来实现输入输出。
  • 【例2.10】将文本“科学技术是第一生产力!”保存到文件f.txt中。
slide67
〖编写程序〗
  • # include <stdio.h> // 预编译命令,将标准输入输出函数
  • # include <stdlib.h>
  • void main( ) {
  • FILE *fp; // 定义文件指针
  • if((fp=fopen(“f.txt”,”w”))==NULL) // 以“写”的方式打开文件
  • {
  • printf(“Error on open f.txt!\n”);
  • exit(0);
  • }
  • fprintf(fp,”%s”,” 科学技术是第一生产力!\n”); // 将内容写入文件
  • if(fclose(fp)) // 关闭文件
  • {
  • printf(“Can not close f.txt and strike any key exit!\n”);
  • getch();
  • exit(0);
  • }
  • }
slide68
2.7 预处理
  • 2.7.1 不带参宏定义
  • # define PI 3.1415926
slide69
2.7.2 带参宏定义
  • 【例2.13】
  • #include <stdio.h>
  • #define MUL(x,y) (x)*(y)
  • void main( )
  • {
  • int a,b,m,n;
  • printf("Please input a and b:");
  • scanf("%d%d",&a,&b);
  • printf("%d\n", MUL(a,b));
  • printf("Please input m and n:");
  • scanf("%d%d",&m,&n);
  • printf("%d\n",MUL(a+m,b+n));
  • }

考虑若将#define MUL(x,y) (x)*(y)更改为

#define MUL(x,y) x*y,结果如何?

slide70
2.7.3 文件包含
  • 文件包含旨在将指定文件插入文件包含命令行位置,取代该命令行,从而把指定文件和当前源程序文件连成一个源文件。
  • 文件包含命令的一般形式为:
  • #include <文件名>或 #include "文件名“
  • 作业:完成所有课后作业及实践教材习题
slide71
第三章 程序控制结构
  • 本章重点
  • ① if语句和switch语句的使用方法
  • ② 分支结构的算法描述
  • ③ 分支语句的嵌套应用
  • ④ while语句、do while语句和for语句的使用方法
  • ⑤ 循环结构的算法描述
  • ⑥ 三种循环语句的关系
  • ⑦ 循环语句的嵌套应用
slide72
3.1.1 单分支if语句应用实例
  • 【例3.1】宇宙速度是物体从星球出发,在天体的重力场中运动,四个较有代表性的初始速度的统称。 航天器按其任务的不同,需要达到这四个宇宙速度的其中一个。地球的第一宇宙速度(又称环绕速度)为7.9km/s,是指物体紧贴地球表面作圆周运动的速度;第二宇宙速度(又称逃逸速度)为11.2km/s,是指物体完全摆脱地球引力束缚,飞离地球所需要的最小初始速度;第三宇宙速度为16.7km/s,是指在地球上发射的物体摆脱太阳引力束缚,飞出太阳系所需的最小初始速度。已知这些速度均与重力加速度和星球半径成正比,火星的质量和半径分别为地球的1/10和1/2,请编程建立菜单,由用户选择要计算的速度并将速度显示在屏幕上。
slide74
〖编写程序〗
  • #include<stdio.h>
  • void main()
  • { int a;
  • float v;
  • printf("请选择\n");
  • printf("1:火星第一宇宙速度\n");
  • printf("2:火星第二宇宙速度\n");
  • printf("3:火星第三宇宙速度\n");
  • scanf("%d",&a);
  • if(a==1)
  • v=7.9/10/2;
  • if(a==2)
  • v=11.2/10/2;
  • if(a==3)
  • v=16.7/10/2;
  • printf("火星第%d宇宙速度为%fkm/s。\n",a,v);
  • }
slide75
3.1.2 if语句的形式
  • if 语句的一般形式如下:
  • if(表达式)
  • 语句组1
  • else
  • 语句组2
  • “if”和“else”是关键字。当表达式结果为“真”(即等于1)时,执行语句组1,表达式结果为“假”(即等于0)时,执行语句组2。在语句组1和语句组2中只能选择执行一组,而后执行整个 if 语句后面的语句。
slide76
3.1.3 switch语句应用实例
  • 在实际问题中经常遇到学生成绩分类、工资统计分类、菜单选择等操作,这些问题都是多分支情况,这类问题如果用C语言编程序来实现,一般使用多分支选择语句switch语句。
  • 【例3.3】编写程序实现:已知两个实数,在如下菜单中选择一个运算类型,并进行相应的运算,显示运算结果。例如选择加法,就进行求和运算;选择乘法,就进行求积运算。
  • Please choose
  • +: addition
  • - : subtraction
  • *: multiplication
  • /: division
slide77
〖问题分析〗将已知的两个实数分别赋值给变量a, b, 从键盘接收选择的运算符,然后根据不同的运算符进行计算,将计算结果赋给变量c,最后输出计算的结果。如果输入的字符不是这四个运算符中的一种,则要提示错误。
slide79
〖编写程序〗
  • #include <stdio.h>
  • void main( )
  • {
  • float a=5.0,b=2.0,c=0.0;
  • char sym='\0'; /* 初始化变量sym为空格*/
  • printf("Please choose\n");
  • printf("+ : addition\n");
  • printf("- : subtraction\n");
  • printf("* : multiplication\n");
  • printf("/ : division\n");
  • sym=getchar();
  • printf("%f%c%f=",a,sym,b); /* 显示算式 */
slide80
switch(sym) /* 计算算式 */
  • {
  • case '+': c=a+b; break;
  • case '-': c=a-b; break;
  • case '*': c=a*b; break;
  • case '/': c=a/b; break;
  • default:printf("Error!");
  • }
  • printf("%f\n",c); /* 显示结果 */
  • }
slide81
3.1.4 switch语句的形式
  • switch语句的一般形式为:
  • switch(表达式)
  • {
  • case 常量表达式1: 语句组1;
  • case 常量表达式2: 语句组2;
  • … …
  • case 常量表达式n: 语句组n;
  • default: 语句组n+1;
  • }
slide82
3.1.5 分支嵌套语句
  • if 语句的语句组1或语句组2中又包含另一个或多个 if 语句称为 if 语句的嵌套。 嵌套if语句的一般形式为:
  • if(表达式)
  • if (表达式)
  • 语句;
  • else
  • 语句;
  • else
  • 语句;
slide83
【例3.5】上体育课时,老师要将本班的55位学生排为四列,采用的方法是按学号(1~55)“1,2,3,4”报数。编写程序实现:输入本班任一位学生的学号,显示该学生应排在第几列。【例3.5】上体育课时,老师要将本班的55位学生排为四列,采用的方法是按学号(1~55)“1,2,3,4”报数。编写程序实现:输入本班任一位学生的学号,显示该学生应排在第几列。
  • 〖问题分析〗
  • 根据题意,首先须判断输入的学号是否有效,在学号有效的情况下,将学号模4运算,根据不同的计算结果分四种情况进行处理,显然既需要用到if语句,也需要用到switch语句。
slide85
〖编写程序〗
  • #include <stdio.h>
  • void main()
  • {
  • int i,n;
  • i=0;
  • printf("Please input the number:");
  • scanf("%d",&n);
  • if ((n<1)||(n>55))
  • printf("Error!");
  • else
slide86
{
  • i=n%4;
  • switch(i)
  • {
  • case 1:printf("The first column !\n");break;
  • case 2:printf("The second column !\n");break;
  • case 3:printf("The third column !\n");break;
  • case 0:printf("The forth column !\n");break;
  • }
  • }
  • }
slide87
3.2 循环结构
  • C语言提供了三种循环语句,可以组成各种不同形式的循环结构,它们是: while语句、do-while语句和for 语句。
slide88
3.2.1 while语句
  • 【例3.6】编写程序计算“1+2+3+...+n”的累加和,其中n由键盘输入。
  • 〖问题分析〗这是数学中的一个简单问题,但如果用程序实现,就必须寻找规律。累加和的形成是由前一次的累加和再加上这次的数值,总是重复这个操作,因此,解决类似问题必须用C语言中的循环语句。
slide90
〖编写程序〗
  • #include<stdio.h>
  • void main()
  • {
  • int i,n,sum;
  • sum=0;
  • i=1;
  • printf("Please input n:");
  • scanf("%d",&n);
  • while (i<=n)
  • {
  • sum=sum+i;
  • i=i+1;
  • }
  • printf("The sum is %d\n",sum);
  • }
slide91
3.2.2 while语句的形式
  • while语句的一般形式为:
  • while(表达式)
  • 语句组;
  • 其中表达式是循环条件,语句组为循环体。
  • while语句中的表达式一般是关系表达式或逻辑表达式。while语句的语义是:计算表达式的值,当值为真(非0)时,执行循环体语句,否则结束循环。
slide92
3.2.3 do-while语句应用实例
  • 【例3.7】用do-while语句求解例3.6的问题。
slide93
〖编写程序〗
  • #include<stdio.h>
  • void main()
  • {
  • int i,n,sum;
  • sum=0;
  • i=1;
  • printf("Please input n:");
  • scanf("%d",&n);
  • do
  • {
  • sum=sum+i;
  • i=i+1;
  • } while(i<=n);
  • printf("The sum is %d\n",sum);
  • }
slide94
3.2.4 do-while语句的形式
  • do-while语句的一般形式为:
  • do
  • 语句组;
  • while(表达式);
  • do-while语句和while语句的区别在于while语句是先判断后执行,如果条件不满足,则一次循环体语句也不执行;do-while语句是先执行后判断,如果条件不满足,至少要执行一次循环体。
slide95
3.2.5 for语句应用实例
  • 【例3.8】古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,前20个月中,每个月的兔子总数为多少对?
slide96
〖问题分析〗兔子在前两个月,都只有1对;在第三个月,原来的一对兔子再加上新出生的一对小兔子,兔子总数变为2对,;在第四个月,在第三个月的基础上,原来的一对兔子又生一对兔子,共3对;在第五个月,在第四个月的基础上,第一对出生的兔子开始生小兔子,所以在第四个月的基础上再加两对,共5对……〖问题分析〗兔子在前两个月,都只有1对;在第三个月,原来的一对兔子再加上新出生的一对小兔子,兔子总数变为2对,;在第四个月,在第三个月的基础上,原来的一对兔子又生一对兔子,共3对;在第五个月,在第四个月的基础上,第一对出生的兔子开始生小兔子,所以在第四个月的基础上再加两对,共5对……
  • 经过归纳分析,兔子总数的规律为数列1,1,2,3,5,8,13,21.... 这是数学中的Fibonacci数列。该数列的规律为
  • a1=1(n=1)
  • a2=1(n=2)
  • an=an-1+an-2 (n≥3)
  • 这里求前20项。
slide98
〖编写程序〗
  • #include "stdio.h"
  • void main( )
  • {
  • long a1,a2,an;
  • int i;
  • a1=a2=1; /* 给a1,a2赋初值 */
  • printf("a1=1\ta2=1\t"); /* 输出第1,2个月的兔子对数 */
  • for(i=3;i<=20;i++)
  • {
  • an=a1+a2;
  • printf("a%d=%ld\t",i,an);
  • if(i%5==0) /* 每行输出5个结果 */
  • printf("\n");
  • a1=a2;
  • a2=an;
  • }
  • }
slide99
3.2.6 for语句语法
  • for语句格式:
  • for ( 表达式1 ; 表达式2 ; 表达式3 )
  • 语句组;
  • 表达式1通常用来给循环变量赋初值,一般是赋值表达式。也允许在for语句外给循环变量赋初值,此时可以省略该表达式。表达式2通常是循环条件,一般为关系表达式或逻辑表达式。表达式3通常可用来修改循环变量的值,一般是赋值语句。即:
  • for (循环变量赋初值;循环结束条件;循环变量增值 )
  • 语句组;
slide100

表达式1

表达式2

语句组

表达式3

slide101
这三个表达式都可以是逗号表达式, 即每个表达式都可由多个表达式组成。
  • 例:for ( sum=0 , i=1 ; i<=50 ; i++,i++ )
  • sum=sum+2*i;
  • 或:for ( sum=0 , i=1 ; i<=50 ; i=i+2 )
  • sum=sum+2*i
  • 这三个表达式可以省略,但分号不能省略。
slide102
3.2.7 循环嵌套
  • 【例3.9】搬砖问题。36块砖,36人搬,男搬4,女搬3,两个小孩抬一砖,要求一次全搬完,问男、女、小孩各若干?
  • 〖问题分析〗根据题意,设完成任务需要男m人,女w人,小孩c人。列数学方程,得出问题条件:
  • m+w+c=36
  • 4*m+3*w+c/2=36
  • 可以用枚举的方法,让变量w在0~9 、m在0~12 、c在0~36的偶数中取值,形成满足上述条件的w 、m、c的组合。
slide104
〖编写程序〗
  • #include "stdio.h"
  • void main( )
  • { int m,w,c;
  • for(m=0;m<9;m++)
  • for(w=0;w<12;w++)
  • { c=36-w-m;
  • if(c%2==0&&4*m+3*w+c/2==36)
  • printf("m=%d,w=%d,c=%d\n",m,w,c);
  • }
  • }
slide105
【例3.10】求解2008!的末尾有多少个0。
  • 〖问题分析〗根据题意,因为数值太大,肯定不能用连乘的方法来求;经过分析,末尾0产生的原因是“5乘以任一偶数都可在末尾产生一个0”。因此只需在1-2008中统计能整除5的数包含的5的因子个数即可。
slide107
〖编写程序〗
  • #include "stdio.h"
  • void main( )
  • {
  • int i,k,count;
  • count=0;
  • for(i=5;i<=2008;i=i+5)
  • {
  • k=i;
  • while((k%5)==0)
  • {
  • count=count+1;
  • k=k/5;
  • }
  • }
  • printf("There are %d zero in the end of 2008!\n",count);}
slide108
3.2.8 break语句应用实例
  • 【例3.11】把数316表示为两数之和,其中一个是13的倍数,另一个是11的倍数。
  • 〖问题分析〗
  • 根据题意,第一个数是13m,那么第二个数就一定是316-13m,然后令m的值从1开始试探,如果316-13m正好也是11的倍数,那么就得到了答案。
slide110
〖编写程序〗
  • #include "stdio.h"
  • void main( )
  • {
  • int m;
  • for(m=1;;++m)
  • if((316-13*m)%11==0)
  • break;
  • printf("316=%d+%d\n",13*m,316-13*m);
  • }
slide111
3.2.9 continue语句应用实例
  • 【例3.12】输出100以内能被7整除的数。
  • 〖问题分析〗
  • 根据题意,本实例需执行重复操作对1到100的整数依次进行判断,判断该整数是否是7的倍数,若是则输出,否则判断下一整数。因此,需使用循环语句来实现。
slide113
〖编写程序〗
  • #include "stdio.h"
  • void main( )
  • {
  • int n,i=0;
  • for(n=7;n<=100;n++)
  • {
  • if (n%7!=0)
  • continue;
  • printf("%d\t",n);
  • i++;
  • if (i%5==0)
  • printf("\n");
  • }
  • printf("\n");
  • }
slide114
温馨提示:
  • continue语句只结束本层本次的循环,并不跳出循环,而break语句会跳出循环。
  • 作业:
  • 1.自学3.3综合应用实例一节并上机调试程序。
  • 2.所有章后作业作业本完成并上机完成。
  • 3.实践教材所有习题
slide115
第4章 数组
  • 本章重点
  • ① 一维数组的定义及其使用方法
  • ② 多维数组的定义及其使用方法
  • ③ 指针与一维数组的关系及混合应用
  • ④ 指针与多维数组的关系及混合应用
  • ⑤ 指针数组的基本概念及其使用方法
  • ⑥ 动态数组的基本概念及其构造方法
slide116
程序处理的对象是数据,这些数据通常不仅是一个数据,而是批量数据。一个变量只能存储一个数据,要存储批量数据就需要许多变量。C语言中提供了一种专门用来组织批量数据的数据类型——数组,它将相同数据类型的数据组织在一起,用统一的数组名来表示,每个元素用下标来区分,按照下标的个数,数组分为一维数组和多维数组。程序处理的对象是数据,这些数据通常不仅是一个数据,而是批量数据。一个变量只能存储一个数据,要存储批量数据就需要许多变量。C语言中提供了一种专门用来组织批量数据的数据类型——数组,它将相同数据类型的数据组织在一起,用统一的数组名来表示,每个元素用下标来区分,按照下标的个数,数组分为一维数组和多维数组。
slide117
4.1一维数组
  • 4.1.1 一维数组应用实例
  • 【例4.1】 计算参加英语口语考试的最终得分。考生考试完毕后,由15位评委对考生进行打分,每位考生的最终得分计算规则为去掉一个最高分和去掉一个最低分,然后计算剩下的平均分,该平均分为考生的最终得分。
  • 〖问题分析〗
  • 根据题意,考生考试完毕后,由15位评委对参赛队员进行打分,则必定产生15个分值,因此可以用有15个元素的数组来储存这些分值。然后对15个数组元素进行求和sum,并从中找出最大值max和最小值min,最终得分result=(sum-max-min)/(15-2)。
slide119
〖编写程序〗
  • #include <stdio.h>
  • void main()
  • {
  • float score[15],sum,min,max,result;
  • //定义有15个元素的浮点型数组score
  • int i;
  • printf("请输入成绩:\n");
  • for(i=0;i<15;i++)
  • scanf("%f",&score[i]); //为数组score的元素输入值
  • min=max=score[0];
  • //将min和max赋值为下标0的元素score[0]
  • sum=0;
slide120
for(i=0;i<15;i++)
  • {
  • sum+=score[i];
  • if(score[i]<min)
  • min=score[i]; //求最小值
  • else if(score[i]>max)
  • max=score[i]; //求最大值
  • }
  • result=(sum-min-max)/(15-2); //求平均成绩
  • printf("该考生最终得分是%f\n",result);
  • return 0;
  • }
slide121
4.1.2 一维数组的定义
  • 1. 数组的定义
  • <类型说明符> <数组名>[<常量表达式>]
  • 例如:float score[15];
  • 表示这个数组名为score,包含15个元素,每个元素都是float类型。在例4.1中,将记录15个评委给出的分数的float型数据组织在一起,并用一个介于0到14的下标唯一标识。
slide122
使用数组需注意以下几点:
  • (1)数组名按照标识符的规则命名;
  • (2)用方括号将常量表达式括起;
  • (3)常量表达式定义了数组元素的个数;
  • (4)数组下标从0开始。如果定义了15个元素,是从第0个元素至第14个元素;
  • (5)常量表达式中不允许包含变量;
  • 例如:
  • int n= 5;
  • int a[n]; 是不合法的!
slide123
2. 数组的物理结构
  • 数组定义后,系统就为其分配相应的存储空间。在C程序中,系统为数组分配一片连续的存储空间,数组名score指明了这片连续存储空间的起始地址。由于每个float型数据占4个字节的存储空间,所以系统为数组score[15]分配15*4个字节的存储空间,数组中的每个元素按其下标依次存储,如图所示。
slide124
3. 数组的初始化
  • 在定义数组时,系统只为数组元素分配存储空间,而并没有为其中任何元素赋予一个确定的值。如果这时使用数组元素的内容,就会得到一个不确定的值。因此,引用数组元素之前,必须确保该元素已有一个确定的值。为每个元素赋予初值的过程称为数组的初始化。
  • 例如:int a[5] = { 3, 5, 4, 1, 2 };
  • 即a[0] = 3; a[1] = 5; a[2] = 4; a[3] = 1; a[4] = 2;如果只是部分赋值,则只提供给前面的元素,后面的元素为0。
slide125
以下是赋值的各种情形:
  • (1)数组元素值不确定
  • int a[4]; /* 声明项 */
  • printf("a[0]=%d; a[1]=%d; a[2]=%d; a[3]=%d\n", a[0], a[1], a[2], a[3]);
  • (2)对部分元素赋值,剩余元素均被自动赋值为0
  • int a[4] = { 3, 8 };
  • (3)为所有元素都赋值,可以省略方括号内的数组元素个数;
  • int a[ ] = { 3, 8,0,0 };
slide126
4.1.3 一维数组的引用
  • 从前面的章节中已知:变量可以通过变量名对该变量所对应的存储空间进行存取操作,但对于数组不能只用数组名来引用数组中的某一个元素,需要按照以下格式书写:
  • <数组名>[<下标表达式>]
  • 例如:score[0]、score[1]、…、score[14]分别表示数组score的15个元素。
  • 引用数组元素时需要特别注意下标不能越界
  • 例如:在例4.1中,数组的元素个数为15,下标的取值范围是从0到14,所以不能引用元素score[15]。
  • 在程序中,通常引用数组是执行两种操作:赋值和读值。
slide127
4.1.4 字符数组与字符串
  • 1.字符数组
  • 数组元素的值全部为字符的数组称为字符数组。定义形式与前面介绍的数值数组相同,只是数据类型为char。
  • 2.字符串
  • 所谓字符串是指一个有限长度的字符序列。在C语言中,字符串常量用一对双引号(””)括起来。例如,”Welcome to Shanxi”、”This is a C program.”都是字符串常量。其中字符串中所包含的字符个数被称为字符串的长度。”Welcome to Shanxi”的长度是17,””是空串,表示一个字符也没有,它的长度是0,” ”是空格串,表示这个字符串由一个空格字符组成,长度为1。
slide129
例如用字符数组初始化为:
  • char c[ ]={'c', ' ','p','r','o','g','r','a','m'};
  • 数组c在内存中的实际存放情况为:
  • 而用字符串的方式对数组作初始化赋值,可写为:
  • char c[ ]={"C program"};
  • 或去掉{ }写为:
  • char c[ ]="C program";
  • 数组c在内存中的实际存放情况为:
slide130
几个最常用的字符串函数
  • (1)字符串输出函数 puts
  • (2)字符串输入函数gets
  • (3)字符串连接函数strcat
  • (4)字符串拷贝函数strcpy
  • (5)字符串比较函数strcmp
  • (6)测字符串长度函数strlen
slide131
4.2 多维数组
  • 4.2.1 二维数组应用实例
  • 【例4.8】一个学习小组有5个人,每个人有三门课的考试成绩,见表4-1所示,求全组各科的平均成绩和全组所有课程的总平均成绩。
slide132
〖问题分析〗
  • 可设置一个二维数组score[3][5]存放五个人三门课的成绩。再设置一个一维数组ca[3]存放所求得各科平均成绩,设变量average 为全组所有课程的总平均成绩。程序中可以用一个双重循环。在内层循环中循环5次,把该组一门课程的成绩累加起来,退出内循环后再把该累加成绩除以5送入ca[i]之中,这就是该门课程的平均成绩。外循环共循环三次,分别求出三门课各自的平均成绩并存放在ca数组之中。退出外循环之后,把ca[0],ca[1],ca[2]相加除以3即得到全组所有课程的总平均成绩。实例的N-S流程图如图所示。
slide134
〖编写程序〗
  • #include <stdio.h>
  • void main()
  • {
  • int i,j,s=0,score[3][5]={{80,61,59,85,76},{75,65,63,87,77},{92,71,70,90,85}};
  • float average,ca[3];
  • for(i=0;i<3;i++)
  • { s=0;
  • for(j=0;j<5;j++)
  • s=s+score[i][j];
  • ca[i]=s/5.0;
  • }
  • average =(ca[0]+ca[1]+ca[2])/3;
  • printf("高等数学:\t%6.2f\n",ca[0]);
  • printf("C程序设计:\t%6.2f\n",ca[1]);
  • printf("数据结构:\t%6.2f\n ",ca[2]);
  • printf("总均分:\t%6.2f\n", average);
  • }
slide135
4.2.2 多维数组的定义
  • 1. 二维数组的定义
  • 二维数组定义的一般形式是:
  • 类型说明符 数组名[常量表达式1][常量表达式2]
  • 其中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。
  • 例如:
  • int a[3][4];

定义了一个三行四列的数组,数组名为a,其下标变量的类型为整型。该数组的元素个数共有3×4个,即:

a[0][0],a[0][1],a[0][2],a[0][3]

a[1][0],a[1][1],a[1][2],a[1][3]

a[2][0],a[2][1],a[2][2],a[2][3]

slide136
2. 二维数组的物理结构
  • 二维数组在概念上是二维的,即其下标在两个方向上变化,元素在数组中的位置也处于一个平面上,而不象一维数组只是一个向量。但实际的硬件存储器却是连续编址的,也就是说存储器单元是按一维线性排列的。在C语言中,二维数组是按行排列的, 即放完一行之后顺次放入第二行。
  • 上例中数组a的各元素在内存中的存放顺序是,先存放a[0]行,再存放a[1]行,最后存放a[2]行。每行中有四个元素也是依次存放。由于数组a说明为int类型,该类型占4个字节的内存空间,所以每个元素均占有4个字节。
slide138
3. 二维数组的初始化
  • 二维数组初始化也是在类型说明时给各下标变量赋以初值。二维数组可按行分段赋值,也可按行连续赋值。
  • 例如对数组a[5][3]:
  • (1)按行分段赋值可写为:
  • int a[5][3]={ {80,75,92},{61,65,71},{59,63,70},
  • {85,87,90},{76,77,85} };
  • (2)按行连续赋值可写为:
  • int a[5][3]={ 80,75,92,61,65,71,59,63,70,85,87,90
  • ,76,77,85};
  • 这两种赋初值的结果是完全相同的。
slide139
4.2.3 多维数组的引用
  • 二维数组的引用方式和一维数组基本类似,其表示的形式为:
  • 数组名[下标][下标]
  • 其中下标应为整型常量或整型表达式。
  • 例如:
  • a[3][4]
  • 表示数组a中第三行第四列的元素。
  • 元素和数组说明在形式中有些相似,但这两者具有完全不同的含义。前者只能是常量,后者可以是常量,变量或表达式。
slide140
4.3 指针与一维数组
  • 4.3.1 指针与一维数组应用实例
  • 【例4.9】一辆汽车在开始出发前其里程表上的读数是一个5位对称数95859,匀速行驶两小时后,发现里程表上是一个新的5位对称数。问新的对称数是多少?汽车的速度是多少?
  • 〖问题分析〗
  • 从95860开始依次递增,将每一个数分解成五个一位数,将这五个一位数存储在一个一维数组中,比较对称位置上的数字是否相等,若相等,则是对称数。然后根据前后两个对称数的差即可求出汽车的速度。
slide142
〖编写程序〗方法一:下标法
  • #include <stdio.h>
  • void main()
  • { int data[5],i,found=0,m;
  • long n=95860,nx,v;
  • while(!found)
  • { m=10;nx=n;
  • for(i=0;i<5;i++)
  • { data[i]=nx%m;
  • nx=nx/m; }
  • if((data[0]==data[4])&&(data[1]==data[3]))
  • found=1;
  • else
  • n++; }
  • printf("新对称数是%d\n",n);
  • v=(n - 95859)/2;
  • printf("汽车的运行速度是%ld公里/小时\n",v);}
slide143
方法二:通过数组名计算元素的地址,对元素进行访问方法二:通过数组名计算元素的地址,对元素进行访问
  • #include <stdio.h>
  • void main()
  • { int data[5],i,found=0,m;
  • long n=95860,nx,v;
  • while(!found)
  • { m=10;nx=n;
  • for(i=0;i<5;i++)
  • { *(data+i)=nx%m;
  • nx=nx/m;}
  • if((*(data+0)==*(data+4))&&(*(data+1)==*(data+3)))
  • found=1;
  • else
  • n++; }
  • printf("新对称数是%d\n",n);
  • v=(n - 95859)/2;
  • printf("汽车的运行速度是%ld公里/小时\n",v);}
slide144
方法三:用指针变量指向元素地址,对元素进行访问方法三:用指针变量指向元素地址,对元素进行访问
  • #include <stdio.h>
  • void main()
  • { int *d,data[5],i,found=0,m;
  • long n=95860,nx,v;
  • d=data;
  • while(!found)
  • { m=10;nx=n;
  • for(i=0;i<5;i++)
  • { *(d+i)=nx%m;
  • nx=nx/m; }
  • if((*(d+0)==*(d+4))&&(*(d+1)==*(d+3)))
  • found=1;
  • else
  • n++; }
  • printf("新对称数是%d\n",n);
  • v=(n - 95859)/2;
  • printf("汽车的运行速度是%ld公里/小时\n",v);}
slide145
方法四:用指针运算找出元素的地址,对元素进行访问方法四:用指针运算找出元素的地址,对元素进行访问
  • #include <stdio.h>
  • void main()
  • { int *d,data[5],i,found=0,m;
  • long n=95860,nx,v;
  • d=data;
  • while(!found)
  • { m=10;nx=n;
  • for(i=0;i<5;i++,d++) //指针运算
  • { *d=nx%m; nx=nx/m;}
  • d=data;
  • if((*(d+0)==*(d+4))&&(*(d+1)==*(d+3)))
  • found=1;
  • else
  • n++; }
  • printf("新对称数是%d\n",n);
  • v=(n - 95859)/2;
  • printf("汽车的运行速度是%ld公里/小时\n",v);}
slide146
4.3.2 指针与数组的关系
  • 1. 指向数组元素的指针
  • 一个数组是由连续的一块内存单元组成的。数组名就是这块连续内存单元的首地址。一个数组也是由各个数组元素(下标变量)组成的。每个数组元素按其类型不同占有几个连续的内存单元。一个数组元素的首地址也是指它所占有的几个内存单元的首地址。
  • 定义一个指向数组元素的指针变量的方法,与以前介绍的指针变量相同。
  • 例如:
  • int a[10]; /*定义a为包含10个整型数据的数组*/
  • int *p; /*定义p为指向整型变量的指针*/
  • 应当注意,因为数组为int型,所以指针变量也应为指向int型的指针变量。
slide147
下面是对指针变量赋值:
  • p=&a[0];
  • 该赋值语句的功能是把a[0]元素的地址赋给指针变量p,也就是说,p指向a数组的第0号元素,如图所示。

p,a,&a[0]均指向同一单元,它们是数组a的首地址,也是元素a[0]的首地址。应该说明的是p是变量,而a、&a[0]都是常量,在编程时应予以注意。

slide148
2.通过指针引用数组元素
  • 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素。
  • 如果p的初值为&a[0],则:
  • (1)p+i和a+i就是a[i]的地址,或者说它们指向a数组的第i个元素,如图所示。
  • (2)*(p+i)或*(a+i)就是p+i或a+i所指向的数组元素,即a[i]。例如,*(p+5)或*(a+5)就是a[5]。
  • (3)指向数组的指针变量也可以带下标,如p[i]与*(p+i)等价。
  • (4)若p的初值为a[i],则p+j指向a[i+j]
slide149
4.4 指针与多维数组
  • 【例4.10】用不同的方式显示二维数组的元素。
  • 〖编写程序〗
  • void main()
  • { int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
  • printf("a=%d,*a=%d\n ",a,*a);
  • printf("a[0] =%d, &a[0]=%d, &a[0][0]=%d\n",a[0] ,&a[0] ,&a[0][0]);
  • printf("a+1=%d, *(a+1)=%d\n",a+1,*(a+1));
  • printf("a[1]=%d, &a[1]= %d , &a[1][0]= %d\n",a[1] ,&a[1] ,&a[1][0]);
  • printf("a+2=%d, *(a+2) =%d , a[2]= %d \n", a+2, *(a+2), a[2]);
  • printf("&a[2]=%d, &a[2][0] =%d \n",&a[2] , &a[2][0]);
  • printf("a[1]+1=%d, *(a+1)+1=%d\n",a[1]+1,*(a+1)+1);
  • printf("*(a[1]+1)=%d, *(*(a+1)+1)=%d\n",*(a[1]+1),*(*(a+1)+1));
  • }
slide150
4.4.2多维数组的地址
  • 设有短整型二维数组a[3][4]各元素的值如下:
  • 0 1 2 3
  • 4 5 6 7
  • 8 9 10 11
  • 它的定义为:
  • Short int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
slide152
C语言允许把一个二维数组分解为多个一维数组来处理。因此数组a可分解为三个一维数组,即a[0],a[1],a[2]。每一个一维数组又含有四个元素。例如a[0]数组,含有a[0][0],a[0][1],a[0][2],a[0][3]四个元素,如图所示。C语言允许把一个二维数组分解为多个一维数组来处理。因此数组a可分解为三个一维数组,即a[0],a[1],a[2]。每一个一维数组又含有四个元素。例如a[0]数组,含有a[0][0],a[0][1],a[0][2],a[0][3]四个元素,如图所示。
slide153
4.4.3指针与多维数组的地址
  • 从二维数组的角度来看,a是二维数组名,a代表整个二维数组的首地址,也是二维数组0行的首地址,等于1000。a+1代表第一行的首地址,等于1008,如图所示。

思考:

(1)a,a[0],*(a+0),*a,&a[0][0]的值是什么?

(2)a+1,a[1],*(a+1),&a[1][0]的值

(3)a[i],&a[i],*(a+i)和a+i的值

(4)a[i][j],a[i]+j,*(a+i)+j和*(*(a+i)+j)

slide154
4.5 指针数组
  • 4.5.1 指针数组应用实例
  • 【例4.12】用一个指针数组来输出一个二维数组。
  • 〖编写程序〗
  • #include <stdio.h>
  • void main(){
  • int a[3][3]={1,2,3,4,5,6,7,8,9};
  • int *pa[3]={a[0],a[1],a[2]};
  • //定义指针数组并将其初值分别赋值为a[0]行首地址、a[1]行首地址和a[2]行首地址
  • int *p=a[0];
  • int i;
  • for(i=0;i<3;i++)
  • printf("%d,%d,%d\n",a[i][2-i],*a[i],*(*(a+i)+i));
  • for(i=0;i<3;i++)
  • printf("%d,%d,%d\n",*pa[i],p[i],*(p+i));
  • }
slide155
4.5.2 指针数组的定义
  • 一个数组的元素值为指针则是指针数组。 指针数组是一组有序的指针的集合。 指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。
  • 指针数组说明的一般形式为:
  • 类型说明符 *数组名[数组长度]
  • 其中类型说明符为指针值所指向的变量的类型。
  • 例如:
  • int *pa[3]
  • 表示pa是一个指针数组,它有三个数组元素,每个元素值都是一个指针,指向整型变量。
slide156
4.6动态数组
  • 4.6.1 动态数组应用实例
  • 【例4.13】用动态数组打印6行杨辉三角形。杨辉三角形如图所示。
slide157
〖问题分析〗
  • 观察杨辉三角形可发现:
  • (1)第i行有i个值;
  • (2)i行的第一个数和第i个数都是1;
  • (3)i行的第j(1<j<i)个数的值是第i-1行的第j-1个数和第j个数的和。
  • 考虑各行的数据个数不同,应为每行数据提供不同数量的存储空间来存储每行的数据。同时为了能够找到每行数据并将其组织在一起,需要将每一行的首地址保存下来,所以需要定义一个6个元素的数组来存储每一行的首地址,由于这个数组存储的是地址,所以需要定义为指针数组。
slide159
〖编写程序〗
  • #include <stdio.h>
  • #include <stdlib.h>
  • #define MAXLINE 6
  • void main()
  • {int *a[MAXLINE]; //定义指针数组
  • int i,j;
  • a[0]=(int*)malloc(sizeof(int));
  • //动态申请1个整型数据存储空间
  • *a[0]=1; //a[0]行的列标为0的元素赋值1
  • for(i=1;i<MAXLINE;i++)
  • { a[i]=(int*)malloc(sizeof(int)*(i+1));
  • //动态申请a[i]行的i+1个整型数据存储空间
  • *a[i]=1; // a[i]行的列标为0的元素赋值1
slide160
for(j=1;j<i;j++)
  • *(a[i]+j)=*(a[i-1]+j-1)+*(a[i-1]+j);
  • // 为a[i]行的列标为j的元素赋值
  • *(a[i]+i)=1;
  • // a[i]行的列标为i的元素赋值1
  • }
  • for(i=0;i<MAXLINE;i++)
  • {
  • for(j=0;j<=i;j++)
  • printf("%4d",*(a[i]+j));
  • printf("\n");
  • }
  • for(i=0;i<MAXLINE;i++)
  • free(a[i]);
  • }
slide161
4.6.2 动态数组的构造
  • 在C语言的stdio.h函数库中提供了一对动态申请和释放存储空间的标准函数,在调用时,必须利用include命令将stdlib.h加载到程序中。这两个函数如下:
  • void *malloc(int size)
  • void free(void *p)
  • malloc函数的功能是请求系统分配size个字节的存储空间。若分配成功,则返回所分配的存储空间的首地址;否则返回NULL。
  • free函数的功能是释放由指针p指向且由malloc()函数分配的存储空间。
slide162
作业:
  • 1.自学4.7节内容,上机调试所有程序
  • 2.完成课后所有作业。
  • 3.完成实践教材习题。
slide163
第5章 函数
  • 本章重点
  • ① 函数的基本概念及其定义方法
  • ② 形参与实参的联系与区别
  • ③ 函数指针的概念及其应用
  • ④ 函数的嵌套调用及递归调用
  • ⑥ 全局变量、局部变量的区别
  • ⑦ 动态变量与静态变量的区别
  • ⑧ void main()函数的参数
slide165
5.1函数的基本应用
  • 5.1.1 函数基本应用实例
  • 【例5.1】已知函数f(x)=x2+x+5,编程序求当x=1,2,3,…,10时f(x)的值。
  • 〖问题分析〗
  • 根据题意,当x=1,2,3, …,10时,f(x)对应不同的值,因此,可以分别设计两个函数:
  • f(x)——给定一个x的值,求f(x)的值;
  • main()——求出不同f(x)的值并输出;
slide166
〖编写程序〗
  • #include <stdio.h>
  • int f(int x) //求f(x)=x2+x+5的函数
  • {
  • int s;
  • s=x*x+x+5;
  • return(s);
  • }
  • void main() // 主函数
  • {
  • int f(int x); // 对被调用函数声明
  • int i;
  • for(i=1;i<=10;i++)
  • printf("x=%d f(x)=%d\n",i,f(i)); // 输出结果
  • }
slide167
5.1.2 函数的定义
  • 函数的定义格式:
  • <数据类型> <函数名> (<参数表>)
  • {
  • 说明部分
  • 语句部分
  • }
  • (1)int f(int x)中, f为函数名,其定义规则请参考标识符的定义规则。int是函数的数据类型,表示函数的返回值为整型,函数的数据类型可以定义为void类型,表示函数没有返回值 。
  • (2)(int x)括号中的x为函数的形式参数,可以是多个。
  • (3)说明部分主要用来声明变量 。
  • (4)语句部分是函数的执行部分,描述函数功能。
  • (5)函数中返回语句的形式为:
  • return(表达式);或 return 表达式;
slide168
5.1.3 函数的声明
  • 函数的声明格式:
  • <数据类型> <函数名> (<参数表>);
  • 如例5.1main()函数体中第一行,int f(int x);为对函数f的声明。
slide169
5.1.4 函数的调用
  • 函数调用的一般形式为:
  • <函数名> (<实参列表>)
  • 例如,在例5.1中,语句
  • printf("x=%d f(x)=%d\n",i,f(i));
  • 调用了函数f,实参为i。
slide170
5.1.5 形式参数与实际参数之间的关系
  • 形式参数(简称形参)是在定义函数时放在函数名后括号中的参数。在未进行函数调用时,并不对形式参数分配内存单元。在发生函数调用时,立刻给形式参数分配内存单元。调用结束后,释放掉形参所占的内存单元。因此,形参只有在函数内部有效。函数调用结束后则不能再使用该形参变量。
  • 在定义函数的时候,必须指定形参变量的类型,指定的方法有两种:
  • 例:
  • (1)int f(int x) (2)int f(x)
  • { int x;
  • int s; {
  • s=x*x+x+5; int s;
  • return(s); s=x*x+x+5;
  • } return(s);
  • }
slide171
实际参数(简称实参)是一个具有确定值的表达式。函数在调用时,将实参赋给形参。实际参数(简称实参)是一个具有确定值的表达式。函数在调用时,将实参赋给形参。
  • 函数调用中发生的数据传送是单向的,即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。
slide172
5.2 数据传递
  • 5.2.1 函数参数传递实例
  • 【例5.2】输入10个学生的成绩,对这些成绩进行升序排序,并输出排序的结果。
  • 〖问题分析〗
  • 用一个一维数组来记录10个学生的成绩,然后设计三个函数input()、sort()和print(),分别实现接收成绩、将成绩排序和输出结果。
slide173
#include <stdio.h>
  • #define SIZE 10 // 定义符号常量SIZE=10
  • void input(int *a); // 被调用函数声明
  • void sort(int *a);
  • void print(int *a);
  • void main() // 主函数
  • {
  • int score[SIZE];
  • input(score); //实参为数组的首地址值
  • sort(score);
  • print(score);
  • }
slide174
void input(int *a) // 输入成绩函数
  • {
  • int i;
  • printf("请输入%d个成绩:\n", SIZE);
  • for (i=0;i< SIZE;i++)
  • scanf("%d",&a[i]);
  • }
  • void print(int *a) // 输出结果函数
  • {
  • int i;
  • printf("排序后的成绩:\n");
  • for(i=0;i<SIZE;i++)
  • printf("%4d",a[i]);
  • printf("\n");
  • }
slide175
void sort(int *a) // 升序插入排序函数
  • {
  • int i,j,min,temp;
  • for (i=0;i<SIZE;i++)
  • {
  • min=i;
  • for (j=i;j< SIZE;j++)
  • if(a[j]<a[min])
  • min=j;
  • temp=a[min];
  • a[min]=a[i];
  • a[i]=temp;
  • }
  • }
slide176
5.2.2 函数参数值的形式
  • 函数传递的参数值方式
  • 有数据值和地址值两种。

上例中,score为实参数组名,a为指针类型形参。数组名就是数组的首地址,实参向形参传送数组名实际上就是传送数组的首地址,形参a得到的是该数组的首地址后,使指针变量a也指向同一数组score,这样指针a也可以看做是一个数组,与数组score共同占用相同的存储空间,如图所示。

slide177
对于一个数组,实参与形参进行地址传递的形式有以下4种,下面是将数组a的首地址值传递给x,使数组a和数组x共同占用相同的存储空间的4种形式:对于一个数组,实参与形参进行地址传递的形式有以下4种,下面是将数组a的首地址值传递给x,使数组a和数组x共同占用相同的存储空间的4种形式:
  • (1) 形参和实参都是数组名。
  • void main() f(int x[],int n)
  • {int a[10]; {
  • …… ……
  • f(a,10) }
  • ……
  • }
slide178
(2)实参用数组名,形参用指针变量。
  • void main() f(int *x,int n)
  • {int a[10]; {
  • …… ……
  • f(a,10) }
  • ……
  • }
slide179
(3)实参、形参都用指针变量。
  • void main() f(int *x,int n)
  • {int a[10],*p=a; {
  • …… ……
  • f(p,10) }
  • ……
  • }
slide180
(4)实参为指针变量,形参为数组名。
  • void main() f(int x[],int n)
  • {int a[10],*p=a; {
  • …… ……
  • f(p,10) }
  • ……
  • }
slide181
5.3函数指针
  • 5.3.1 函数指针应用实例
  • 【例5.3】用复化梯形公式计算三个定积分值
  • 对于积分函数 复化梯形公式为
  • ,其中
  • 化简后得:
slide182
〖问题分析〗
  • 按照题目要求需求三个函数的积分,从复化梯形公式可以看出,对于不同的定积分问题求解基本相同,只是需要更改 ,所以这里可以定义函数指针,当函数指针指向不同函数的入口地址时,就可以调用不同的函数。因此,这里可以定义一个用梯形法求定积分的通用函数ft(int n,double a,double b,double (*f)()),通过形式参数函数指针f来传递不同的函数的入口地址,使(*f)()代表不同的函数,而不需要对函数体做任何修改便可求出不同函数的定积分。
slide183
#include "stdio.h"
  • #include "math.h“
  • double f1(double x) //函数f1
  • {
  • double y;
  • y=exp(-x*x);
  • return(y);
  • }
  • double f2(double x) //函数f2
  • {
  • double y;
  • y=1.0/(1+25*x*x);
  • return(y);
  • }
  • double f3(double x) //函数f3
  • {
  • double y;
  • y=log(1+x)/(1+x*x);
  • return(y);
  • }
slide184
double ft(int n,double a,double b,double (*f)()) //求给定函数f的a到b的定积分
  • {
  • int k;
  • double s,h;
  • h=(b-a)/n;
  • s=((*f)(a)+(*f)(b))/2;
  • for(k=1;k<n;k++)
  • s+=(*f)(a+k*h);
  • s=s*h;
  • return(s);
  • }
slide185
void main()
  • {
  • double (*p)(); //定义函数指针
  • double s;
  • double f1(double x); //函数声明
  • double f2(double x);
  • double f3(double x);
  • double ft(int n,double a,double b,double (*f)());
  • p=f1; //指向f1函数
  • s=ft(10,0,1,p); //求s1
  • printf("s1=%e\n",s);
  • p=f2; //指向f2函数
  • s=ft(10,0,1,p); //求s2
  • printf("s2=%e\n",s);
  • p=f3; //指向f3函数
  • s=ft(10,0,1,p); //求s3
  • printf("s3=%e\n",s);
  • }
slide186
5.3.2 函数指针
  • 函数指针变量定义的一般形式为:
  • 类型说明符 (*指针变量名)();
  • 其中“类型说明符”表示被指函数的返回值的类型。“(* 指针变量名)”表示“*”后面的变量是定义的指针变量。最后的空括号表示指针变量所指的是一个函数。
  • 例如:
  • int (*pf)();
  • 表示pf是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。
slide187
5.4 函数的嵌套调用
  • 5.4.1 函数嵌套调用应用实例
  • 【例5.4】计算s=22!+32!
  • 〖问题分析〗
  • 本题可编写两个函数,一个是用来计算平方值的函数f1,另一个是用来计算阶乘值的函数f2。主函数先调f1计算出平方值,再在f1中以平方值为实参,调用 f2计算其阶乘值,然后返回f1,再返回主函数,在循环程序中计算累加和。
slide188
#include "stdio.h"
  • long f1(int p) //计算平方值并求阶乘
  • {
  • int k;
  • long r;
  • long f2(int);
  • k=p*p;
  • r=f2(k);
  • return r;
  • }
slide189
long f2(int q) //计算阶乘
  • {
  • long c=1;
  • int i;
  • for(i=1;i<=q;i++)
  • c=c*i;
  • return c;
  • }
slide190
void main()
  • {
  • int i;
  • long s=0;
  • for (i=2;i<=3;i++)
  • s=s+f1(i);
  • printf("\ns=%ld\n",s);
  • }
slide191
5.4.2 函数嵌套调用
  • C语言中不允许作嵌套的函数定义。因此各函数之间是平行的,不存在上一级函数和下一级函数的问题。但是C语言允许在一个函数的定义中出现对另一个函数的调用,即函数的嵌套调用。其调用关系如图所示。
slide192
5.5 函数的递归调用
  • 5.5.1 函数递归调用应用实例
  • 【例5.5】用递归算法求n!
  • 〖问题分析〗
  • 根据数学知识得知:
  • n!=n*(n-1)!=n*(n-1)*(n-2))=
  • ……=n*(n-1)*(n-2)*……*2*1!
  • 发现每一次分解得到的子问题跟原问题结构很相似,只是规模更小,一直可以分解到计算1!。
  • 定义一个函数fact():
  • fact(n) = n!
  • fact(n-1) = (n-1)!
  • 则有fact(n)=n*fact(n-1)
slide194
〖编写程序〗
  • #include <stdio.h>
  • long fact(int n);
  • void main()
  • { int n;
  • printf("请输入n的值:",&n);
  • scanf("%d",&n);
  • printf("%d!=%ld\n",n,fact(n)); }
  • long fact(int n)
  • { if(n==1)
  • return 1;
  • else
  • return n*fact(n-1); //递归调用函数fact
  • }
slide195
5.5.2 函数递归调用
  • 递归过程就是函数在不停调用本身的过程,在递归调用过程中,为了防止函数无休止的递归下去,总会设置一个递归边界,就像这里的1!,下面3!递归调用过程示意图。
slide196
5.6 变量的作用域
  • 5.6.1全局变量与局部变量
  • C语言中的变量,按作用域范围可分为两种,即局部变量和全局变量。
  • 1. 局部变量
  • 局部变量也称为内部变量。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的。
  • 例如:
  • int f1(int a) //函数f1
  • {
  • int b,c;
  • ……
  • }
  • 在函数f1内定义了三个变量,a为形参,b,c为一般变量。在 函数f1的范围内a,b,c有效,或者说a,b,c变量的作用域限于f1内。
slide197
关于局部变量的作用域还要说明以下几点:
  • (1)主函数中定义的变量也只能在主函数中使用,不能在其它函数中使用。同时,主函数中也不能使用其它函数中定义的变量。
  • (2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。
  • (3)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。
  • (4)在复合语句中也可定义变量,其作用域只在复合语句范围内。
slide198
例如:
  • void main()
  • {
  • int s,a;
  • ……
  • {
  • int b;
  • s=a+b;
  • ……
  • }
  • ……
  • }

变量s和a的作用域

变量b的作用域

slide199
2. 全局变量
  • 全局变量也称为外部变量,它是在函数外部定义的变量。它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。
slide200
例如:
  • int a,b; //外部变量
  • void f1() //函数f1
  • {
  • ……
  • }
  • float x,y; //外部变量
  • int fz() //函数fz
  • {
  • ……
  • }
  • void main() //主函数
  • {
  • ……
  • }

(1)a,b定义在源程序最前面,因此在f1,f2及main内不加说明也可使用。

(2)x,y 定义在函数f1之后,而在f1内又无对x,y的说明,所以它们在f1内无效。

(3)若想扩展外部变量的作用域可用extern声明外部变量,扩展程序文件中的外部变量的作用域。

slide201
例如:
  • int max(int x,int y)
  • {int z;
  • z=x>y?x:y;
  • return(z);
  • }
  • void main()
  • {extern A,B;//扩展外部变量的作用域
  • printf("%d\n",max(A,B));
  • }
  • int A=13,B=-8;
slide202
5.7变量的存储类型
  • 5.7.1 变量存储类型应用实例:考察静态局部变量的值。
  • #include "stdio.h"
  • f(int a)
  • {
  • auto int b=0; //声明为动态变量
  • static int c=3; //声明为静态变量
  • b=b+1;
  • c=c+1;
  • return(a+b+c);
  • }
  • void main()
  • {
  • int a=2,i;
  • for(i=0;i<3;i++)
  • printf("%d\n",f(a));
  • }
slide204
5.7.2 动态变量与静态变量
  • 从变量值存在的作用时间(即生存期)角度来分,可以分为静态存储方式和动态存储方式。
  • 静态存储方式:是指在程序运行期间分配固定的存储空间的方式。
  • 动态存储方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。
slide205
1. 动态变量
  • 函数中的局部变量,如不专门声明为static存储类别,都是动态地分配存储空间的。
  • 函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。这类局部变量称为动态变量。
  • 动态变量用关键字auto作存储类别的声明。关键字auto可以省略,auto不写则隐含定为“自动存储类别”,属于动态存储方式。
slide206
2. 静态变量
  • 希望函数中的局部变量的值在函数调用结束后不消失而保留原值,这时就应该指定局部变量为“静态局部变量”,用关键字static进行声明。
slide207
动态变量和静态变量的关系:
  • (1)静态局部变量属于静态存储类别,在程序整个运行期间都不释放。而自动变量(即动态局部变量)属于动态存储类别,函数调用结束后即释放。
  • (2)静态局部变量在编译时赋初值,即只赋初值一次;而对自动变量赋初值是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
  • (3)如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。而对自动变量来说,如果不赋初值则它的值是一个不确定的值。
slide208
5.8 main()函数的参数
  • 5.8.1 void main()函数的参数应用实例
  • 【例5.9】编制一个C程序,使编译生成的生成的可执行文件e59.exe在DOS环境中运行时需输入的命令行为:
  • D:\>e59 BASIC foxpro FORTRAN
  • 输出:
  • BASIC
  • foxpro
  • FORTRAN
slide209
〖编写程序〗
  • #include "stdio.h"
  • void main(int argc,char *argv[ ])
  • {
  • while(argc-->1)
  • { printf("%s\n",*++argv); }
  • }
slide210
5.8.2 main()函数的参数使用方法
  • C语言规定argc(第一个形参)必须是整型变量,argv( 第二个形参)必须是指向字符串的指针数组
  • main函数的函数头应写为:
  • void main (int argc,char *argv[])
  • 例如有命令行为:
  • D:\>e59 BASIC foxpro FORTRAN
  • 由于文件名e59本身也算一个参数,所以共有4个参数,因此argc取得的值为4。argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。指针数组的长度即为参数个数。数组元素初值由系统自动赋予。
slide212
作业:
  • 1.自学5.9综合实例,并上机调试完成。
  • 2.完成习题5所有作业题。
  • 3.实践教材作业。
slide213
第6章 复杂数据类型
  • 本章重点
  • ① 结构体类型的构造及使用方法
  • ② 枚举类型的构造及使用方法
  • ③ 用户自定义数据类型的定义方法
  • ④ 文件类型及其读写方法
  • C语言中前面介绍的int、float、long、double、char等基本数据类型不能满足复杂的应用环境,所以C语言提供了一些复杂的数据类型,如数组、结构体、文件类型和用户自定义类型,这些数据类型都是针对某些复杂的应用环境的。
slide214
6.1 结构体
  • 例如有若干学生的信息,每个学生的信息是由学号、姓名、三门课成绩和总分组成的。学号可为整型或字符型;姓名为字符型;三门课的成绩和总分为整型或实型。
  • 为了解决这个问题,C语言中给出了另一种数据类型,称为构造数据类型——“结构(structure)”或叫“结构体”。 “结构”是一种构造类型,它是由若干“成员”组成的。每一个成员可以是一个基本数据类型或者又是一个构造类型。
slide215
6.1.1 结构体基本应用实例
  • 【例6.1】学校建立学生登记表,每位学生的信息是一个结构体类型数据,其成员为学号、姓名及语文、数学和英语三门课程的成绩。要求编写函数实现从键盘上读入学号、姓名、和三门课程的成绩。
slide216
〖编写程序〗
  • #include<stdio.h>
  • struct member
  • {
  • char num [10];
  • char name[10];
  • int yuwen;
  • int shuxue;
  • int yingyu;
  • }; /*定义结构体类型*/
slide217
void main( )
  • {
  • struct member stu;
  • printf("输入学生学号\n");
  • scanf("%s",stu.num);
  • printf("输入学生姓名\n");
  • scanf("%s",stu.name);
  • printf("输入学生语文、数学、英语各门课成绩\n");
  • scanf("%d%d%d",&stu.yuwen,&stu.shuxue,&stu.yingyu);
  • printf("%s,%s,%d,%d,%d\n", stu.num,stu.name,stu.yuwen,stu.shuxue,stu.yingyu);
  • }
slide218
6.1.2 结构体及结构体变量的定义
  • 1、结构体定义的一般形式
  • struct 结构名
  • {成员表列};
  • 成员表列由若干个成员组成,每个成员都是该结构的一个组成部分。对每个成员也必须作类型说明,其形式为:
  • 类型说明符 成员名;
slide219
2、结构类型变量的定义
  • 定义结构体数据类型的变量的定义方式有三种:
  • (1)首先定义结构类型,然后用结构类型定义变量。
  • 如例6.1就是采用了此种定义方式。
  • (2)也可以在定义结构类型的同时定义结构变量,例如:
  • struct member
  • {
  • char num [10];
  • char name[10];
  • int yuwen;
  • int shuxue;
  • int yingyu;
  • }stu1,stu2;
slide220
(3)直接定义结构类型变量
  • struct
  • {
  • char num [10];
  • char name[10];
  • int yuwen;
  • int shuxue;
  • int yingyu;
  • }stu1,stu2;
slide221
6.1.3 结构体变量的引用
  • 结构变量的引用一般有三种形式:
  • (1)结构变量名.成员名
  • (2)指针变量名成员名
  • (3)(*指针变量名).成员名
  • 例如:
  • struct member stu;
  • struct member *p=&stu;
  • 引用name成员可以是:
  • stu.name、pname或(*p).name
slide222
6.1.4 结构体数组
  • 【例6.2】建立一个班学生的计算机成绩管理,将学生的数据(姓名,班级,计算机成绩)存放到计算机中。编一个程序,将学生按计算机成绩升序输出。
slide223
#include<stdio.h>
  • #include<string.h>
  • struct stud
  • {
  • char name[10];
  • char class[10];
  • int score;
  • };
slide224
void main( )
  • { void sort(struct stud student[ ]);
  • int i;
  • struct stud student[5]={{"wang", "01",67},{"li", "02",88},
  • {"zhang","06",87},{"wu","02",75},{"xiao","08",97}};
  • sort(student);
  • printf("%15s,%10s,%10s\n", "name", "class","score");
  • for(i=0;i<5;i++)
  • printf("%15s,%10s,%10d\n",student[i].name, student[i].class,student[i].score);
  • }
slide225
void sort(struct stud student[ ]) /*采用选择排序算法实现*/
  • {
  • int i,j,min,k;
  • struct stud temp;
  • for(i=0;i<4;i++)
  • {
  • min=student[i].score;
  • k=i;
  • for(j=i+1;j<5;j++)
  • if(min>student[j].score)
  • {min=student[j].score;k=j;}
  • temp=student[i];
  • student[i]=student[k];
  • student[k]=temp;
  • }
  • }
slide226
选择排序算法基本思想是:
  • 将初始序列(A[0]~A[n-1])作为待排序序列,第一趟在待排序序列(A[0]~A[n-1])中找最小值元素,与该序列中第一个元素A[0]交换,这样序列中A[0]有序,下一趟排序在待排序子序列(A[1]~A[n-1])中进行,在待排序序列(A[1]~A[n-1])中找最小值元素,与该序列中第一个元素A[1]交换,使得序列中A[1]有序。第i趟排序在待排序序列(A[i-1]~A[n-1])中找最小值元素,与该序列中第一个元素A[i-1]交换。经过n-1趟排序后使得初始序列有序。
slide227
6.1.5 结构体指针
  • 【例6.3】用结构指针访问结构中的成员。
  • #include<stdio.h>
  • struct stud{
  • char name[10];
  • char class[10];
  • int score;
  • }stu={“wang”, “01”,67};
  • void main( )
  • {
  • struct stud *p;
  • p=&stu;
  • printf(“%s,%s,%d\n”,stu.name,stu.class,stu.score);
  • printf(“%s,%s,%d\n”,p->.name, p->class, p->score);
  • }
slide228
6.2 枚举类型
  • 在实际问题中,有些变量的取值被限定在一个有限的范围内。例如,一个星期内只有七天,一年只有十二个月,一个班每周有六门课程等等。如果把这些量说明为整型,字符型或其它类型显然是不妥当的。为此,C语言提供了一种称为“枚举”的类型。在“枚举”类型的定义中列举出所有可能的取值,被说明为该“枚举”类型的变量取值不能超过定义的范围。应该说明的是,枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。
slide229
【例6.4】枚举的定义及使用。
  • 〖编写程序〗
  • void main()
  • {
  • enum weekday
  • { sun,mon,tue,wed,thu,fri,sat } a,b,c;
  • a=sun;
  • b=mon;
  • c=tue;
  • printf("%d,%d,%d",a,b,c);
  • }
slide230
1.枚举的定义枚举类型定义的一般形式为:
  • enum 枚举名{ 枚举值表 };
  • 2.枚举变量的说明
  • enum weekday{ sun,mou,tue,wed,thu,fri,sat };
  • enum weekday a,b,c;
  • 或者为:
  • enum weekday{ sun,mou,tue,wed,thu,fri,sat }a,b,c;
  • 或者为:
  • enum { sun,mou,tue,wed,thu,fri,sat }a,b,c;
slide231
3.枚举值是常量,不是变量。不能用赋值语句对它赋值。3.枚举值是常量,不是变量。不能用赋值语句对它赋值。
  • 例如对枚举weekday的元素再作以下赋值:
  • sun=5;
  • 这是错误的。
  • 4.枚举元素本身由系统定义了一个表示序号的数值,从0开始顺序定义为0,1,2…。 如在weekday中,sun值为0,mon值为1,…,sat值为6。
  • 5.只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如:
  • a=sum; b=mon; 是正确的。
  • 而:
  • a=0; b=1; 是错误的。
  • 如一定要把数值赋予枚举变量,则必须用强制类型转换。
  • 如:a=(enum weekday)2;
slide232
作业:
  • 1.自学文件类型及读写文件的几种方法。
  • 2.自学综合应用实例并上机调试完成。
  • 3.完成所有课后作业题。
  • 4.实践教材重点难点分析及习题。
slide233
第七章 链表和树
  • 本章重点
  • ① 链表的基本概念、动态构造方法和基本操作
  • ② 链表操作的程序设计
  • 链表和树都是重要的数据结构,他们在计算机领域中有着广泛的应用。链表是将具有线性关系的数据集合动态组织起来的一种形式,它弥补了数组不能动态更改元素个数的不足。树的链式存储结构也是将树结点动态组织在一起,逻辑上形成一种树状结构,利用树结构,可以有效地解决一些算法问题。本章以链表和树的应用实例讨论链表和树的构造、遍历。
slide234

1356

1249

head

1475

1021

A

B

C

D

1249

1356

1475

1021

Null

  • 7.1.1链表的基本概念
  • 链表分单向链表和双向链表,单向链表的存储结构如图所示。
  • 图中,head是头指针,它指向链表中的第一个结点,即它的指针域存放着第一个结点的起始地址1249,这是链表的惟一入口点,所有链表元素的访问必须从这里出发。A,B,C,D表示数据域,数值1249,1356,1475,1021是存放数据的地址,在结点A中的指针域中存储着结点B的起始地址1356,在结点B中的指针域中存储着结点C的起始地址1475,这样依次存放形成一个链式结构。由于最后一个结点没有后继结点,所以,它的指针域存放一个空值(NULL)。
slide235
在C语言中,通常使用结构体的嵌套定义单向链表结点的数据类型,如学生纪录(结点)定义为:在C语言中,通常使用结构体的嵌套定义单向链表结点的数据类型,如学生纪录(结点)定义为:
  • struct SStudent
  • {
  • unsigned number;
  • char name[10];
  • int score;
  • struct SStudent * link;
  • };
slide236
特别注意该结构体类型SStudent中的link分量又是该结构体类型的指针,称为结构体的递归定义。因为链表中每个结点的link分量指向链表的下一结点,而链表中的结点均为结构体SStudent类型,所以link指针必须定义为结构体SStudent类型。特别注意该结构体类型SStudent中的link分量又是该结构体类型的指针,称为结构体的递归定义。因为链表中每个结点的link分量指向链表的下一结点,而链表中的结点均为结构体SStudent类型,所以link指针必须定义为结构体SStudent类型。
  • 链表是通过动态内存分配建立起来的,所以需要前面介绍的malloc()、calloc()和free()函数来动态申请存储空间。动态申请空间的语句如下:
  • struct SStudent * stu;
  • stu = (struct SStudent *) malloc(sizeof(struct SStudent));
slide237
7.1.2 链表应用实例
  • 【例7.1】学校建立学生成绩登记表(包括学号、姓名及成绩)的单向链表,学生记录按学号从小到大排序,要求实现对成绩信息的查找、添加、删除操作,并可以将数据保存为文件和从文件中读取数据。
  • 〖问题分析〗
  • 问题要求建立单向链表,在这个链表中每个结点包含学号、姓名及成绩三个元素及一个指向后继结点的指针,所以结构体类型包含4个成员。对成绩信息的查找、添加、删除、排序操作均为对链表的查找、添加、删除和排序操作,为了程序的模块化,需分别定义函数以实现相应功能。
  • (1) CreateTable() 和AppendToTable() 创建链表;
  • (2) PrintTable ()实现对链表的查找及输出功能;
  • (3)InsertToTable()实现按顺序插入新结点的功能;
  • (4)DeleteTable()实现从链表删除指定结点的功能;
  • (5)SortTable()实现对链表中的数据进行从小到大排序。
  • 以上函数均对链表操作,它们的入口参数均需给定链表首指针。
slide238
1.链表的建立
  • 利用函数CreateTable()创建链表首结点,首结点建立以后通过AppendToTable()函数在链表尾结点后增加新结点。
slide239
struct SStudent * CreateTable()
  • {
  • struct SStudent * stu;
  • stu = (struct SStudent *) malloc(sizeof(struct SStudent));
  • stu->number = 0;
  • stu->score = 0;
  • stu->name[0] = '\0';
  • stu->link = NULL;
  • return(stu);
  • }
slide240
void AppendToTable(struct SStudent * stu)
  • {
  • struct SStudent * next, * last;
  • int number;
  • last = stu;
  • while(last->link) last = last->link;
  • /*将指针last移到链表尾结点*/
  • printf("Please input the number (0 to quit): ");
  • scanf("%d", &number);
slide241
while(number)
  • {
  • next = (struct SStudent *) malloc(sizeof(struct SStudent));
  • next->number = number;
  • printf("Please input name: ");
  • scanf("%10s", next->name);
  • printf("Please input the score: ");
  • scanf("%d", &next->score);
  • last->link = next; /*将尾结点的link成员指向新结点首地址*/
  • last = last->link; /*将指针last移到链表尾结点*/
  • printf("\nPlease input the number (0 to quit): ");
  • scanf("%d", &number);
  • }
  • last->link = NULL;
  • }
slide242

student

stu

student

NULL

NULL

stu

  • 2.下面以PrintTable()函数为例说明查找过程

执行stu = stu->link 后指针stu向后移动一个结点

slide243
void PrintTable(struct SStudent * stu)
  • {
  • stu = stu->link;
  • if(!stu)
  • {
  • puts("The table is EMPTY!");return;
  • }
  • printf("number\tname\t\tscore\n");
  • while(stu)
  • {
  • printf("%3d\t", stu->number);
  • printf("%-10s\t", stu->name);
  • printf("%4d\t\n", stu->score);
  • stu = stu->link;
  • }
  • }
slide244

last

student

student

last

NULL

NULL

newstu

newstu

  • 3.插入结点

执行newstu->link=last->link后,

将待插入结点newstu与last->link结点连接

slide245

NULL

newstu

student

last

执行last->link=newstu后,

将第last结点与待插入结点newstu连接,

并last与原last->link结点断开。

slide246
void InsertToTable(struct SStudent * stu)
  • {
  • struct SStudent * newstu, * last;
  • int number,inserted=0;
  • printf("Please input the number (0 to quit): ");
  • scanf("%d", &number);
  • while(number)
  • {
  • newstu = (struct SStudent *) malloc(sizeof(struct SStudent));
  • newstu->number = number;
  • printf("Please input name: ");
  • scanf("%10s", newstu->name);
  • printf("Please input the score: ");
  • scanf("%d", &newstu->score);
  • last = stu;
slide247
while(last->link){
  • if(last->link->number >newstu->number)
  • { newstu->link = last->link;
  • last->link = newstu;
  • inserted=1;break;
  • }
  • else last = last->link;
  • }
  • if (inserted==0) /*待插入结点number比所有结点number大*/
  • {
  • newstu->link=last->link;
  • last->link = newstu;
  • }
  • printf("\nPlease input the number (0 to quit): ");
  • scanf("%d", &number);
  • }
  • }
slide248

student

next

temp

next

temp

student

NULL

NULL

  • 4.删除结点

执行next->link = temp->link后结点next与temp断开连接

slide249

next

temp

student

NULL

执行free(temp)后释放结点temp

slide250
void DeleteTable(struct SStudent * stu, unsigned number)
  • {
  • struct SStudent * temp, * next;
  • next = stu;
  • while(next->link)
  • {
  • if(next->link->number == number)
  • {
  • temp = next->link;
  • next->link = temp->link;
  • free(temp);
  • }
  • else next = next->link;
  • }
  • }
slide251
作业:
  • 1.自学7.2树
  • 2.自学7.3综合应用实例
  • 3.完成课后所有作业题
  • 4.实践教材重点难点解析与练习题