目录1
学习目标
1,学习程序逻辑结构,掌握设计程序的基本思路,
初步了解结构化程序设计方法;
2,学习一门编程语言,掌握 C程序的基本构成;
数据类型和表达式;
顺序,分支,循环语句;
数组,函数,指针;
结构共用体,位运算,文件目录2
教 学 目 录
数 组
函 数
预处理
指 针
结构体
位运算
文 件
程序设计基础
C语言概述
数据类型表达式
顺序结构程序设计
选择结构程序设计
循环结构程序设计目录3
第 2章 C语言概述
2-1 C语言发展特点 (了解)
2-2 C程序基本结构 (熟练掌握)
2-3 C程序上机步骤 (熟练掌握)
作业目录4
2-1 C语言简介
背景:
汇编语言可移植性差
其他高级语言不能对硬件操作
发展:
63年 CPL--67年 BCPL--70年 B语言 --73年 C语言
75年 C语言引起注意(写 UNIX 6)
78年 K&R合著,The Programming Language,(标准
C)
83年美国家标准化协会 ANSI制订 ANSI C--87 ANSI
目录5
C语言特点
允许直接访问物理地址,能进行位操作
语言简洁,使用灵活
数据类型丰富
运算符丰富
目标代码质量高
可移植性好(与汇编比)
结构化的控制语句
语法限制不严,程序设计自由度大概述目录6
2-2 C程序基本结构
几个简单例子
C 程序一般结构概述目录7
例 2.1 在屏幕上输出一句话
main() /*函数头 */
{ /* 函数体 */
printf(―How are you?\n‖);
}
运行结果:
How are you?
说明:该程序只包含一个函数(函数名为 main),函数体内只有一个语句。
目录8
例 2,2 已知圆的半径为 6,计算圆的周长和面积。
main()
{/*定义三个实型变量,分别表示半径、周长和面积 */
float r,length,area;
r=6; /*赋值:将 6送给 r*/
length=2*3.14*r; /*计算周长,结果送 length*/
area=3.14*r*r; /*计算面积,结果送给 area*/
printf("\n length=%f,area=%f\n",length,area);
/*输出周长、面积 */
}
目录9
说明:
该程序只由一个 main函数组成。
main函数的函数体中可包含多个语句。
根据语句的功能,该语句分为两类:
说明语句和可执行语句。
注释,/* …… */
目录10
main()
{ int n,s;
printf(―enter n:‖); scanf(―%d‖,&n); /*输入 n*/
s=sum(n); /*函数调用 */
printf(―\n the result is,%d‖,s ); /*输出结果 */
}
int sum( int m) /*函数定义 */
{ int j,s1=0;
for(j=1;j<=m;j++) /*求和 */
s1= s1 + j;
return(s1); } /*返回和值 */
例 2.3 求 1+2+3+4+…… +n( 其中 n由用户输入 )
目录11
一个完整的 C语言程序一般包含以下 4部分:
(1)编译预处理命令
(2)全局变量及函数说明
(3)main()函数
(4)用户自定义函数目录12
关于 C源程序结构的说明
1.程序的基本组成单位是函数一个程序可以由一个或多个函数组成。
函数有两类:库函数和用户自定义函数。
库函数是系统提供的,用户可直接调用,如求正弦的函数 sin( ),开平方函数 sqrt( )等。
对于库函数中没有的功能,需要用户自己编程实现,
即自己定义函数。
目录13
因为 C程序总是从 main函数开始执行,所以一个 C
语言源程序中只能 有且仅有一个 main函数 。
该函数在程序中的位置任意:
可以在用户自定义的函数前面,也可以在后面,
也可以在各个函数之间。
2,main函数目录14
3,函数的一般结构
C语言的函数由函数头和函数体两部分组成:
函数类型 函数名(形参表) 函数头
{说明部分 函数体可执行部分 }
其中:形参可以没有,但括号不能省略。如:
void line()
{ printf(―-------------------‖);}
特别提示,函数体中,说明语句必须在所有可执行语句之前目录15
C函数结构举例变量定义执行部分函 数 头函 数 体函数参数函数名函数类型
main ( )
{ int a,b,c;
scanf(“%d%d”,&a,&b);
c=max(a,b);
printf(“max=%d\n”,c);
}
int max(int x,int y)
/*求两数最大值 */
{int z;
if (x>y) z=x;
else z=y;
return(z);
}
目录16
4.语句
根据语句的复杂程度,可将 C语言中的语句分为 简单语句和复合语句 两大类。
简单语句,一般表示一个基本操作,以分号结束。
(其中 分号 是该语句的必要组成部分)
复合语句,用花括号 { }括起来的一系列语句,该语句以,{” 开始,以,}” 结束,末尾不需加分号。
目录17
5.源程序的书写格式
① 书写(或输入)源程序时,可以一行写一个语句,
也可以一行写多个语句,还可以一个语句分几行写。
因为各语句之间是以 分号 或 花括号 间隔的。
通常,一行写一个语句,以保证程序结构清晰。
② C语言中用“大括号对”即,{ }”来表示程序的层次结构目录18
6.注释程序中用 /*……*/ 给出注释,注释中可以是任意的内容。
位置,原则上,注释可以出现在程序中 允许出现空格的位置 ;实际编程时,一般放在函数之前或语句之后。
作用,编译时注释被忽略,因此注释对程序的运行结果不起作用。使用注释的目的是提高程序的 可读性目录19
7,C语言中,大小写字符不通用如 a 和 A 表示两个不同的符号。
注意培养良好的编程风格,如加注释、大括号的对齐、
适当的右缩等
(参见 P17例 2.5)
概述目录20
2,3 C源程序的上机调试步骤
Turbo C 上机调试包括:编辑、编译、连接、运行 4步
1,编辑,包括源程序的输入、修改、存盘。
所用命令,File /New,Save,Load,EDIT
2,编译,翻译成二进制并进行语法检查。
所用命令,Compile/Compile to OBJ
3,连接,目标程序模块连接,生成可执行文件。
所用命令,Compile/Link。
其中 2和 3可以合为一步 Compile/Make EXE file
4,运行,RUN/RUN,查看结果,RUN/User screen
目录21
C程序调试过程图示新建或打开文件编辑程序保存文件编译运行查看结果出错正确目录22
说明:
举例讲解:输入一源程序,演示上述过程的各个调试步骤。
将源程序改为错误的程序,演示调试、排错的过程。
概述目录23
作 业
1.课后上机练习题
2.习题 1,2
预习:
第三章 数据类型、运算符和表达式问题:
1,整型、实型、字符型变量所占内存空间的大小、表示数据的范围有何区别?
2.如何根据要处理的数据确定其类型?
3.为什么在处理数据之前要先确定其数据类型?
4,i++,++i有何区别?
概述目录24
第 1章 程序设计基础主要内容:
1、程序设计概述 (理解)
程序、程序设计语言的概念
程序设计的一般过程
如何学习程序设计
2、算法 定义,特点 与 描述 (熟悉)
3、结构化程序设计方法 (了解)
小结目录25
1.1 程序设计概述一、概念:
1、程序:程序是为解决某一问题而编写的语句序列。通俗的说,将解决一个实际问题的具体操作步骤用某种计算机语言描述出来,
就形成了程序。
目录26
例如:判断输入的任意整数 n能否被 3整除,程序如下:
main()
{ int n;
printf("\n enter a integer:");
scanf("%d",&n);
if( n%3==0)
printf("\n %d can be divided by 3",n);
else printf("\n %d can't be divided by 3",n);
}
目录27
2、程序设计语言:即计算机语言分类:
机器语言:用计算机能直接理解和执行的,0” 和,1”
表示各种操作的程序设计语言,很难使用。
汇编语言:用助记符号来表示各个基本操作的程序设计语言,如 add r1,4
高级语言,用接近自然语言和数学语言的语法符号描述基本操作的程序设计语言目录28
二、程序设计的一般过程
用计算机解决问题的基本过程如图 1.1所示:
目录29
程序设计基本步骤
(1)分析要解决的问题,明确任务。
即分析要处理的数据是什么,从哪里来,作怎样的处理,结果送往那里。
例如:求学生的平均成绩。
分析该问题,明确它有三项功能:
1、输入学生成绩 2、求平均成绩 3、输出结果;
要处理的原始数据为:学生的成绩;
要进行的处理为:求平均;
结果为:平均值,送屏幕显示或保存于文件中。
目录30
( 2)分析问题,建立数学模型,并选择合适的解决方案 ;
例如:求平均成绩的处理过程,可以抽象为:计算一批数据的平均值。
( 3)确定数据结构和算法,数据结构即数据的组织方式,
算法是对数据处理过程的具体描述。
只有先确定了数据结构,才能设计相应的算法。
例如:要处理的原始数据为学生成绩,如何组织,如何在计算机中表示?
对于简单问题,前三步可看作一步,即分析问题、设计算法。
目录31
(4)编码:即编写程序 。
用某种计算机语言将上一步设计好的算法描述出来,
就是程序。
可见,算法是编程的基础。
(5)调试程序:将源程序送入计算机,进行排错、试运行,
调试的结果是得到一个能正确运行的程序。
通常,调试程序至少占整个程序设计工作量的一半。
(6)整理资料,交付使用目录32
三、学习程序设计的方法
1、一讲、二练、三思考
2、多读源程序、多编写程序、多上机调试
3、忌上课只听不记、忌,纸上谈兵,,忌课下不练习具体要求:
1、上课有重点、有选择的记 ;
2、上机有准备:准备好课本、笔记、作业等
3、除课后习题必须全部解决外,多做一些二级考试的模拟题
4、做一个自己感兴趣的完整的程序(小项目)
第一章目录33
N.Wirth公式:
程序 =数据结构 +算法 +语言环境
1-2 算法
定义,算法是指为解决一个问题而采取的方法和步骤。
例如:
复习算法、乐谱、看病算法等
算法有优劣目录34
简单算法举例
例 1-1 计算任意长方形的面积
例 1-2 计算 1x2x3x4x5=?
例 1-3 计算 1-1/2+1/3-...+1/99-1/100=?
例 1-4 判断一个数是否素数第一章算法描述目录35
计算任意长方形的值
问题分析:
输入长和宽
计算面积 =长 X宽
输出面积
数据存放:
长 -len,宽 -wid,面积 -area
设计算法:
输入 len和 wid的值;
计算 area =lenXwid;
输出面积 area的值;
目录36
计算 1x2x3x4x5
方法一:数学思路
方法二:利用变量 P存放中间结果
S1,P=1/ I=2
S2,P=P x I
S3,I=I+1
S4,如果 I>5 转 S5
否则 转 S2
S5,打印 P的值
P=1
1x2 P
Px3 P
Px4 P
Px5 P
打印 P的值
P=1 / I=2
P x I P / I=I+1
P x I P / I=I+1
P x I P / I=I+1
P x I P / I=I+1
当 I>5 时打印 P的值目录37
1-1/2+1/3-...+1/99-1/100=?
提示:
首先考虑实现 1+2+3+4+… +100
其次考虑实现 1/1+1/2+1/3+1/4+...+1/100
最后考虑实现 1/1-1/2+1/3-1/4+...+1/100
思路:
S1,sum=0,I=1
S2,SUM=SUM+ I
S3,I=I+1
S4,如果 I<=100则转 S2 否则转 S5
S5:打印 SUM的值
1/ I
,P=1
P*1/I
,P=-P;
目录38
判断一个数是否素数分析:
本题要点是先搞清楚什么是素数。
思路:
输入一个大于 3的正整数 N
定义一个变量 I 从 2~N-1,I 每取一个值做:
如果 N能被 I整除则中断循环,N不是素数;否则 I
继续取下一个值;
若当 I取完所有值都不能整除 N,则 N是素数一个正整数,若不能被除 1和它本身以外的任何整数整除,则是素数。
目录39
算法的特性
有穷性指一个算法经过有限步骤后停止(或在合理范围内)
确定性算法的每一步都应当是确定的,无歧义的
有效性算法的每一步计算机都能有效执行
有 0个或多个输入一个算法可以没有输入,即执行时无需输入信息
有 1个或多个输出一个算法必须有输出,运算过程或运算结果目录40
算法的描述描述算法的方法有多种,常用有:
自然语言 (描述不够严格,容易出现歧义性)
结构化流程图 * (同一问题的 流程图 不唯一)
N-S图 * (结构清晰,但难于修改)
PAD图 (结构清晰,唯一性好)
伪代码 * (灵活,但需要有一定的程序设计基础)
学习建议:
流程图或 N-S图一定要熟练掌握,伪代码表示法在学习完基本的流程控制语句后也经常使用。
目录41
流程图
流程图是用几种图形、箭头线和文字说明来表示算法的框图。
流程图中规定使用的符号如图 1.2所示
用流程图的优点是:直观形象、易于理解,能将设计者的思路清楚地表达出来,便于以后检查修改和编程。
目录42
例如:计算 1+2+3+…… +100=? 画流程图
S=0,I=1
I<=100
输出 S
N
开始结束
S=S+I
I=I+1
Y
目录43
S=0,I=1
S=S+I
I=I+1
I>100
输出 S
Y
N
开始结束例如:计算 1+2+3+…… +100=? 画流程图练习目录44
结构化流程图三种基本结构:
顺序结构
选择结构
循环结构说明举例练习
A
B
P
Y
A
N
当型循环 直到型循环
P
A B
Y N
A
N
P Y
目录45
三种基本结构说明
1,循环结构:
当型循环 先判断后执行 ;
即当条件 P成立时重复执行循环体 A;
直到型循环 先执行后判断 ;
即重复执行循环体 A直到条件 P成立结束;
2,三种基本结构特点:
1,只有一个入口和一个出口;
2,结构内每一部分都有机会执行到;
3,结构内不存在,死循环,
N-S
目录46
求 N! 流程图输入 N
N?0
S=1
打印 ERROR
NY
I=1
I?N
S=S*I/I=I+1
Y
N
打印 S
目录47
N-S图
三种基本结构:
顺序结构
选择结构
循环结构说明举例练习
A
B
T P F
A B
当 P成立
A
A
直到 P成立当型循环 直到型循环目录48
求 N! N-S图输入 N
N?0Y N
S=1,I=1
当 I<=N
S=S*I
I=I+1
输出 S 输出 ERROR
第一章目录49
1-3 结构化程序设计方法
总的原则:
自顶向下,逐步细化,模块化设计,结构化编码
也就是说:
当面临一个复杂问题时,先进行整体设计,将问题分为几个大模块;
然后再对每个模块进一步分解为较小模块;
每个模块还可再分,直到不能再细分为止。
设计思路:
给定问题,先搞清楚已知哪些数据(输入),需要什么结果?(输出)
然后再分析如何解决。
第一章目录50
本章小结
本章介绍程序设计及算法的基本知识
重点:
分析问题,设计算法
习题:
P12习题第 3,4题第一章目录51
第 3章 数据类型、运算符和表达式主要学习内容:
1,数据类型
2,常量和变量
3.基本数据类型,
整型、实型,字符型
4,最常用的 运算符和表达式,
算术运算符和算术表达式
赋值运算符和赋值表达式
逗号运算符和逗号表达式目录52
引 入
假设某任务中有如下数据需要处理,1024,65536、
123.5,123.456789e10,'a',"name"
设计算法前,需要先定义数据结构:即如何在计算机中表示这些数据?
编程时,函数的说明部分就是对数据结构的描述,可执行部分是对算法的具体描述。那么,在 C语言中如何描述数据结构呢? —— 数据类型
数据结构描述完毕,然后是算法的 C语言描述了。算法是对数据处理过程的描述,数据处理过程许多基本操作是如何实现的呢? ——运算符和表达式目录53
3.1 C的基本语法单位
1、字符集 -包括字母、数字和特殊符号;
2,关键字 -C系统保留字;
3,标识符 -用户为自定义变量、函数等所起的名字;
标识符命名规则:
( 1)由字母、数字及下划线组成,且不能以数字开头。
( 2)不能与关键字同名
( 3)尽量,见名知义,
例如:下列标识符,是否合法?为什么?
area if score student_no 2day
month_3 int _sum no*3 &aa
数据类型目录54
3.2 常量和变量例 1:输入任一半径,计算球的体积。
#define PI 3.1415926 /*定义符号常量 PI */
main( )
{ float r,v ; /*定义变量 r,v*/
printf("\n enter r:");
scanf("%f",&r);
v = PI*r*r*r*4/3; /*3,4 是常量 */
printf("\n volume,%f ",v);
}
目录55
分 析例题中处理的数据有两类:常量和变量
1,常量:在程序的运行过程中其值不能被改变的量。
如程序中的 3,4
2,符号常量:用一个标识符代表一个有特定含义的常量如 PI
3,变量:在程序的运行过程中其值可以被改变的量。
如 r,v
目录56
符号常量名一般用大写,变量名用小写;
选择标识符应“见名知义”;
变量必须先定义,后使用;
C中要求所有变量“先定义,后使用”,目的是:
(1)保证程序中变量名使用正确;
(2)系统编译时根据变量的类型为其分配合适的存储单元。如 系统给 r,v 各分配大小为 4B 的单元
(3)编译系统根据变量的类型进行语法检查。
如 r %5 是非法的表达式
提示:
数据类型目录57
3.3 基本数据类型
主要掌握 整型、实型、字符型 三种类型的:
1) 常量表示
2) 变量的定义
3) 所占存储空间的大小
4) 取值范围目录58
用于表示整数
所占内存空间:随机器而异,一般微机上一个整型数据占 2Byte 的内存空间。
整型的可用修饰符:
int
short [int]
long [int]
unsigned int
unsigned short
unsigned long
一,整型:
目录59
整型变量的定义:
类型说明符 变量名表;
如,int a,c,no;
long s;
unsigned x,y;
整型常量:
十进制整数,12 -3 0
八进制整数,024 056
十六进制整数,0x3A -0X34
long int,12L 0L
目录60
整型变量内存中的存放形式
(在内存中占 2个字节)
正数以原码存放;负数以补码存放;最高位为符号位,
0表示正数,1表示负数 ;
如:
10
-10
01010000
10101111
00000000
11111111
11111111 01101111
取反加 1
目录61
问题,要定义两个变量 x 和 y,分别用于存放数据 1024
和 65536,如何确定 x和 y类型?
分析:首先,要处理的数据为整型;
其次,根据数据的大小确定其具体类型,1024可用 int
型变量保存,65536必须用 long int 型变量才能保存。
main( )
{ int x; long y ;
x=1024; y= 65536 ;
printf("\n x=%d,y=%ld",x,y);
}
说明:若 x,y 均定义为 long 型,程序运行如何?
若 x,y均定义为 int 型,程序运行结果如何?
数据类型目录62
二、实型
Float,单精度浮点型
Double,双精度浮点型
long Double,长双精度浮点型
所占空间大小,
float 型数据占 4B,double型数据占 8B
实型变量的定义:
float r,v ;
double z,a,x ;
float result ; 等目录63
实型常量:
( 1)小数形式,可由正负号、小数点和数字 组成。
如,12.35 -78.09 0.789,145
(2) 指数形式,用科学计数法表示的实数。
如,44e –3 0.34e12
问题,若程序要处理的数据为,123.5 和 123.456789e10
设该数据分别用变量 a,b 保存,试问 a,b的类型如何确定?
分析:
首先该数据均为实型; 其次,根据数据的大小及精度,可确定 a 为 float型即可,b必须为 double型。
目录64
程序如下:
main( )
{ float a; double b;
a=123.5; b=123.456789e10 ;
printf("\n a=%f,b=%f",a,b) ;
}
运行结果:
a=123.500000,b=1234567890000.000000
若改为,float b; 运行结果如下:
a=123.500000,b=1234567954432.000000
数据类型目录65
1,字符常量:
字符常量:用单引号括起来的一个字符;如
‘ A‘ ‘%‘
转义字符:以,\” 开头的特殊字符,含义见 P33表
3-3;
2,字符变量:
一个字符变量只能存放一个字符常量;
定义形式,char c1,c2;
存储空间,1个字节;如 c1=?a‘;
3,字符存储及使用:
实际存是字符的 ASCII码;如,?a‘
10000110
三、字符型目录66
4,字符串
用双引号括起来的字符序列,如 "a","123\n"
注:字符串存储时,系统自动在字符串的末尾加上字符串结束标记 '\0'
比较字符常量 'a'与字符串常量 "a",
'a' "a"
97 97 0
目录67
例 3.5 定义两个变量 c,d,用于保存字符 'a','b',然后按字符和数值两种形式输出变量 c,d的值。
main( )
{ char c,d ;
c= 'a' ; d='b' ;
printf("\n %c %c",c,d);
printf("\n %d %d",c,d);
}
结果,
a b
97 98
若将第三行改为,c=97; d=98; 结果如何?结论:
在字符的 ASCII码( 0~255)内字符与整数可通用。
数据类型目录68
3.4 变量的初始化(自学)
如 int r=6; 等价于
int r ; r=6;
应用:
编程时,当定义变量时就已知该变量的值,则可以使用初始化的方式给变量赋值。
注意:
int i=j=k=0; 是错误的。正确的应为:
int i=0,j=0,k=0;
数据类型目录69
3.5 运算符和表达式
运算符,规定了对数据的基本操作。
主要掌握:各运算符的功能、优先级和结合方向。
本章只介绍最常用的运算符:
算术运算符、赋值运算符 及其表达式目录70
3.5.1 算术运算符表达式
基本算术运算符
1,+ ( 加法运算符,或正值运算符 。 如 3+5,+3)
2,- ( 减法运算符,或负值运算符 。 如 5-2,-3)
3,* ( 乘法运算符 。 如 3*5)
4,/ ( 除法运算符 。 如 5/3)
5,%( 求余运算符,%两侧均为整型,如 7%4为 3) 。
说明,双目运算符,即要求有两个操作数。
,/” 两边都为整数时作整除;如 5/2=2
目录71
1,用算术运算符和括号将运算对象连接起来的符合 C语法规则的式子,称 C算术表达式 ;
如,a+b*c-5.5/d+‘a‘
2,优先级:表达式求值时,按运算符的优先级别高低次序执行 ;
3,结合性:运算符的优先级别相同,按自左至右的结合方向运算(左结合);
4,如果一个运算符的两侧的数据类型不同,则先自动进行类型转换,然后进行运算。
算术表达式目录72
++i,--i 使用 i之前,先使 i的值加 ( 减 ) 1;
i++,i-- 使用 i之后,再使 i的值加 ( 减 ) 1;
如果 i=3,则执行语句:
① j=++i; j值为 4;
② j=i++; j值为 3;
注意:
1,++和 ―― 只能用于变量,不能用于常量或表达式 。
2,++和 ――的结合方向是,自右至左,。
如,printf(―%d‖,-i++),输出为 - 3。
自增、自减运算符:
目录73
例如:
main()
{ int i=6,j=6,k=6,h=6,m,n,x,y;
m=i++; n=++j; x = k--; y= --h;
printf("\n i=%d,m=%d,j=%d,n=%d",i,m,j,n);
printf("\n k=%d,x=%d,h=%d,y=%d",k,x,h,y);
}
运行结果:
i=7,m=6,j=7,n=7
k=5,x=6,h=5,y= 5
目录74
前缀,n=++j; 等价于 j=j+1; n=j;
( j先加 1,后参与表达式的运算)
后缀,m=i++; 等价于 m=i; i=i+1;
( i先参与表达式的运算,然后加 1 )
结论,
目录75
算术表达式的计算,
当一个表达式中包含多个算术运算符时,如何计算呢?
表达式的计算方法:
按优先级由高到低进行,相同优先级的运算符按结合方向计算。例如计算,4 + 5*8/10 – 12%5/2
算术表达式的书写 要求自学
注意:表达式中的符号均写在同一行中,不能有上标或下标。
合理运用括号,保证原数学表达式的运算顺序。
数据类型目录76
3.5.2 类型转换
1,当参加运算的两个操作数类型不同时,先将,低,类型的数据转换为,高,类型,再计算。 ——隐式转换
(系统自动进行的转换)
2,当需要将某数据转换成指定类型时使用 ——强制类型转换 。
目录77
char,shortint
unsigned
long
double float
低高
图中横向向左的箭头表示必定的转换 ;纵向箭头表示运算对象为不同类型时转换的方向 。
1、隐式转换 (自动进行的转换 )
目录78
2、强制 类型转换一般形式为,(类型名)(表达式)
例如,(double)a (将 a转换成 double型 )
(int)(x+y) (将 x+y的值转换成整型 )
说明:强制类型转换得到一个所需类型的中间变量,原来变量的类型未发生变化 。
目录79
例 3.8 含有牵制类型转换的表达式的计算。
main()
{ int a=2,b=3;
float x=3.5,y=2.5,z;
z=(float )(a+b)/2 + (int)x%(int)y;
printf("\n %f",z);
}
运行结果:
3.500000
数据类型目录80
3.5.3 赋值运算符和表达式赋值,给变量提供数据的一种方法。
1,赋值表达式,变量 = 表达式作用:先计算,=”右边表达式的值,然后将其送给,=”
左边的变量。
如 area = r*r*3.14
s=0
优先级,比算术运算符低,仅高于逗号运算符。
结合性,自右向左目录81
2,复合赋值表达式,在赋值符,=”之前加上其他运算符。
例如:
a+=3 等价于 a=a+3
x*=y+8 等价于 x=x *(y+8)
说明:
1,赋值表达式也可以包含复合的赋值运算符 。
2,凡是二元运算符都可与赋值符一起组合成复合赋值符 。
若定义,int a=2; 则表达式 b=c=a+4 的计算过程为:
先计算,c=a+4 得该表达式的值为 6 ;
然后计算,b=6 得该赋值表达式的值为 6。
目录82
3,赋值运算中的类型转换:
当赋值号,=” 右边表达式的值与,=” 左边的变量的类型不同时,先将,=” 右边表达式的值转换成,=” 左边变量的值,然后再赋值 。
例如,int n=4; float s;
s = n*5;
结果类型,float
数据类型目录83
3.5.4 逗号运算符
用逗号运算符将两个表达式连接起来称为逗号表达式 。
一般形式为:表达式 1,表达式 2
如,3+5,6+8
逗号表达式的计算:自左向右依次计算各表达式
逗号表达式的值:最右边一个表达式的值如,for( j=0,s=0,p=1; j<10; j++)
{s + =j; p*=j; }
注意:并不是任何地方出现的逗号都是作为逗号运算符 。
例如,printf(―%d,%d,%d‖,a,b,c);
目录84
作 业上机练习,1
习题 1,3,5,6,7
预习:第四章 顺序结构程序结构要求明确:
Getchar() putchar() scanf() printf() 的使用数据类型目录85
顺序结构程序设计
C语句概述
输入输出概述
字符输入输出
格式输出 printf
格式输入 scanf
顺序程序设计
要求:熟练掌握用合适的格式输入 /输出不同类型的数据;会编写简单的程序。
目录86
C语句概述顺序结构
C基本语句:
1,表达式语句:在一个表达式的后面加上分号即构成表达式语句。例如 x=0; k++; printf("\n welcome");
2,流程控制语句:用来控制程序中语句的执行顺序。
C语言中提供了 9种流程控制语句;
If~else /switch /for()~ /while()~ /do~while()
/continue /break /goto /return
目录87 顺序结构例,重复( 10次)输入两个整数,输出两数之和。
main()
{ int a,b,c,n;
for(n=1; n<=10; n++) /*for语句控制重复 10次 */
{ printf("\n input 2 integers:");
scanf("%d%d",&a,&b);
c=a+b;
printf("sum,%d\n",c);
}
}
3,复合语句,用一对花括号 { }括起来的多个语句序列,
有时称为分程序 。
目录88
输入 /输出概述
1,C不提供输入输出语句,输入输出操作由函数实现 。
printf函数和 scanf函数不是输入输出语句,而是
C标准函数;
可以使 C编译系统简单化,避免在编译阶段处理与硬件有关问题 。
2,使用 C库函数时,必须将该函数所在头文件 (,h) 包含到源文件中 。
如,#include ―stdio.h‖
顺序结构目录89
字符数据输入输出
1,putchar函数 ( 字符输出函数 )
格式,putchar(字符型数据 );
功能:向终端输出一个字符;
说明:
函数参数可以为 char或 int型常量,变量,表达式;
也 可 以 输 出 控 制 字 符 或 其 他 转 义 字 符,如
putchar(\n); putchar(\015);
如在函数中调用 putchar()函数,应在函数前加上包含命令,#include ―stdio.h‖
顺序结构目录90
例:输出三个字符,A*B。
#include ―stdio.h‖
main()
{ char c1; int c2;
c1='A'; c2=66; /*给变量 c1,c2赋值 */
putchar( '\n' ); /*输出字符 '\n'*/
putchar(c1); /*输出变量 c1中的字符,'A'*/
putchar( '*' ); /*输出字符 '*' */
putchar(c2); /*输出变量 c2中的字符,'B'*/
}
目录91
字符数据输入输出
2,getchar函数
格式,getchar();
功能:从键盘输入一个字符;
注意:
getchar()只能接收一个字符;
如在函数中调用 getchar()函数,应在函数前加上包含命令,#include ―stdio.h‖
目录92
例,输入两个字符,输出每个字符及其 ASCII码。
#include "stdio.h"
main()
{ char c,d;
printf("\n enter two characters:");
c=getchar( );
d=getchar( );
printf("%c:%d\n",c,c); /*输出 c字符及 ASCII码 */
printf("%c:%d",d,d);
}
顺序结构目录93
格式输入 printf()函数
1,格式,printf(格式控制,输出表列 );
其中:
1),格式控制,是用双引号括起来的字符串,包括:
格式说明,由 %和格式字符组成,作用是将输出项转换为指定格式输出 。 如 %d,%f
普通字符,格式控制串中除格式说明外的其他字符,
是需要 原样输出 的字符 。
2),输出表列,是需要输出的一些数据,可以是常量,
变量,函数,表达式等 。
目录94
如:
a=5;b=7;
printf(―%d,%d‖,a,b);
printf(―a=%d,b=%d\n‖,a,b);
如:
int a=3; float b=5;
printf("\n enter name:");
printf("\n a=%d,b=%f ",a,b);
printf("%f",a*b);
顺序结构目录95
main()
{ char ch='Y';
/*定义一个字符数组 s,并保存字符串 "very good"*/
char s[ ]="very good";
printf("\nch,%c,s,%s",ch,s);
}
运行结果,
ch,Y,s,very good
程序中,输出项 ch,s与控制项 %c,%s一一对应,且类型匹配 。
例:输出字符和字符串 。
目录96
格式输出 scanf()函数
1,scanf(格式控制串,地址表列);
例如,scanf("%d%f ",&a,&b);
其中:
1),格式控制,是用双引号括起来的字符串,包括:
格式说明,由 %和格式字符组成,作用按指定格式输入 。 如 %d,%f
普通字符,格式控制串中除格式说明外的其他字符,
是需要 原样输入 的字符 。
2),地址表列,是需要输入的变量地址,&表示取地址 。
目录97
格式输出 scanf()函数
3,注意事项 P55:
1) 格式控制后应当是变量地址,不是变量名;
2) 若格式控制串中包含其他字符,输入时也应包含它们;如:
scanf(―a=%d,b=%d‖,&a,&b);
输入时应输入,a=5,b=7回车
3) 用 %c格式时,空格和转义字符都认为有效;如:
scanf(―%c%c‖,&c1,&c2);
输入,a b 则 c1=?a‘,c2=;
目录98
例,用 scanf输入整型数据,实型数据 。
#include <stdio.h>
main()
{ int a,b; float x,y;
scanf("%d%d",&a,&b);
scanf("%f,%f",&x,&y);
printf("\na=%d,b=%d,x=%f,y=%f",a,b,x,y);
}
运行结果,
12 24↙ ( 12与 24之间用空格分开)
15,30↙ (注意两数间的逗号 )
a=12,b=24,x=15.000000,y=30.000000
目录99
例:输入某商品的编号(整型)、类别( A~Z字符型)、价格(实型),并输出该信息。
#include <stdio.h>
main()
{ int num ; char class ; float price ;
printf("\n enter number(A~Z):");
scanf("%d",&num); getchar( );
printf("\n enter class,");
class = getchar( ); getchar( );
printf("\n enter price,");
scanf("%f",&price); getchar();
printf("no=%d,class=%c,price=%6.2f",no,
class,price);
}
目录100
混合输入有两种处理方法:
一是在一个 scanf()函数中实现所有数据的输入;
二是 将数值与字符分别输入,而不是用一个 scanf()函数。
通常采用第二种方法,用第一种方法输入时容易出错。
本程序中分别用 scanf()和 getchar()输入编号、价格和类别。
并且每次输入后用 getchar() 读取多余的回车符,以免影响后面的正确输入。
顺序结构目录101
顺序程序设计举例顺序结构例 1,交换两个整型变量的值。
分析:首先定义程序中要用到的变量,设两个整型变量为 a,b。 然后设计算法:①输入两个整数 a,b ② 交换
a,b的值 ③输出交换后的 a,b的值。
其中,实现② 的方法有两种:
一是用第三个变量 c暂存其中一个数,如程序 1。
二是不使用第三个变量,利用其中一个变量保存两数之和,通过减法来实现,如程序 2。
目录102
程序 1:
main()
{ int a,b,c;
printf("\n enter 2 integers to a,b:");
scanf("%d%d",&a,&b);
c=a; a=b; b=c; /*交换 */
printf("\na=%d,b=%d",a,b);
}
运行结果,
enter 2 integers to a,b:4 6↙
a=6,b=4
思考,若将三个交换语句的顺序调整,结果如何?
目录103
程序 2:
main()
{ int a,b,c;
printf("\n enter 2 integers to a,b:");
scanf("%d%d",&a,&b);
a=a+b ; b= a - b ; a=a – b; /*交换 */
printf("\na=%d,b=%d",a,b);
}
运行结果同上。
目录104
例 2,输入一个数字符号,并转换成相应的整数输出。
如读入字符 '8',转换成整数 8输出。
分析:首先定义程序中要使用的变量,设字符变量 ch 存放读入的字符,整型变量 d存放转换后的整数。
然后设计算法:
①输入一个字符给 ch
② 将字符 ch转换成对应的数值 d
③ 输出 d。
第二步转换利用 ASCII码的编码规律,‘ 0’,‘ 1’,
'2' … '9'的 ASCII码值是连续的,即 48,49,50…… 57。
只要将数字字符与字符 '0'相减,结果就是该字符对应的数值。
如 '8'- '0'值为 8。
目录105
程序:
#include <stdio.h>
main()
{ char ch; int d;
printf("\n enter a character(0~9):");
ch=getchar( ); /*读入一个字符并赋给 ch */
d=ch -'0';
printf("d=%d",d);
}
目录106
作 业
P57
1,上机练习 1~4
2,习题 1,3
顺序结构目录107
选择结构程序设计关系运算符表达式逻辑运算符表达式
IF语句
SWITCH语句程序举例根据选择结构的组成特点,分析问题时着重明确以下两点:
1) 条件是什么
2) 条件成立和不成立时分别执行什么操作 。
目录108
关系运算符与关系表达式
所谓,关系运算,实际上是,比较运算,,若条件满足,
则关系表达式的值为 ture(1); 否则,关系表达式的值为 false(0)。
1.关系运算符,
< <= > >= == !=
例如下列关系表达式,
a>b 表示条件,a 大于 b―,
x! =0 表示条件,x 等于 0“,
a+b<x*y 表示条件,a+b 小于 x*y―。
目录109
关系运算符 优先次序算术运算符 > 关系运算符 > 赋值运算符且 < <= > >= 高于 == !=
例如:
c>a=b 等效于 c>(a=b)
a==b<c 等效于 a==(b<c)
a=b>c 等效于 a=(b>c)
算术运算符关系运算符赋值运算符低高目录110
2、关系表达式
用关系运算符将两个表达式连接起来的式子 。
如 a>b,a>=80
关系表达式的值是一个 逻辑值,即,真,或,假,。
如,5==3值为假,5>=0值为真 。
说明:
1) 表达式可以是算术或关系,字符等表达式 。 如
a+b>b+c,’a’<’b’,a>(b<c)
2) C语言 以 1代表,真,,以,0” 代表,假,,如:
d=5>3 d的值为 1。
目录111
关系运算符,==”与赋值运算符,=”不同。
比较,k=1 与 k==1
表达式 a+b>d>c也是合法的关系表达式,但在实际应用中很少使用;因为它不能表示,a+b大于 c且大于 d。
问题,
如何表示,a < x < b
0<y<10 等条件特别提示选择结构目录112
逻辑运算符与逻辑表达式
逻辑运算符
&& 逻辑与 ( 相当于其他语言中的 AND)
|| 逻辑或 ( 相当于其他语言中的 OR)
! 逻辑非 ( 相当于其他语言中的 NOT)
逻辑运算规则:
a&&b 只有当 a,b都为真时,a&&b才为真,否则为假 。
a||b 若 a,b之一为真,则 a||b为真 。 ( 或只有当 a,b都为假时,a||b才为假,否则为真 )
!a 若 a为真,则 !a为假 。
目录113
逻辑运算符与逻辑表达式
优先次序 (见图 ):
算术运算符关系运算符赋值运算符低高 !运算符
&&.||运算符目录114
逻辑运算符与逻辑表达式
逻辑表达式
1) 逻辑表达式的值应该是一个逻辑量,真,或,假,。
2) 注意,C在 给出运算结果 时,0代表假,1代表真,
但在 判断一个量是否为,真,时,0代表假,非 0代表,真,。
例如,
if ( !x ) 等价于 if( x==0)
4&&0||2=?
目录115
特别提示
在逻辑表达式求解中,并不是所有的逻辑运算符都被执行 。
a&&b 只有 a为真 ( 非 0) 时,才需要判别 b的值 。
a||b 只要 a为真 ( 非 0),就不必判断 b; 只有 a为假,
才判别 b。
如当 a=1,b=2,c=3,d=4,m=n=1时,
执行 (m=a>b)&&(n=c>d); 后 n=?
选择结构目录116
分支语句
在程序的三种基本结构中选择(分支)结构,根据条件的取值不同分别执行不同的操作。
C语言提供了两种语句分别用于实现二分支和多分支。
分别是 IF 语句 和 SWITCH语句
介绍这二种语句的语法结构和使用方法。
选择结构目录117
5.3 if 语句
if语句能实现所有的选择结构
例 5.5 将任意两个整数 m,n中的较大数送给 x,较小数送给 y,然后按大小顺序输出 (设 m,n的值由键盘输入)
分析:共分三步,
S1,输入两个整数给 m和 n。
S2,判断 m,n的大小并分别给 x和 y 赋值。
S3,输出 x和 y。
其中第二步用选择结构,
目录118
main()
{ int m,n,x,y;
printf(―\n please enter two integers,‖);
scanf(―%d%d‖,&m,&n);
if(m>n) {x=m; y=n; } /*复合语句 */
else
{x=n; y=m;}
printf(―\n x=%d,y=%d‖,x,y);
}
思考:若条件改为,m<n,如何实现?
目录119
1,If语句的一般形式:
if( 表达式 )
语句 1
else
语句 2
执行过程,(如右图 )
格式说明,
语句 1 和语句 2 在语法上是一个语句,可以是简单语句,也可以是复合语句。要特别注意复合语句中一对 { }的正确使用 。
条件表达式语句 1 语句 2
Y N
目录120
2,if 语句的简化形式例 5.6 从键盘接收一个字符,若为大写字母,则将其转换成小写字母并输出;否则输出原字符。
分析:判断 ch是大写字母的条件是,ch 在 A~Z之间
#include <stdio.h>
main()
{ char ch;
printf(―\n enter a character:‖);
ch=getchar(); getchar();
if( ch>=’A’ && ch<=’Z’) /*判断并处理 */
ch=ch+32;
putchar(ch);
}
目录121
if语句的简化形式
if( 表达式 ) 语句 1
执行过程如图:
例:输入任意数 n,输出其绝对值。
main()
{ float a;
scanf(―%f‖,&a);
if(a<0) a=-a;
printf(―\n |a| is:%f ‖,a);
}
条件表达式语句 1
Y N
目录122
3,if语句的嵌套
当 if语句中的语句 1或语句 2又是 if语句时,
就构成了 if语句的嵌套
嵌套的 if语句能实现多分支结构。
目录123
假设 m是一个整型数 15,让用户从键盘上输入所猜数字
(用户已知数的范围是 10~20),若猜对,则输出
,RIGHT‖,否则,输出” WRONG―,并指出所猜的数比
m大还是小。
分析:
第一步,输入猜测的数 n。
第二步,判断并输出结果:条件可以是,m等于 n‖,若条件成立,则输出 RIGHT; 否则,需执行两步操作:①输出
WRONG,② 判断 m,n的大小:条件可以是,n>m‖; 条件成立时输出,big‖; 否则输出,small‖。
例 5.7 猜数游戏:
目录124
main()
{ int m=15,n;
printf("\n guess a num:");
scanf("%d",&n); /
if( m= =n) printf(" RIGHT"); /*语句 1*/
else
{printf("\n WRONG");
if( n>m) printf(― BIG‖); /*语句 2*/
else printf(" SMALL");
}
}
目录125
例 5.8某幼儿园只接收 2~6岁的小孩,其中 2~3岁编入小班,4~5岁编入中班,6岁编入大班。编程实现,对输入的任意一个年龄,输出该编入什麽班,或者告知“不收
“。
分析:首先根据条件“年龄在 2~6岁之间”判断是“收”
还是“不收“,若条件满足,则执行的操作为:( 1)判断该编入何班;否则,输出“不收”。
其次,分析( 1)如何实现。
提示:本例可用不同的程序实现。
如将条件改为“年龄超出 2~6岁”;或者从条件“年龄小于 2”开始判断。试编程实现。
目录126
main()
{int age;
printf(―\n enter age:‖);
scanf(―%d‖,&age);
if(age>=2&&age<=6)
if(age<=3) /*语句 1开始 */
printf(―\n small class‖); /*语句 1_1*/
else
if(age<=5) /*语句 1_2*/
printf(―\n middle class‖);
else
printf(―\n big class‖); /*语句 1结束 */
else
printf(―\n can not accept‖); /*语句 2*/
}
目录127
If语句中的语句 1和语句 2都可以包含 if语句。
If语句的嵌套结构中,因为 else 是可选项,所以要注意 if与 else的匹配。
C语言规定:
else总是与其前面最近的、还没有匹配过的 if相匹配,
并且是由里向外逐对对匹配。编程时,可以通过合理使用 { }来明确 if与 else匹配关系。
目录128
例 5.10:
分析以下两个程序,哪一个能实现:从键盘上接收一个整数,若该数为正偶数,则输出,positive
and even.‖; 若为负数,则输出,negative‖。
提示:可画出流程图,即可看得很清楚。
目录129
程序( 1):
main()
{ int n;
printf(―\n enter a integer:‖);
scanf(―%d‖,&n);
if(n>0)
if(n%2==0)
printf(―positive and even.\n‖);
else
printf(―negative\n‖);
}
目录130
程序( 2):
main()
{ int n;
printf(―\n enter a integer:‖);
scanf(―%d‖,&n);
if(n>0)
{if(n%2==0)
printf(―positive and even.\n‖);
}
else
printf(―negative\n‖);
}
目录131
4.条件运算符
使用 if语句时,有时是根据条件给同一变量赋不同的值
比如:
main()
{ int x,y;
scanf(―%d‖,&x);
if(x>0) y=x+1;
else y=x*x+1;
printf(―\n x=%d,y=%d‖,x,y);
}
用 C语言提供的条件运算符描述,比用 if语句更简练。
上述 if语句等价于:
y= x>0? x+1,x*x+1;
目录132
条件表达式的一般格式:
表达式 1?表达式 2,表达式 3
计算过程,
首先计算表达式 1;
若其值为真(非 0),则计算表达式 2,取表达式 2的值作为整个条件表达式的值;
否则,计算表达式 3,取表达式 3的值作为整个条件表达式的值。
条件运算符,?,
目录133
优先级,条件运算符的优先级比关系运算符和算术运算符都低,比赋值运算符高。
例如,y=x>0?x+1:x*x+1
等价于
y=(( x>0)?( x+1),( x*x+1) )
说明:并不是所有的 if语句都能用条件表达式实现。
如 if(n>0) a=6; else b=8;
目录134
main()
{int n1,n2,n3,max;
printf("Please input three numbers:");
scanf("%d%d%d",&n1,&n2,&n3);
if (n1>n2) max=n1;
else max=n2;
if (n3>max) max=n3;
printf("max=%d\n",max);
}
[案例 ] 输入任意三个整数 n1,n2,n3,求三个数中的最大值。
目录135
main()
{int n1,n2,n3,temp;
printf("Please input three numbers:");
scanf("%d%d%d",&n1,&n2,&n3);
if (n1>n2) {temp=n1;n1=n2;n2=temp;}
if (n2>n3) {temp=n2;n2=n3;n3=temp;}
if (n1>n2) {temp=n1;n1=n2;n2=temp;}
printf("Three numbers after sorted,
%d,%d,%d\n",n1,n2,n3);
}
[案例 ]输入任意三个数 n1,n2,n3,按从小到大的顺序排序输出。
选择结构目录136
SWITCH语句
1,格式:
switch(表达式 )
{case 常量表达式 1:语句 1;
case 常量表达式 2:语句 2;
case 常量表达式 n,语句 n;
default 语句 n+1;
}
2,执行顺序:
1,计算表达式的值;
2,若值与某个常量表达式值相等,则转到其后语句执行;
3,若值与所有常量表达式值都不等,则执行 default后语句;
switch 表达式常量
1
常量
2
常量
3
常量
4 其它语句
1
语句
2
语句
3
语句
4 语句 5
目录137
举例:百分制成绩转为五分制成绩。
输入 score
score ==100
s=9 S=score/10
Switch(s)
9 8 7 6 其它优秀 良好 中等 及格 不及目录138
main()
{int score,s;
printf(―Input a score(0~100),‖);
scanf(―%d‖,&score);
s = score/10; /*将成绩整除 10*/
switch (s)
{case 10:
case 9,printf(―grade=A\n‖);
case 8,printf("grade=B\n");
case 7,printf("grade=C\n");
case 6,printf("grade=D\n");
default,printf(―grade=E\n‖);
}
}
参考程序如下:
目录139
1) case后常量的值必须互不相同,否则会出现相互矛盾 ;
2) case后面的常量表达式仅起语句标号作用,并不进行条件判断。系统一旦找到入口标号,就从此标号开始执行,不再进行标号判断,所以必须加上 break语句,以便结束
switch语句。
3) 各 case及 default子句的先后次序,不影响程序执行结果 ;
4) 多个 case子句,可共用同一语句(组) 。
说明:
目录140
main()
{int score,s;
printf(―Input a score(0~100),‖);
scanf(―%d‖,&score);
s = score/10; /*将成绩整除 10*/
switch (s)
{case 10:
case 9,printf(―grade=A\n‖); break;
case 8,printf("grade=B\n"); break;
case 7,printf("grade=C\n"); break;
case 6,printf("grade=D\n"); break;
default,printf(―grade=E\n‖);
}
}
改进程序如下:
选择结构目录141
程序举例
1、判断某一年是否闰年,闰年的条件是:能被 4整除、但不能被 100
整除,或者能被 400整除。 (使用 IF的嵌套解决)
设一标志变量 leap,leap=1为闰年,leap=0为非闰年,
输入 y
如果 y能被 4整除则如果 y能被 100整除则如果 y能被 400整除则是闰年否则不是闰年否则是闰年否则不是闰年目录142
main()
{int year,leap=0; /* leap=0,预置为非闰年 */
printf("Please input the year:");
scanf("%d",&year);
if (year % 4==0) {if (year % 100 != 0) leap=1;}
else {if (year%400==0) leap=1; }
if (leap) printf("%d is a leap year.\n",year);
else printf("%d is not a leap year.\n",year);
}
参考程序如下:
目录143
main()
{int year;
printf("Please input the year:");
scanf("%d",&year);
if ((year%4==0 && year%100!=0)||(year%400==0))
printf("%d is a leap year.\n",year);
else
printf("%d is not a leap year.\n",year);
}
利用逻辑运算能描述复杂条件的特点,可将上述程序优化如下:
目录144
程序举例
2、求一元二次方程的解 (使用 if语句的嵌套解决 )
输入 a,b,c
如果 a=0则不是一元二次方程否则
d=b*b-4*a*c
p=-b/(2*a),q=sqrt(|d|)/(2*a)
如果 d>0 则输出 x1=p+q,x2=p-q
如果 d==0则输出 x1=x2=p
如果 d<0 则输出 x1=p+i q,x2=p-i q
目录145
必要的注释,可有效地提高程序的可读性,从而提高程序的可维护性。
在C语言源程序中,注释可分为三种情况:
( 1) 在函数体内对语句的注释;
( 2) 在函数之前对函数的注释;
( 3) 在源程序文件开始处,对整个程序的总体说明 。
加注释的原则:如果不加注释,理解起来就会有困难,
或者虽无困难,但浪费时间 。
良好的源程序书写风格 ── 注释目录146
/*…… (说明功能) */
if(条件表达式 ) /*条件成立时的含义 */
{……}
else /*入口条件含义 */
{……}
在选择结构中,一般要在前面说明其作用,在每个分支条件语句行的后面,说明该分支的含义,如下所示:
目录147
第一部分:写出程序运行结果;
1,2,3,4
第二部分:编程
9,10,11
选择结构课后习题目录148
循环结构程序设计
6-1 循环结构组成
6-2 while语句
6-3 do_while语句
6-4 for语句
6-6 循环的嵌套
6-5 break,continue,goto语句学习目标目录149
重点掌握:
学会独立分析循环结构;
掌握三种循环语句的格式与执行过程。
本章学习目标:
循环结构目录150
6.1循环结构的组成循环结构:描述有规律的重复操作问题。
组成:要重复执行的操作,重复执行所需的条件。
例 6.1:求 10!,
分析:计算 1*2*3*…… *10;
需重复执行,乘,的操作,
用循环结构描述,
设 k为每次要乘的乘数,p为部分积,则
循环执行的条件为,k<=10;
循环重复执行的操作为,k*p送给 p,k加 1。
start
k=1,p=1
k≤10
p=p*k
k=k+1
输出 p
end
Y
N
目录151
循环体、循环控制条件、循环变量的初始化、循环变量的增值其中:
循环体是需要重复执行的操作序列;
循环控制条件是重复执行循环体所需的条件,即当条件成立时执行循环体,否则结束循环;
循环变量是决定循环条件是否成立的变量;循环变量的初始化是指在进入循环前,给循环变量赋初值。
循环变量的增值反映了循环变量的改变规律;
完整的循环结构由四部分组成:
目录152
循环体,P=P*K; K=K+1;
循环条件,K<=10
循环变量,K
进入循环前给 K赋初值,1
改变循环变量的值,K++
如在本例中:
目录153
例 6.2 从键盘输入 20个试验数据 (整型 ),
统计正数、负数的个数。
分析:
重复执行 20次,每次对一个数进行处理:输入,判断正、负。
设 j为已处理实验数据的个数,a,b分别保存正数、负数的个数,则:
循环体:输入第 j个数,并处理数据,
j增 1
循环条件,j<20
循环变量的初值,0
循环变量增值,1
start
j=0
j<20
j=j+1
输出 a,b
end
a=0;b=0
Y
N
输入 x
处理 x
目录154
循环有两类:
1.当型循环:先判断后执行
WHILE
2.直到型循环:先执行后判断
DO ~ WHILE
循环结构目录155
6.2 while语句用于描述,当型,循环结构。
一般格式:
while( 表达式) /*表达式为循环条件 */
循环体语句
其中,循环体语句可以是简单语句、复合语句或空语句。
while语句的执行过程为:
① 计算 while后条件表达式的值。
② 若表达式的值为真,则执行循环体语句,然后转①;
否则,退出循环,即结束 while语句。
目录156
While语句的执行流程图:
表达式循环体语句
Y
N
目录157
例 6.3 用 while语句编写例 6.1的程序
main() /*求 10! */
{int k; long p;
p=1; k=1; /*给循环变量赋初值 */
while(k<=10) /*循环条件放在括号中 */
{ p=p*k; /*循环体是一个复合语句 */
k++; /*循环变量增值语句 */
}
printf("\n10! is:%ld",p);
}
思考,若 n 由键盘输入,如何实现 n!?
目录158
例 6.4 用 while语句编写例 6,2的程序
main()
{int a,b,j,x;
a=b=0;
j=0; /*循环变量赋初值 */
printf("\n enter 20 integers:");
while(j<20) /*条件,j<20*/
{ scanf(―%d‖,&x); /*循环体为复合语句 */
if(x>0) a++;
else if (x<0) b++;
j++; /*循环变量增值 */
}
printf("\n positive,%d,negative,%d",a,b);
}
目录159
例 6.5 输入某班一门课的成绩并计算其平均成绩。
(人数不定,当输入成绩为 -1时结束)
分析:本例的特点是,循环次数不确定,,而是已知循环结束的条件。
循环的组成:
循环体:将有效成绩 score 加入 sum;输入下一个成绩
score。
循环控制条件,score!=-1
循环变量,score
循环变量的改变:用户输入
循环变量的初值,键盘输入目录160
例 6.5程序:
main()
{ float score,sum=0;
int n=0;
printf("\n enter a score(-1 for end):");
scanf("%f",&score); /*循环变量赋初值 */
while(score!=-1)
{sum+=score; n++;
printf("\n enter a score(-1 for end):");
scanf(―%f‖,&score); /*改变循环变量的值 */
}
printf("\n n=%d,aver=%f",n,sum/n);
}
特点:当第一次输入 -1时,循环体执行次数为 0。
目录161
Break语句:用于结束循环
main() /*例 6.5可改用以下程序实现 */
{float score,sum=0;
int n=0;
while(1) /*无条件循环:表达式永为,真,*/
{ printf("\n enter a score(-1 for end):");
scanf("%f",&score);
if(score== -1) break; /*利用 break语句跳出该循环 */
else { sum+=score; n++;}
}
printf("\n n=%d,aver=%f",n,sum/n);
}
目录162
本例的特点是:
虽然,循环次数事先不确定,,但已知,循环结束的条件,,因此采用,结束标志控制法,来控制循环的进行。
程序中用 -1作为结束标志,当读取到该数时,循环体中的 if语句的条件成立,便结束循环。
结论:
此处 break语句作用是提前结束它所在的循环。通常与 if语句连用,即当满足某条件时,结束循环。
而且 break语句只能用在 switch 和循环语句( while、
do_while,for)中。
循环结构目录163
6.3 do_while语句
do_while语句的特点,
,先执行循环体,后判断循环条件,
当条件成立时执行循环体,条件不成立时结束循环。
因此,
do_while循环常用来实现类似,直到型,循环的,当型,循环结构。
目录164
例 6,6 求自然数 n中各位数字之和( n由用户输入)
问题分析,从 n的个位开始,重复求每一位数字,并加入和 s,直到最高位数字处理完为止。因此,解决问题的关键是:
① 如何求得 n中的每一位数字?
② n的位数事先不确定,如何控制循环次数?
具体方法是:
用 n%10得 n的最低位,加入和 s;
然后 n=n/10,即 n始终表示由未处理的数字组成的自然数。
重复以上过程,直至 n等于 0时结束。
目录165
由此得循环结构的组成要素为:
循环体,计算 n的个位并加入和 s,即 s+=n%10
循环控制条件,n大于 0
循环变量,n
循环变量增值规律,n=n/10
循环变量初值:用户输入目录166
例 6.6 程序,用 while循环
main()
{ int s=0,n;
printf("\n input a natural number:");
scanf("%d",&n);
while(n>0)
{ s+=n%10;
n/=10;
}
printf("\n sum=%d",s);
}
目录167
例 6.6 程序,用 do_while循环
main()
{ int s=0,n;
printf("\n input a natural number:");
scanf("%d",&n);
do{
s+=n%10;
n/=10;
} while(n>0);
printf("\n sum=%d",s);
}
结论:两种语句可通用。
目录168
Do_while 语句的格式及执行过程
Do_while语句的一般格式,
do { 语句 }
while(表达式 );
其中,
表达式是循环控制条件
语句是循环体,通常用复合语句,使得结构更清楚。
特别注意:
该语句最后的,;,
执行过程:
Y
N
表达式语句注:循环体最少执行一次。
目录169
Do_while语句的应用
例 6.7 编写含菜单的程序。
设小学生算术练习系统的菜单项包括:
1.加法,2.减法,3.乘法,4.除法,5.退出。
编程实现:进入系统后显示菜单,等待用户选择,然后执行相应的功能;执行完后返回主菜单,直至选择退出。
分析:
循环体:显示菜单,选择,执行子功能。
循环条件,ch 不等于 ‘ 5’
循环变量,ch
循环变量的改变:用户输入目录170
#include "stdio.h"
main()
{ char ch;
do{/*显示菜单项 */
printf("\n exercise for primary student");
printf("\n 1,add");
printf("\n 2,subtraction");
printf("\n 3,multiplicative ");
printf("\n 4,divide");
printf("\n 5,exit");
printf("\n please choice(1~5):"); /*输出提示信息 */
ch=getchar(); getchar(); /* 接收用户输入 */
程序实现:
目录171
/*根据用户的选择执行相应的功能 */
switch( ch )
{case '1',printf("\nJIA."); break;
case '2',printf("\n JIAN."); break;
case '3',printf("\n CHENG."); break;
case '4',printf("\n CHU."); break;
case '5',printf("\n END."); break;
default,printf("\n ERROR,choice again!");
}
}while(ch!='5');
}
循环结构目录172
6.4 for语句
For 语句较前两种循环语句更简洁、更灵活,特别是处理循环次数确定的问题。
例 6.8 输出 100~200之间不能被 3整除的数。
问题分析:
用计算机解决此类问题常采用,穷举法,,
将 100~200之间的数逐个处理一遍:
(即判断其能否被 3整除,若不能,则输出;
否则,不需处理)。
目录173
设变量 n表示每次待处理的数,对 n的处理采用选择结构实现,其中 n不能被 3整除的条件是,n%3!=0‖。
则循环结构的组成为:
循环体:判断 n能否被 3整除并进行相应的操作。
循环条件,n<=200 ( 循环变量,n )
循环变量的初值,100
循环变量的增值,n++
目录174
例 6.8程序
/*等价的 while循环 */
main()
{ int n;
n=100; /*初值 100*/
while(n<=200)
{
if(n%3!=0)
printf("%5d",n);
n++;/ *增值 1*/
}
}
/*用 for循环 */
main()
{ int n; /*循环变量 */
for(n=100;n<=200;n++)
if(n%3!=0) /*循环体 */
printf("%5d",n);
}
目录175
for 语句的格式、执行过程一般格式:
for(表达式 1;表达式 2;表达式 3)
循环体语句其中:
表达式 1为赋初值表达式
表达式 2为条件表达式
表达式 3为循环变量增值表达式
流程图:
计算表达式 1
计算表达式 2
循环体语句计算表达式 3
Y
N
目录176
关于 for 语句的几种用法:
例 6.9 求 n!。 (n=10)
main()
{ int n,t=1;
n=10;
for(k=1;k<=n;k++) t=t*k;
printf(("\n N! =%d",t);
}
红色语句段等价于:
for(n=10,k=1 ; k<=n ; k++) t*=k;
表达式 1可以是逗号表达式。
目录177
也等价于:
n=10; k=1;
for( ; k<=n ; k++) t*=k;
表达式 1可省略,赋初值语句上移,但,;” 不能省略。
又等价于:
for(k=1 ; k<=n ;)
{ t*=k; k++; }
表达式 3可省略,移至循环体内
若表达式 2省略,则为死循环,除非用 break结束循环目录178
例 6.10 猴子吃桃
分析:
设第 k+1天的桃子数为 n,则第 k天的桃子数为 (n+1)*2
k的初值为 9,终值为 1,增值步长为 1; n的初值为 1。
main()
{ int n=1,k;
for(k=9;k>=1;k--) n=2*(n+1);
printf("\n total:%d",n);
}
注意:
k=9,计算出的 n是第 9天的桃子数,所以条件是
k>=1,而不是 k>1。
目录179
例 6.11 判断一个整数 n是否为素数。
#include ―math.h―
main()
{ int n,k,j;
printf(―\n enter n:‖); /*输入 n*/
scanf("%d",&n);
k=sqrt(n);
for(j=2;j<=k;j++) /*判断 n能否被 2~k之间的数整除 */
if(n%j==0) break; /*结束循环 */
if(j>=k+1) /*若正常退出,则为素数 */
printf("\n%d is a prime number.",n);
else
printf("\n %d is not a prime number.",n);
}
目录180
Continue语句例 6.12 求输入的十个整数中正数的平均值。
main()
{int k,n,a=0; float s=0;
printf(―\n enter 10 integers:‖);
for(k=0;k<10;k++)
{ scanf(―%d‖,&a);
if (a<=0) continue;
s+=a; n++;
}
printf((―aver=%f\n‖,s/n);
}
continue作用:结束本次循环体的执行,即跳过循环体中
continue语句后面尚未执行的语句。
循环结构目录181
6.5循环的嵌套例 6.13 求 1!+2!+3!+ …… n!
分析,重复求 j!并加入和 sum 。 求 j!又是一循环结构。
main()
{ int n,j,i; long t,sum=0;
printf("\n enter n:"); scanf("%d",&n);
for(j=1;j<=n;j++)
{t=1; /*循环体:计算 n!,并加入 sum*/
for(i=1;i<=j;i++) t*=i;
sum+=t;
}
printf("\n result is %ld.",sum);
}
目录182
例 6.13 方法 2:不用嵌套的循环。
main()
{ int n,j; long t,sum=0;
printf("\n enter n:");
scanf("%d",&n);
t=1;
for(j=1;j<=n;j++)
{ t=t*j; /*循环体:计算 j!=j*(j-1)!,*/
sum+=t; /*j!加入 sum*/
}
printf("\n result is %ld.",sum);
}
目录183
计算 e的近似值:
e= (n=10)
!
1
!2
1
!1
1
n
目录184
例 6.14、某班期末考试有 4门课,输入每人每门课的成绩,并计算每门课最高成绩。(设人数为 30)
定义数据结构:
score表示一个成绩
k表示第几门课
j表示某一门课中的第几个人
max表示某一门课中的最高成绩。
目录185
采用,自顶向下,逐步细化,的模块化方法,该问题的总体思路是:
重复处理每一门课的成绩,采用循环结构。这是 第一层循环,
循环体为,处理一门课的成绩,;
循环的条件是,k<=4‖;
k的初值是,1”,增值规律是,k++‖。
算法分析:
目录186
循环体为,输入第 k门课第 j个人的成绩,与该门课已输入成绩中的最大值 max 比较。
循环条件是 ― j<=30”
j的初值是,1“,增值规律是,j++―。
注意,处理一门课成绩开始前,应给 max赋初值。
然后分析循环体如何实现,如何处理一门课的成绩,
即 第二层循环,
目录187
#define N 30 /*定义符号常量 N表示人数 30*/
main()
{ int k,j; float score,max;
for(k=1;k<=4;k++) /*外层循环 */
{ printf("\n enter score of course no %d:",k);
scanf("%f",& max ); /*输入该门课第一个成绩给 max*/
for(j=2;j<=N; j++) /*内层循环,从第二个成绩开始逐个与
max比较 */
{ scanf("%f",&score);
if(score>max) max=score;
}
printf("\n the max of course no %d is,%f",k,max);
}
}
目录188
问题:作为最大值变量,其初值如何设?
一般有两种方法:
( 1)取第一个数据作为最大值变量的初值。这种方法是最常用的,
对数据范围已知和未知的情况均适合。编程时,需先给最大
(或最小)值变量赋初值,再进入循环处理后面的数据。
( 2)若已知待处理数据的范围,则最大值变量的初值可以取其范围下界,最小值变量的初值则取其范围的上界。如该例可以用语句,max=0;‖赋初值,循环变量 j的初值改为,1” 。
2,若人数不定,需根据实际有效成绩计算,如何实现?
目录189
不定方程求解问题(穷举问题)
例 6.15 打印出所有的水仙花数。
方法 1:设 a,b,c分别表示一个三位数的百、十、个位,则满足条件 a3+b3+c3=100*a+10*b+c的数是水仙花数。
该方程的求解方法:首先找出方程中每个变量的取值范围,
对所有 a,b,c的任意组合都验证一遍,满足上述条件的一组
a,b,c即是方程的解。
方法 2:对每一个三位数 n,判断其是否为水仙花数。
首先分解出 n的三位数字 a,b,c,然后判断是否满足条件 n=
a3+b3+c3,若满足,则 n为水仙花数。
目录190
main() /*程序( 1) */
{ int a,b,c,n,s;
printf("\n daffodil number:");
for(a=1;a<=9;a++)
for(b=0;b<=9;b++)
for(c=0;c<=9;c++)
{ n=100*a+10*b+c;
s=a*a*a+b*b*b+c*c*c;
if(n==s) printf("%d ",n);
}
}
目录191
main() /*程序( 2) */
{int a,b,c,n,s;
printf("\n daffodil number:");
for(n=100;n<1000;n++)
{ a=n/100; b=(n%100)/10; c=n%10;
s=a*a*a+ b*b*b+ c*c*c;
if(n==s) printf("%d ",n);
}
}
目录192
类似的穷举问题:
1,百钱买百鸡:一百钱,买一百只鸡,其中公鸡 3钱 /只、
母鸡 1钱 /只、小鸡 3只 /钱,问可买公鸡、母鸡、小鸡个多少只?
2,换零钱:将十元钱换成五元、二元、一元的方法有多少种?分别输出各种组合情况。
3,鸡兔同笼:笼子里有鸡、兔若干只,其中头数为 16,
脚的总数为 40,问笼子里有鸡、兔各多少只?
4,取红、黑、白球:盒子里共有 12个球,其中 3个红球、
3个白球,6个黑球,从中任取 8个球,问至少有一个红球的的取法有多少种?并输出每一种具体的取法。
目录193
练习:使用 Continue语句填空:下例统计所有的三位数中三个数字各不相同的数有多少个?
main()
{ int i,j,k,count=0;
for(i=1;i<=9;i++)
for(j=0;j<=9;j++)
if( ① ) continue;
else
for(k=0;k<=9;k++)
if( ② ) count++;
printf(("%d",count);
}
目录194
循环次数事先不确定,但已知循环结束的条件的问题:
1) 输入一行字符,分别统计其中字母,数字,空格及其它字符的个数。
2) 计算,的近似值,
直到最后一项的绝对值小于 10-6。
3) 译密码问题。
4) 迭代法求方程的根。

7
1
5
1
3
11
4
循环结构目录195
Goto语句
1,格式,goto 语句标号;
2,功能:无条件转向语句标号所指语句去执行。
3,说明:
1,任何语句前都可以加上标号,形如,标号,语句
2,语句标号的命名与变量名相同。特别注意不能用整数做标号!
4,Goto语句用途:
1,与 if 一起构成循环;
2,跳出循环。
目录196
Goto语句
例:计算 S=1+2+3+… +100;
main()
{ int i,s=0;
i=1;
loop:s=s+i;
i=i+1;
if ( i<=100) goto loop;
printf(―sum=%d\n‖,s);
}
循环结构目录197
例:输出 r=1到 10时圆的面积,到面积 >100为止。
main()
{int r; float area;
for(r=1;r<=10;r++)
{ area=3.14*r*r;
if (area>100) break;
printf(―r=%d,area=%.2f\n‖,r,area);
}
}
程序举例目录198
例:输出 1~100之间不能被 7整除的数。
main()
{int n;
for(n=1;n<=100;n++)
{ if (n%7==0) continue;
printf(―%4d‖,n);
}
}
程序举例目录199
程序举例例 计算?的值,直到最后一项绝对值 <10-6为止。
分析:
/4=1-1/3+1/5-1/7+…
要点是每次循环中符号要改变,可用一符号变量来处理。
另外,结束条件用到当前项的值。
目录200
程序举例例 求 Fibonacci 数列前 20个数。
分析:
递推公式为,F1=1,f2=1,fn=fn-1+fn-2(n>=3)
新一项的值只和前面二项的值有关。
故有以下思路(迭代):
循环结构
f1=1,f2=1,n=3
输出 f1,f2
当 n<=40 做:
f3=f2+f1;
输出 f3 ( 求出 f3后变量 f1的值不再使用)
f1=f2;f2=f3;
结束目录201
数 组
数组:用于保存一批相同类型的数据,属构造类型;
数组名:一批同类型数据共有的名字;
下标:数组中的各个数据用下标来区分;
例如,学生成绩、矩阵等的处理,均可用数组表示;
本章介绍:
1,一维 数组
2,二维 数组
3,字符 数组
重点:数组的定义和数组元素的引用!
目录202
一维数组例 7.1 输入 50个整数,按逆序输出。
main()
{int a[50],i; /* 定义数组 a*/
for (i=0; i<50; i++) /* 从键盘输入 50个整数 */
scanf("%d",&a[i]);
for (i=49; i>=0; i--) /* 逆序输出 50个整数 */
printf("%4d",a[i]);
}
a[0] a[1] a[2] a[3] a[4]……a[49]
目录203
一维数组
1,一维数组的一般形式,
类型说明符 数组名 [常量表达式 ]
如,float score[30];
float 是类型说明符,定义了该数组中元素的类型。
score 是数组名,是这一组数据共有的的名字;
30是常量表达式,表示数组的长度,即数组元素的个数。
也就是说:数组 score 可以保存 30个 float型的数据。
注意:定义数组时长度必须是确定的值。
例如,int aa[ ]; (error)
int n; float xs[n]; (error)
目录204
一维数组
2,数组元素的引用,数组名 [下标 ]
如,a[i]是数组 a 中下标为 i 的数组元素 ;
a[0]是下标为 0 的数组元素。
C语言的规定:数组元素的下标从 0开始。
如数组 a[50]包含 50个数组元素,
分别是,a[0] a[1] a[2] …… a[49]
即不存在数组元素 score[50]。
若使用 a[50]是危险的,为什么?
一维数组的存储格式:每个数组占用一片连续的存储单元,按下标次序依次存放各元素。
如 int data[10]; 数组 data占 2*10=20个字节数组目录205
一维数组
3,一维数组的存储格式:每个数组占用一片连续的存储单元,按下标次序依次存放各元素。
如 int data[10]; 数组 data占 2*10=20个字节
4,数组初始化:
定义数组时对数组元素赋予初值;如:
int a[6]={0,1,2,3,4,5};
对全部元素赋初值时,可不指定数组长度;如:
int a[ ]={0,1,2,3,4,5};
若只给部分元素赋初值,长度不能省略;如:
int a[6]={0,1,2};
数组目录206
一维数组例 1:用数组求 Fibonacci数列前 20项。
分析:可以直接套用公式 ---
定义一个有 20个元素的数组 F[20];
已知 F[0]=F[1]=1;
I从 2~19,做:
F[I]=F[I-1]+F[I-2];
I从 0~19,做:
打印输出 F[I];
数组目录207
一维数组例 2:给定 5个数,按由小到大排序(交换法)
思路:相邻元素比较,小的调到前面。具体如下:
第一遍,A[1]比 a[2],a[2]比 a[3],a[3]比 a[4],a[4]比 a[5]
第二遍,A[1]比 a[2],a[2]比 a[3],a[3]比 a[4]
第三遍,A[1]比 a[2],a[2]比 a[3]
第四遍,A[1]比 a[2]
数组目录208
一维数组例如:设 待排序的数据为,3 2 10 8 1
起泡排序过程为,
第一遍,2 3 8 1 10
第二遍,2 3 1 8 10
第三遍,2 1 3 8 10
第四遍,1 2 3 8 10
从上面思路可找出规律:
I取 1~~5-1,每取一个值时:
J从 1~~5–I+1,每取一个值时:
如果 a[j]>a[j+1]则交换 a[j]和 a[j+1]的值。
目录209
一维数组
#define N 5
main()
{ int a[ ]={0,3,2,10,8,1}; int t,i,j;
for(i=1;i<=N-1;i++)
for(j=1;j<N-i+1;j++)
if(a[j]>a[j+1]) /*若相邻两数逆序,则交换 */
{t=a[j];a[j]=a[j+1];a[j+1]=t;}
for(i=1;i<=N;i++) printf("%d ",a[i]);
printf("\n");
}
目录210
一维数组思考题:输入 10个数,输出其最大值及下标。
数组
Max=a[0];k=0;
For(I=0;I<10;I++)
If (a[i]<max)
{max=a[I];k=I;}
输出 max,k
k=0;
For(I=0;I<10;I++)
If (a[k]<a[I]) k=I;
输出 a[k],k
目录211
二维数组
1,定义:类型说明符 数组名 [常量 ] [常量 ] ;
例如,int a[2][3];
定义一个 2行 3列的数组,元素类型为整型,数组元素共有 2× 3个,即,a[0][0],a[0][1],a[0][2]
a[1][0],a[1][1],a[1][2]
说明:
1,二维数组可以看作一种特殊的一维数组;
2,C中二维数组元素在内存中的排放顺序是按行存储;
目录212
二维数组
2,元素的引用:数组名 [下标 ] [下标 ]
其中:下标可以为整型常量、变量或表达式。
注意:数组元素下标的使用范围;如:
a[0][0]=1; a[1][1]=1;
又如:
int I,j,a[3][4];
For(I=0;I<3;I++)
For(j=0;j<4;j++) a[I][j]=I;
目录213
3、二维数组初始化:
1) 分行赋初值;如:
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
2) 按顺序赋初值;如:
int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
3) 给部分元素赋初值,可写成:
int a[3][4]={{0},{1},{2}};
4) 某些情况下可不指定数组第一维的长度,但第二维不能省;如:
int a[ ][3]={0,1,2,3,4,5};
目录214
1,分析数据结构:
30人 4门课的成绩用二维数组表示,30人的平均成绩可用一维数组表示。
2,算法:
S1:输入 30人(每人 4门课)的成绩。
S2:计算每人的平均成绩。
S3:输出每个人的平均成绩。
3,分别分析每一步如何实现?
例,输入某班( 30人)期末考试 4门课的成绩,计算每人的平均成绩,输出各科及平均成绩。
目录215
# define M 30
# define N 4
main()
{int i,j ;
float s[M][N],aver[M],sum ;
for(i=0 ; i<M ; i++)
for(j=0 ; j<N ; j++) scanf("%f",&s[i][j]) ;
for(i=0 ; i<M ; i++)
{sum=0;
for(j=0 ; j<N ; j++) sum+=s[i][j] ;
aver[i]=sum/N ;
}
for(i=0 ;i<M;i++)
printf("aver[%d]=%.2f\n",i,aver[i]);
}
目录216
例:求已知矩阵 a的转置矩阵 b(行列互换)
分析两个矩阵元素的对应关系:
a[i][j] 对应 b[j][i]
987
654
321
a
963
852
741
b转置目录217
main(?)
{int a[3][3]={1,2,3,4,5,6,7,8,9};
int i,j,b[3][3] ;
for(i=0 ; i<3 ; i++)
for(j=0 ; j<3 ; j++) b[j][i]=a[i][j];
for(i=0 ; i<3 ; i++)
{for(j=0 ; j<3 ; j++)
printf(―%d\t",b[i][j]);
printf(―\n‖);
}
}
目录218
例,求矩阵中的最大值元素及其所在行和列。
main(?)
{int i,j,r,c,max ;
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12} ;
max=a[0][0] ;
for(i=0 ; i<3 ; i++)
for(j=0 ; j<4 ; j++)
if(a[i][j]>max) max=a[i][j],r=i,c=j ;
printf("max=%d,r=%d,c=%d\n",max,r,c);
}
数组目录219
字符数组
C语言没有专门存放字符串的变量,用字符数组存放字符串,每个数组元素保存一个字符。
1,定义,char 数组名 [常量 ];如:
Char c[10];
2,引用:
同一维数组元素引用。
3,初始化:
Char c[6]={?h‘,‘e‘,‘l‘,‘l‘,‘o‘,‘!‘};
目录220
4,字符串
字符串存储时,系统自动在字符串最后加入一个字符
'\0',作为字符串的结束标志 。
可用字符串常量初始化数组;
如,Char c[]={―good!‖};
或,Char c[]=―good!‖;
注意:字符数组并不要求其最后一个字符一定为‘ \0’。
c g o o d ! \0
目录221
字符数组的输入输出
1,逐个字符输入输出;,%c‖
2,整个字符串一次输出;,%s‖
注意:
1,输入输出字符串均不包括 ‘ \0’ ;
2,以,%s‖格式输入输出字符数组时,输入输出项均为字符数组名 ;
3,以,%s‖格式输出,数组长度大于串长,也是遇
‘ \0’ 结束;
4,若数组中有多个 ‘ \0’,遇第一个 ‘ \0’ 输出结束。
目录222
字符串输入输出函数
1,puts(STR)
STR为字符串常量或字符数组名;如:
char str[]=―Hello!‖;
puts(str);
2,gets(STR)
STR为字符串常量或字符数组名;如:
char str[20];
gets(str);
3,例 7-10/7-11
目录223
例:用字符串表示学生姓名
#define MAX 100
#define n 4
#include "stdio.h"
main()
{int a[MAX][5],i,j,n;
char xm[MAX][20];
for(i=1;i<=n;i++)
{printf("input No,%d name:",i) ; gets(xm[I]);
printf("input No,%d three score:",i);
for(j=1;j<=3;j++) scanf("%d",&a[i][j]);
}
1)输入学生信息目录224
XM
Z H A N G
L I S I
W A N G
XH SX YW JSJ ZF
1 55 66 77
2 66 77 88
3 77 88 99
1
2

MAX-1
数组 XM[MAX][6] 数组 a[MAX][5]
学生数据组织形式目录225
2)计算学生总分
(a[i][4]=a[i][1]+a[i][2]+a[i][3])
for(i=1;i<=n;i++)
{a[I][4]=0;
for(j=1;j<=3;j++)
a[I][4]=a[I][4]+a[I][j];
}
目录226
3)输出学生信息
printf(―Output student infomation:\n ");
for(i=1;i<=n;i++)
{printf(―%s\t‖,xm[i]);
for(j=1;j<=4;j++) printf("%d\t",a[i][j]);
printf("\n");
}
}
目录227
字符串处理函数
1,Strcat(字符数组 1,字符数组 2)
2,Stycpy(字符数组 1,字符串 2)
3,Strcmp(字符数组 1,字符串 2)
4,Strlen(字符数组 )
5,Strlwr(字符串 )
6,Strupr(字符串 )
目录228
字符串举例
例 7.12 输入两个字符串,分别测试其长度,并比较两个字符串的大小。
分析,
1)输入两个字符串,分别存入字符数组 str1,str2,
用 gets实现。
2)用函数 strlen测试两个字符串的长度。
3)用函数 strcmp比较两个字符串的大小。
目录229
例 7.14 输入一行字符,统计其中有多少个单词
(单词之间用空格分隔)
思路:
1,输入一行字符串,存入一维字符数组;
2,然后对每一个字符进行如下判断:
当前字符 =空格?
若是,则未出现新单词,使 word=0。
若不是,则根据前一字符是否为空格判断:若是空格
(即 word=0),则新单词出现,num加 1,word=1;
否则(即 word=1),未出现新单词。
目录230
例 7-12:连接两个字符串(使用函数);
要求:
不使用函数,用数组实现连接。
数组
字符串练习
c g o o d \0
d m o r n i n g ! \0
c g o o d m o r n i n g ! \0
目录231
函 数引入函数函数的定义函数的调用局部变量和全局变量变量的存储类别重点数组作函数参数小结目录232
1、函数的定义
2、函数的调用(包括嵌套调用和递归调用)
4、局部变量和全局变量
5、变量的存储类别重点,
1) 如何定义函数(即如何编写函数)
2) 如何调用函数(参数传递过程)
主要内容:
函数目录233
函数的引入
函数:是完成某些特定功能的代码块。
使用函数的优点:
1) 实现模块化设计:将一个大任务分解成一个个小任务,每个任务分别用函数实现。
2) 实现“编写一次,多次调用”,避免在不同的程序中重复编写相同的函数。
3) 便于程序调试和维护,因为每个函数之间相互独立。
函数的分类:库函数和用户自定义函数函数目录234
8.1 函数的定义例 8,1 编写函数 max,求两个整数 x和 y中的较大数 。
int max (int x,int y) /*函数头 */
{ int z=x; /*函数体 {…… }*/
if (x<y) z=y;
return z;
}
main()
{int a,b,d;
printf("input a and b:");
scanf{"%d %d",&a,&b};
d=max(a,b); /*函数调用 */
printf("max is,%d",d);
}
目录235
例 8.2:打印表头
某程序中需多次用到打印表头的功能,用函数实现:
void line( )
{ printf("\n ********************");
printf("\n* score list *");
printf("\n ********************");
}
本例中:
函数的类型,void,函数名为 line,函数无参数。
该函数无返回值,所以函数类型为 void型。
目录236
函数定义的一般形式:
函数类型 函数名 (类型 形式参数 1,类型 形式参数 2,… )
{说明部分语句部分
}
其中:
1.函数名:函数的命名最好做到“见名知义”。
2.函数类型:函数的返回值类型。
当函数无返回值时,规定其类型为,void。
当函数返回值为 int 时,函数类型可以省略。
目录237
3,形式参数:函数被调用时用于接收实参值的变量。
多个参数之间应用逗号分隔。
形参的类型说明可有如下两种格式:
int max(int a,int b)
{ return (a>b?a:b);}

int max(a,b)
int a,b;
{ return(a>b?a:b); }
目录238
4.函数体:即函数功能的具体实现。
包括两部分,说明部分和执行部分
其中说明部分包括函数中所用的局部变量等的说明、函数中要调用的函数的说明。
注意:
函数不能嵌套定义,即函数内不能再定义函数。
目录239
5.空函数:
函数类型 函数名 ( )
{ }
调用此函数时,什么也不做。只是表明这里要调用一个函数,而现在这个函数的功能还没实现。
空函数在程序设计中常常用到:
1) 预留函数,便于以后扩充程序功能。
2) 便于程序的模块化设计和调试目录240
空函数举例
例,编写小学生算术练习系统的主程序:
显示主菜单,用户选择,根据选择执行加、减、乘、
除、退出 5项功能之一。
重复上述步骤,直至选择退出。
其中主程序调用的函数有:
显示主菜单函数 list_menu(),
加、减、乘、除、退出函数分别是 add(),sub(),
mul(),divide(),end(),
以上除 list_menu()外此时均为空函数。
目录241
#include "stdio.h"
main()
{ void add(),sub(),mul(),divide(),end(),list_menu(); int n;
do{list_menu();
scanf("%d",&n); getchar();
switch(n)
{case 1,add(); break;
case 2,sub(); break;
case 3,mul(); break;
case 4,divide(); break;
case 5,end(); break;
default:printf("\n enter error,please again.");
}
}while(n!=5);
}
目录242
void list_menu()
{printf("\n ** exercise system **");
printf("\n ** 1,add **");
printf("\n ** 2,sub **");
printf("\n ** 3,mul **");
printf("\n ** 4,divide*");
printf("\n ** 5,end **\n");
}
void add() { }
void sub() { }
void mul() { }
void divide() { }
void end() { }
目录243
问题:如何定义一个函数?
第一步:分析函数需要的参数,包括参数的的个数以及每个参数的类型,
第二步,分析函数返回值的类型,若无返回值,则为
void。 函数的返回值可看作是函数执行完后需输出的一个数据。
第三步:编写函数体
说明:
参数和返回值是函数之间的接口。
参数包括执行该函数时需要的数据信息,以及返回数据的有关信息。
目录244
例如:
1) 求 n!
要处理的数据是 n,因此必须有一个参数 n,类型为 int。 返回值为 long 型。即
long fact( int n) { }
2) 打印表头
不需输入任何数据即可执行该函数,因此无参数。
执行该函数无返回值,因此函数类型为 void。 即
void line( ) { }
目录245
3) 求两个整数 m和 n的最小公倍数
执行该功能时必须有两个整型参数,返回值为整型。

int min_multiple(int m,int n) { }
4) 求一批整型数据( n个)中的最大值。
实现该功能的函数的参数有两个:该批数据的首地址及数据的个数。返回值为一个整型数。 即
int max( int data[ ],int n) { }
目录246
/*计算两个整数的最小公倍数 */
main()
{int m,n,min;
int min_multiple( int,int ); /*函数声明 */
printf("\n input m,n:"); scanf("%d%d",&m,&n);
min=min_multiple(m,n); /*函数调用 */
printf("\n bei shu:%d",min);
}
int min_multiple(int x,int y) /*函数定义 */
{ int i; i=1;
while(x*i%y!=0) i++;
return(x*i);
}
函数目录247
8.2 函数的调用
重点:
1、对被调函数的声明
2、如何调用一个函数
3、主调函数和被调函数之间如何进行数据传递目录248
分三种情况:
(1)函数 fact()与主函数在同一文件中,且 main( )在
fact()前面。
(2)函数 fact()与主函数在同一文件中,且 main在
fact之后。
(3)函数 fact与 main不再同一程序文件中。
例 8.5 调用函数 fact( )求 n!(n由用户输入)。
目录249
第一种情况:主调函数在被调函数之前
main()
{int n; long p;
long fact(int); /*函数声明 */
scanf("%d",&n);
p=fact(n); /*函数调用 */
printf("\n %ld",p);
}
long fact( int m) /*函数定义 */
{ int i; long s=1;
for(i=1;i<=m;i++) s*=i;
return(s); /*函数返回 */
}
结论,被调函数在后,需在主调函数中先声明后调用。
目录250
第二种情况:主调函数在被调函数之后
long fact( int m) /* 函数定义 */
{ int i; long s=1;
for(i=1;i<=m;i++) s*=i;
return(s);
}
main()
{int n; long p; /*不需函数声明 */
scanf("%d",&n);
p=fact(n); /*函数调用 */
printf("\n %ld",p);
}
结论:被调函数先于主调函数被编译,不需函数声明。
目录251
第三种情况:被调函数存放在另一文件 f1.c中
#include <f1.c>
main()
{int n; long p;
scanf("%d",&n);
p=fact(n);
printf("\n %ld",p);
}
结论:编写主调函数时需用 include 命令将被调函数所在文件包含进来。
目录252
被调函数声明函数原型
1,被调函数函数必须存在 ( 自定义或库函数 ) ;
2,若被调函数是库函数,须在文件头加 #include ―~‖;
3,若被调函数为自定义函数,且与主调函数在同一文件中,
一般应在主调函数中对被调函数作声明,形式为:
类型名 被调函数名 (类型 1 参数 1,类型 2 参数 2,… );
4,C规定,以下几种情况可不作被调函数声明:
1,被调函数函数值为整型或字符型;
2,被调函数的定义在主调函数之前;
3,已经在所有函数定义之前作了函数声明;
目录253
函数的调用与返回过程
1、函数调用的一般形式:
函数名 (实参表 )
如,p=fact (n);
注意,
实参与形参的类型、个数、顺序必须一致!
函数调用方式,
函数语句,printf(―%d,%d‖,a,b);
函数表达式,c=2*max(a,b)
函数参数,m=max(max(a,b),c);
目录254
2、调用函数过程:
1) 给形参分配存储单元,将实参的值传递给形参,再将控制流程转到被调函数;
2)然后执行被调函数。
3)当执行到 return语句,或执行到函数体最后一个大花括号时,控制流程返回到主调函数的断点处继续执行主调函数。 同时释放形参占用存储单元。
目录255
参数传递:实参与形参的结合
形参:定义函数时的参数,作用是:该函数被调用时用来接收实参的值,
此时的参数无具体的值,仅表示参数的类型、个数、
以及在函数体内对其如何处理。
实参:调用函数时的参数为实参,它表示该函数要处理的数据信息。
实参必须有确定的值。调用时,将实参的值传给形参。
调用函数时,实参与形参的类型、个数必须完全一致!
目录256
,参数传递,举例
swap(int x,int y)
{int t;
t=x; x=y; y=t;
printf(―x=%d,y=%d\n‖,x,y);
}
main()
{int a,b;
a=5;b=8;
swap(a,b);
printf(―a=%d,b=%d‖,a,b);
}
5 b 8a
5 yx 8
调用前调用开始时调用结束后
8 5
参数的传递是单向的,
只能由实参传给形参,
在被调函数中对形参的改变的不影响实参的值。
目录257
3、函数的返回值:
函数返回的实现:
1)函数体中通过执行 return语句返回,格式有 3种:
return(表达式 ); 或 return表达式 ; 或 return;
2)若函数体中无 return语句,当执行到函数末尾时自动返回到调用函数。
注意:
1)函数的返回值最多只有一个;
2)当需要返回多个值时,用 return语句无法实现,只能通过传地址调用实现。
目录258
函数调用练习
1,设计一个函数计算 n!
2,设计一个函数求 s=1+2+… +10
3,试着将 2,3结合起来:
计算 S=1! +2! +3! +…… +10!
函数目录259
8-2-4 函数的嵌套调用
所谓嵌套调用,就是在调用一个函数的过程中,又调用另一个函数。即:
函数 A中调用函数 B,函数 B中又调用函数 C。 例如:
main()
{printf(―sum=%d\n‖,total(5));
}
int total(int n)
{int i,s=0;
for(i=1;i<=n;i++)
s=s+fac(i);
return(s);
}
int fac(int n)
{int i,s=1;
for(i=1;i<=n;i++)
s=s*i;
return(s);
}
目录260
例 8,6 嵌套调用实例,
f(int x)
{ int t; t=x+x; return(t);}
g( int a,int b)
{ int z; z=f(a*b); return(z);}
main()
{int x1=3,x2=4,y;
y=g(x1,x2);
printf(―y=%d\n",y);
}
计算过程:
y=g(3,4)
=f(3*4)
=f(12)
=12+12
=24
目录261
函数调用过程
1) 在 main函数中调用 g();
2) 执行 g ()函数的的过程中调用 f();
3) 执行完 f()函数后返回到 g()接着执行;
4) 执行完 g()后返回到 main().
目录262
例 8,7 编写函数,验证陈景润研究的哥德巴赫猜想:任意大偶数为两个素数之和并输出这两个素数(所谓大偶数是指 6开始的偶数)。
分析:
isprime(int a)实现判断一个数 a是否为素数;
even(int x)实现找到并输出两个素数 ( 其和等于 x) ;
main()实现输入一个大偶数,并调用 even()输出该大偶数对应的两个素数 。
目录263
设变量 x为任意大偶数,可从 x中依次减去 i,i从 2变化到
x/2; 依次判断 i,x-I是否为素数 。 步骤如下:
① i初值为 2。
② 判断 i是否是素数 。 若是,执行步骤 ③ ;若不是,执行步骤 ⑤ 。
③ 判断 x-i是否是素数 。 若是,执行步骤 ④ ;若不是,
执行步骤 ⑤ 。
④ 输出结果,返回调用函数 。
⑤ 使 i增 1。
⑥ 重复执行步骤 ② 。
目录264
/*例 8.7程序 */
#include "math.h"
int isprime (int); /*函数声明 */
void even(int);
main()
{ int a;
printf("Enter a even number(≥6):");
scanf("%d",&a);
if(a%2==0&&a>=6) even(a); /*函数调用语句 */
else printf("The %d isn't even number\n",a);
}
目录265
void even(int x) /*函数定义 */
{ int i;
for(i=2;i<=x/2;i++)
if(isprime(i))
if(isprime(x-i))
{printf("%d=%d+%d\n",x,i,x-i);
return ;
}
}
int isprime(int a) /*函数定义 */
{ int i,k= sqrt(a);
for(i=2; i<=k; i++) if (a%i==0) return 0;
return 1;
}
目录266
8-2-5 函数的递归调用
递归调用:一个函数直接或间接的调用自己。
分析递归问题的关键,
1)递推公式:每次调用自己时参数的变化规律;
2)结束条件:递归调用结束的条件。
例 8.8 求 n!.
分析:
1) n!= n*(n-1) ! (n>=1)------规律
2) 0!=1 (n=0) ------递归结束条件目录267
main()
{ int n; long p;
long f(int); /*对被调函数的声明 */
printf("\n input n:"); scanf("%d",&n);
p=f(n); /*函数调用 */
printf("\n n!=%ld",p);
}
long f(int m) / *函数定义 */
{long t;
if(m==0||m==1) t=1;
else t=m*f(m-1); /*函数递归调用 */
return(t); /*函数的返回 */
}
目录268
递归调用过程
f(5)=f(4)*5
=f(3)*4
=f(2)*3
=f(1)*2
=1
回推递推
1) 回推
2) 递推目录269
类似递归问题练习题
1) 猴子吃桃 ;
2) Fibonacci数列;
3) 猜年龄:第一个人说它比第二个人大 4岁,第二个人说它比第三个人大 4岁,第三个人说它比第四个人大四岁,第四个人 10岁,问:第一个人多大?
目录270
例,Hanoi 塔问题
移 n 个盘子从 A柱到 C柱借助 B柱,规定一次只能移一个。
可分解为以下三步,
1,移 n-1个盘子从 A柱到 B柱借助 C柱
2,移 1 个盘子从 A柱到 C柱
3,移 n-1个盘子从 B柱到 C柱借助 A柱函数目录271
8-3 数组作函数参数
1,数组元素作函数实参;
同变量作函数实参,为单向“值传递”;
2,数组名作函数参数(实参和形参均为数组名)
1) 要求在主调和被调函数中分别定义数组;
2) 实参数组和形参数组类型应一致;
3) 调用函数时,将实参数组首地址传递给形参;则实参和形参数组共用同一段内存,二者互相影响;
4) 形参数组可不定义大小,另设参数传递数组大小;
目录272
例 8.12 编写函数实现:用选择法对 n个整数排序。
主程序的算法:
S1,输入一批数据,存入一维数组 aa。
S2,调用函数 sort()对数组 aa中的数据排序。
S3,输出数组 aa中的各元素。
函数 sort()的编写,
首先,确定函数的类型,void; 参数:一维数组的首地址,数据的个数,共 2个;
然后编写函数体,实现排序。
目录273
/*主函数 */
#define N 10
main()
{ int aa[N],i;
void sort(int b[ ],int n);
printf("\n enter integers for sort;");
for(i=0;i<N;i++) scanf("%d",&aa[i]);
sort(aa,N); /*函数调用 */
printf("\n after sort:\n");
for(i=0;i<N;i++) printf("%6d",aa[i]);
}
目录274
/*函数 sort()*/
void sort(int b[ ],int n)
{ int i,j,t,k;
for(i=1;i<=n-1;i++)
{k=0;
for(j=1;j<=n-i;j++) if(b[k]<b[j]) k=j;
t=b[k]; b[k]=b[n-i]; b[n-i]=t;
}
}
目录275
例 8.12 运行结果与图示:
enter 5 integers:1 2 5 7 3↙
after sort,7 5 3 2 1
目录276
两种参数传递方式的比较
传值调用,单向数据传递,对形参的改变不影响实参的值,且只能通过
return语句返回最多一个值。例 8.4
传地址调用,实参传给形参的是数据的地址,目的是:形参与实参共用同一片存储单元,对形参的改变实际上是对实参的改变,从而实现主、被调函数之间的多个数据传递。例 8.12
函数目录277
8.4 局部变量和全局变量
局部变量,在函数体内定义的变量。
作用范围:只在本函数内有效。
全局变量,在函数体外定义的变量作用范围:从定义位置开始,到本源程序文件结束。
目录278
/*全局变量 x,y*/
int x=100; float y=66.6;
f1()
{float y=0; /*局部变量 y*/
printf(―x=%d\t‖,x);
printf(―y=%f\t‖,y);
}
int z=1;
f2()
{int i; /*局部变量 i*/
for(i=1;i<5;i++) putchar('*');
printf("\n z=%d\t ",z);
}
main()
{f1(); f2();
printf(―y=%f\n‖,y);
/*输出全局变量 y*/
}
运行结果:
x=100 y=0.000000 ****
z=1 y=66.600000
/*全局变量 x,y在程序内起作用 */
/*局部变量 y的作用范围内,全局变量 y不作用 */
目录279
int d=1; /*全局变量 d*/
fun(int p)
{int d=5; /*局部变量 d*/
d+=p++; /*使用局部变量 d*/
printf("%d",d);
}
main()
{int a=3;
fun(a);
d+=a++; /*使用全局变量 d*/
printf("%d",d);
}
运行结果,84
结论:在同一源程序文件中,若外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。
目录280
局部变量和全局变量小结
局部变量:保证了函数之间的独立性。(常用)
全局变量:增加了函数之间数据传递的通道,但降低了函数间的独立性,降低了程序的清晰性,因此副作用太大。除非特别需要时,一般不用。
占用内存情况:
局部变量仅当他所在的函数被调用时才存在,执行完该函数返回后,该变量不再存在
全局变量在程序的全部执行过程中一直存在,直至程序执行完,才释放它所占的内存空间函数目录281
8.5 变量的存储类别
变量和函数均有两个属性:
数据类型和存储类别
存储类别指数据在内存中的存储方式。根据变量的
“生存期”不同,变量的存储类别包含以下四种:
1) 自动变量,auto
2) 静态变量,static
3) 寄存器变量,register
4) 外部变量,extern
目录282
自动变量( auto)
函数的形参和在函数中定义的变量(通常省略存储类别,)即隐含指定为自动变量。前面 1~7章中的变量均属自动变量。
自动变量在需要时系统给他门分配存储空间,在函数调用结束时自动释放这些存储空间。
例 auto int a=2,b=3; 与 int a=2,b=3;等价。
目录283
静态 变量 ( static)
1、静态局部变量:
作用域为本函数内部
存储类别为静态存储类,因此其生存期与该函数所在程序运行期间相同。
如果希望变量的值在函数调用结束后仍保留,可指定该变量为“局部静态变量” ---
即当函数调用结束时能保留原值,在下一次调用该函数时该变量的值是上一次函数调用结束时的值,直至程序运行结束。
目录284
func(int a,int b) /*例 8.6.1*/
{static int m=0,i=2;/*静态局部变量 m,I*/
i= i+ m+1;
m=i+a+b;
return(m);
}
main()
{int k=4,m=1,p; /*局部变量 k,m,p*/
p=func(k,m);
printf("%5d",p);
p=func(k,m);
printf("%5d\n",p);
}
目录285
int d=1; /*全局变量 d*/ /*例 8.6.2*/
fun(int p)
{static int d=5; /*静态局部变量 d*/
d+=p;
printf("%5d",d);
return(d);
}
main()
{int a=3; /*自动变量 a*/
printf("%5d\n",fun(a+fun(d)));
}
目录286
结论:
1,局部静态变量在静态存储区内分配存储单元,在整个程序运行期间都不释放;
2,局部静态变量赋初值在编译时只进行一次 (例 8-17);
3,静态变量不赋初值自动赋 0;
4,局部静态变量在调用函数结束后仍然存在,但其他函数不能引用它;
5,静态存储缺点:
1,占用存储空间;
2,降低程序可读性。
目录287
Register 变量
register int k;
则给变量 k分配的空间为某个寄存器。
优点:速度快。
只有局部变量和形参可以定义为 register变量。因为机器的寄存器数量有限,因此该类型不常用。
目录288
外部变量( extern)
外部变量属于静态存储方式:
1,静态外部变量 ── 只允许被本源文件中的函数引用定义格式为,static 数据类型 外部变量表;
2,非静态外部变量 ── 允许被其它源文件中的函数引用
定义时缺省关键字 static的外部变量,即为非静态外部变量 。
其它源文件中的函数,引用非静态外部变量时,需要在引用函数所在的源文件中进行说明:
extern 数据类型 外部变量表;
目录289
务必牢记:
关键字,static‖在不同的地方所起的作用是不同的!
把局部变量改变为静态内部变量后,改变了它的存储方式,即改变了它的生存期。
把外部变量改变为静态外部变量后,改变了它的作用域,限制了它的使用范围。
函数目录290
8.6 内部函数与外部函数
外部函数:如不加特别说明,函数都是全局的,即外部的,一个函数可以调用另一文件中的函数。
内部函数:存储类别为 static,该函数仅限于本程序文件使用,其它程序不能调用它。
目录291
综合题 1
编程实现小学生算术练习系统:
主菜单包括 5项(加法、减法、乘法、除法、退出),前四项中每一项又包括子菜单(一级、二级、三级、返回 4
项),其中一级实现 10以内的运算,二级实现 50以内的整数运算,三级实现 100以内的整数运算;
进入某一级后,反复练习(由机器产生两个随机数,用户输入运算结果,输出正确或错误)待结束时给出本级题目中计算正确的百分比。
要求每个功能分别用函数实现。
目录292
综合题 2
自动阅卷程序,设单选题 20个 (2分 /题 ),多选题 20个 (3
分 /题 )。编写函数实现:阅单选题,阅多选题,阅一个人的答题;编写主函数实现批阅 N个人的答题卡
Main(),
1)输入正确答案,分别存入一维数组 dd,二维数组 ss;
2) 批阅 N个人的答题(用循环),将成绩存入数组 sc。
3) 输出每个人的最后成绩。
目录293
Person(),
函数类型为 int; 参数两个,dd,ss; 函数体:
1)输入某人的答案,分别存入 dd1和 ss1,
2) 分别调用函数 single()和 many()判别对错并计分,
返回总成绩。
Single():函数类型 int,参数 dd,dd1;
函数体:统计数组 dd与 dd1中相同元素的个数,乘 2即得单选题的成绩,返回该成绩。
Many():函数类型 int,参数 ss,ss1;
函数体:统计数组 ss与数组 ss1中对应行元素相同的行数,乘 3即得多选题的成绩,并返回。
目录294
#include "stdio.h" /*程序 1*/
#define N 400 /*num of person*/
#define NUM 20 /*num of question*/
main()
{ char dd[NUM+1],ss[NUM+1][5];
int sc[N+1],i;
printf("\n enter right answer of single select:\n");
printf("\n(format:press enter after finished
inputing all of answers.\n");
for(i=1;i<=NUM;i++)
dd[i]=getchar();
getchar();
目录295
printf("\n enter answer of multi select:\n");
printf("\nformat:(press enter after inputing a
question)\n");
for(i=1;i<=NUM;i++)
gets(ss[i]);
for(i=1;i<=N;i++)
sc[i]=person(dd,ss);
printf("\n no,score,");
for(i=1;i<=N;i++)
printf("\n %-6d%-6d",i,sc[i]);
}
目录296
int person(char dd[],char ss[][5])
{ char dd1[21],ss1[31][5];
int s,i; int signle(),many();
printf("\n enter answer of single select:");
for(i=1;i<=NUM;i++) dd1[i]=getchar();
getchar();
printf("\n enter answer of multi select:");
for(i=1;i<=NUM;i++) gets(ss1[i]);
s=single(dd,dd1)+many(ss,ss1);
return(s);
}
目录297
int single(char dd[ ],char dd1[ ])
{ int n=0,i;
for(i=1;i<=NUM;i++) if(dd[i]==dd1[i]) n++;
return(2*n);
}
int many(char ss[ ][5],char ss1[ ][5])
{ int n=0,i;
for(i=1;i<=NUM;i++)
if(!strcmp(ss[i],ss1[i])) n++;
return(n*3);
}
函数目录298
多个文件组成一个程序的方法
1、使用 project生成项目文件见 ch16.8
2,使用 include 将所有的文件包含到一个文件中。
例 8.5 (3)
目录299
作业
8.1,8.2,8.3,8.4,8.10
函数目录300
第 9章 编译预处理
编译预处理:在编译源程序之前根据预处理命令对源程序进行的预加工,由编译系统中的预处理程序完成;
格式:以符号,#,开头;
如,#include <math.h>
位置:宏定义与文件包含命令一般放在程序的开头(原则上可以放在程序中的任意位置);
作用域:从定义起直到其所在源程序的末尾;
使用预处理命令的好处:
改进程序设计环境,提高编程效率,易读、易改目录301
宏定义
1,不带参数的宏定义
常用于将一些有特殊含义的常量定义成符号常量,使程序易读、易改。
例如在程序中直接用 3.14,2.71828,存在问题:
1) 数值的含义不明显,影响程序可读性;
2) 要修改时必须一一修改,即麻烦又易出错。
宏定义形式,#define 标识符 字符串如,#define PI 3.14159
目录302
常用于实现简单的操作,与函数功能类似。
例 9.2:求三数中的最大值。
#define MAX(a,b) (a>b)?(a),(b)
main()
{int x=10,y=20,z=30;
int t;
t=MAX(x,y);
printf(―\n%d‖,MAX(t,z));
}
2、带参数的宏定义目录303
2、带参数的宏定义
带参宏定义的一般形式:
#define 宏名 (参数表 ) 宏体
如,#define MAX(a,b) (a>b)?(a),(b)
预处理时进行如下宏替换:
用宏体替换宏名的同时,用实参分别替换形参,而不是用实参的值,即替换时不进行任何计算。
本例中的语句,t=MAX(x,y);
若理解为,t=(10>20)?(10),(20); 则错。
目录304
文件包含
定义:
文件包含指用 #include 命令将另一指定的源文件包含进当前源程序文件中 #include 命令所处的位置,共同组成一个程序文件,然后对合并后的源文件进行编译、
连接,生成一个目标文件。
文件包含命令的一般形式有两种:
格式 1,#include <文件名 >
格式 2,#include "文件名 "
如,#include <stdio.h>
#include "math.h"
目录305
图 9.1 文件包含命令的预处理过程目录306
文件包含的应用
用法 1:用 include命令包含标准库函数的头文件 ;
例,#include <stdio.h>
#include ―string.h‖ 等
用法 2:用 include命令包含用户自己编写的文件 ;
例:下面程序实现将两个源程序组合为一个源程序目录307
主程序文件 file_m.c:
#include ―a:\file1.c‖
#include ―c:\my\file2.c‖
main()
{int x; float y;
scanf(―%d‖,&x);
if(x>0) y=f1(x);
else y=f2(x);
printf(―x=%d,y=%f
\n‖,x,y);
}
文件 file1.c:
float f1(int a)
{ float z;
z=a*a+1;
return(z);
}
文件 file2.c:
float f2(int b)
{ float m;
m=2.5*b-1;
return(m);
}
目录308
条件编译
条件编译,就是根据不同的编译条件选择源程序的某些程序段进行编译,从而使同一个源程序在不同的编译条件下可以产生适应不同要求的目标代码程序。
条件编译命令(略 )
条件编译的应用:
1) 便于程序调试
2) 便于程序移植,拓展 c的编程环境
3) 便于编写通用软件
END
目录309 指针
指针的概念
指针变量的定义和引用
指针变量作为函数参数
指针与数组
字符串与指针
函数指针
返回指针值的函数
指针 小结目录310
主要内容:
指针和地址
指针常量和指针变量
指针与数组的结合应用
指针与函数的结合应用
指针数组与指向指针的指针变量重点:
指针变量的定义与引用
通过指针访问数组元素
指针变量作为函数参数
返回指针值的函数目录311
指针的概念
理解数据在内存中的 存储如,int a,b;
a=30; b=40;
指针单元地址 变量内容
1000H 30
1002H 40
p1地址 p1内容
2000H 1000H
内存变量的访问方式:
直接访问:按变量地址存取变量值;
间接访问:将变量地址放在另一变量中;
如 p1=&a;
指针的概念指针 ---指针就是一个变量的地址;
指针变量 ---专门存放 另一变量的地址 的变量;
目录312
数据在内存中的存储目录313
指针变量的定义
1,格式,类型标识符 * 指针变量名
2,说明:
类型规定该指针变量指向变量的类型;
*表示该变量为指针变量。如
int *p1,*p2;
float *p3;
char *p4;
p1=&a;形象记为,30p1
a
目录314
指针变量的引用
有两个运算符可以引用指针变量:
&--取地址 如 &a—变量 a的地址;
*--指针运算符 如 *p1—p1指向的变量;
如果定义:
int i; int *p1; p1 = &i ;
若指针变量 p1指向变量 i,则访问 i 有两种方式:
1,直接访问。如 i =100; j = i
2,通过指针变量间接访问。 *p1 = 100;
目录315
指针变量的定义和引用
t
p 25
a例 10-1
p1
p2
10
a
20
b
例 10-2
目录316
说 明
1,指针变量在未规定它指向哪一个变量时不能用 *运算符访问指针!如
int a; int *p; *p = 100;
2,区分 *运算符在不同场合的作用,编译器能够根据上下文环境判别 *的作用。如
int a,b,c; int *p;
p = &a; *p = 100; c = a * b;
目录317
例 10.2中通过指针变量 p1,p2交换了变量 a和 b的值。
那么,为什么要用指针变量而不直接用变量名呢?
再回顾:编写函数 swap()交换两个变量的值,函数如下:
viod swap( int a,int b)
{ int t; t=a; a=b; b=t;}
main()
{ int x=5,y=6;
swap(x,y);
printf("\nx=%d,y=%d",x,y);}
结果,x=5,y=6
程序并未实现交换 x,y的值,why?
因为参数传递是单向的,对形参的改变不影响实参的值。
指针目录318
解决以上问题的方法是
-----指针变量作函数参数
指向变量的指针变量作函数形参,接收主调函数中实参变量的地址,实现传地址调用。
即在被调函数中通过指针变量访问主调函数中对应的变量,当返回主调函数后,主调函数就得到了这些已修改过的变量的值。
因此,“传地址”调用可以实现函数间多个数据的“双向传递”。
目录319
void swap(int *p,int *q);
main()
{ int a,b; printf("\n input 2 integers:");
scanf("%d%d",&a,&b);
printf("before swap,a=%d,b=%d\n ",a,b);
swap(&a,&b);
printf("after swap,a=%d,b=%d\n ",a,b);
}
void swap(int *p,int *q) /*交换两个变量的值 */
{ int t;
t=*p; *p=*q;*q=t;
}
目录320
调用过程中参数的对应关系:
Main,swap:
a p
b q
*p? a
*q? b
在 swap函数中通过指针变量 p,q就可以访问 main函数中的变量 a,b。
&a
&b
目录321
考虑 Swap做如下修改:
void swap(int *p,int *q);
main()
{ int a,b; printf("\n input 2 integers:");
scanf("%d%d",&a,&b);
printf("before swap,a=%d,b=%d\n ",a,b);
swap(&a,&b);
printf("after swap,a=%d,b=%d\n ",a,b);
}
void swap(int *p,int *q) /*交换两个变量的值 */
{ int *t;
t=p; p=q; q=t;
}
目录322
指针变量作为函数参数 —结论
形参为指针变量,函数调用时:
将实参变量的地址传递给形参指针指针变量地址 5实参指针变量变量地址形参指针? 那么:
若在被调函数中 改变形参指针的值 调用结束后 不会影响实参变量的值 ;
若在被调函数中改变 形参指针指向变量的值 调用结束后则 会影响实参变量的值 ;
目录323
指针与数组
指针可以指向数组和数组元素,当一个指针指向数组后,对数组元素的访问,既可以使用数组下标,也可以使用指针。并且,用指针访问数组元素,程序的效率更高(用下标访问数组元素程序更清晰)。
本节介绍:
1,指向数组元素的指针变量
2,通过指针引用数组元素
3,数组名作函数参数
4,多维数组的指针指针目录324
指向数组元素的指针变量
指向数组元素的指针变量,其类型应与数组元素相同。
如:
int a[10]; /* 元素为整型 */
float b[10] ; /* 元素为实型 */
int *p; /* 可以指向数组 a的元素 */
float *pf; /* 可以指向数组 b的元素 */
为让指针 p指向数组 a,应把数组 a的起始地址赋给指针变量 p。 以下语句均使指针 p指向数组 a:
1,p = &a[0];
2,p = a; /* 把数组 a的起始地址赋给 p*/
数组与指针 指针目录325
通过指针引用数组元素数组与指针 指针如果指针 p指向数组 a,则:
p+1指向 下一个元素 a[1],
注意不是将 p值简单加 1。
可以使用 *(p+i)访问元素 a[i]。
p+i也可以记作 a+i。 指向元素 a[i]
指向数组的指针变量也可带下标,
如,p[i]与 *(p+i)等价,表示元素 a[i]
目录326
访问数组元素方法
1,下标法; a[i] (常用,很直观)
2,用指针访问各元素; *p++ (常用,效率高)
应注意问题:
1,若指针 p指向数组 a,虽然 p+i与 a+i,*(p+i)与
*(a+i)意义相同,但仍应注意 p与 a的区别 (a代表数组的首地址,是不变的; p是一个指针变量,可以指向数组中的任何元素 ) ;
2,指针变量可以指向数组中的任何元素,注意指针变量的当前值。
3,使用指针时,应特别注意避免指针访问越界。
数组与指针 指针目录327
数组名作函数参数
数组名代表数组首地址。因此
它作实参在函数调用时,是把数组首地址传送给形参。
这样,实参数组和形参数组共占同一段内存区域 。从而在函数调用后,实参数组的元素值可能会发生变化。
数组与指针 指针实参数组形参数组目录328
将数组 a中 n个元素按反序存放算法:
a[0]与 a[n-1]交换,a[1]与 a[n-2]交换,.....,
a[(n-1)/2]与 a[n-int((n-1)/2)]交换。
实现:
用 i,j作元素位置变量,开始 i=0,j=n-1。
将 a[i]与 a[j]交换,然后 i加 1,j减 1,
直到 i=(n-1)/2。
指针数组与指针目录329
将数组 a中 n个元素按反序存放 ---程序
void inv(int x[],int n)
{int t,i,j,m=(n-1)/2;
for (i=0; i<=m; i++)
{ j = n - 1 - i; t = a[i]; a[i] = a[j]; a[j] = t;}
} /* 函数的返回值类型是 void,不返回任何值 */
main ()
{ static int i,a[10] = {3,7,9,11,0,6,7,5,4,2};
inv(a,10);
printf("the array hans been inverted:\n");
for(i=0; i<10; i++) printf("%d ",a[i]);
printf("\n");
}
指针数组与指针目录330
函数 inv()可以用指针作形参
void inv(int *x,int n)
{
int *p,t,*i,*j,m=(n-1)/2;
i = x; /* 指针 i指向数组第一个元素 */
j = x + n - 1;/* 指针 j指向数组最后一个元素 */
p = x + m; /* 指针 p指向数组中间一个元素 */
for(; i<=p; i++,j--)
{ t = *i; *i = *j; *j = t; }
return;
}
指针数组与指针目录331
从 10个数中找出其中最大值和最小值方法 1、实参和形参均用数组变量
Void max_min_value(int array[],int n)
{
int *p,*end; /* p是数组元素指针 */
end = array + n; /* 指向数组尾 */
max = min = *array; /* 第一个元素 array[0] */
for(p=array+1; p<end; p++) /* p++指向下一个元素 */
if (*p > max) max = *p;
else if (*p < min) min = *p;
return;
}
指针数组与指针目录332
int max,min; /* 全局变量,最大值和最小值 */
void max_min_value(int *array,int n)
{int *p,*end; /* p是数组元素指针 */
end = array + n; /* 指向数组尾 */
max = min = *array; /* 第一个元素 array[0] */
for(p=array+1; p<end; p++) /* p++指向下一个元素 */
if (*p > max) max = *p;
else if (*p < min) min = *p;
return;
}
指针数组与指针方法 2、形参和实参均使用指针变量目录333
数组名作函数参数小结
数组作函数的参数,实参和形参之间传送数组的首地址,
首地址可以用指针表示,也可以用数组名表示,因此,
实参和形参有以下四种组合情况:
组合情况 实参 形参
1 数组名 数组名
2 数组名 指针
3 指针 指针
4 指针 数组名指针数组与指针目录334
多维数组的指针
二维数组
static int a[3][4]
={{1,3,5,7},{9,11,13,15},{17,19,21,23}};
理解为:有三个元素 a[0],a[1],a[2],每个元素代表一行,每个元素是一个包含 4个元素的数组。
数组与指针 指针目录335
多维数组的指针
数组名 a代表:整个二维数组的首地址,也是元素
a[0][0]的地址,同时代表第一行元素的首地址。
a+1表示第二行元素的首地址。
a+2表示第三行元素的首地址。
数组与指针 指针目录336
多维数组的指针数组与指针 指针目录337
字符串的表现形式字符数组 字符指针指针
main()
{ static char string[ ] = "I love
China!";
printf("%s\n",string);
}
string是数组名。数组可以用下标访问也可以用指针访问。
string[4] 也可以用 *(string+4)
来访问,string+4是指向字符 v
的指针。
main()
{ char *string ="I love China!";
printf("%s\n",string);
}
string是一个指针变量。语句
char *string = "I love China!";
等价于 char * string;
string = "I love
China!";
它把字符串常量的首地址赋给指针 string.不能理解为把字符串常量赋值给指针变量。
目录338
字符指针变量与字符数组的区别字符数组 字符指针变量组成 由若干元素组成,每个元素中放一个字符。
2或 4字节,存放字符串的首地址。
赋初值方式
static char str[]={"I love
China!"};
char *a = "I love China!";
赋 值 方式
char str[14];
str = "I love China!"
char * a;
a = "I love China!";
占用内存字符数组一个元素占一字节内存,且在编译时分配。
指针变量中只可以放一个地址值,且编译时未指定。
输 入 方式
char str[10];
scanf("%s",str);
char *a;
scanf("%s",a);
指针目录339
函数指针
一个函数在编译时被分配一个 入口地址 (第一条指令的地址),这个入口地址称为 函数的指针 。如果一个指针变量的值等于函数的入口地址,称为 指向函数的指针变量,简称为 函数指针 。
指针
int max(int x,int y); /*原型 */
main()
{int (*p)(int,int);
int a,b,c;
p = max;
scanf("%d,%d",&a,&b);
c = (*p)(a,b);
printf("a=%d,b=%d,max=%d",a,b,c);
}
目录340
函数指针说明
1,函数指针定义的一般形式:
函数返回值类型 ( *指针变量名)(形参类型 )
例,int (*p) (int,int);
2,语句 p=max,把函数 max的入口地址赋给函数指针 p,
因此,c=(*p)(a,b)中,*p就是调用函数 max。
3,(*p)( )表示一个指向函数的指针变量,它可以先后指向不同的函数。
4,语句 p=max中,函数名代表函数的入口地址,max后不跟函数参数。
5,用函数指针调用函数时,应指定实参。
6,指向函数的指针变量 p,象 p++,p--,p+n等运算是无意义的。
指针目录341
返回指针值的函数
一般形式类型标识符 * 函数名(参数表)
例 int *a (int x,int y)
声明一个函数,函数名为 a,其返回值类型是“指向整型的指针” 。
例 10-25
float * search( float (*pointer)[4],int n)
{float *pt;
/* pt是指向实数的指针,pointer是指向数组的指针 */
pt = *(pointer+n); /* pt = (float *)(pointer + n) */
return pt;
}
指针目录342
返回指针值的函数
又如单向链表中输出链表元素值的函数,
NODE *pout(NODE *head)
{NODE *p;
p=head;
while(p!=NULL)
{printf(―%d ‖,p->data);
p=p->next;}
printf(―\n‖);
return (head);
}
指针
data
next
目录343
指针的数据类型 小结定义 含义
int i; 定义整型变量 i
int *p; p是指向整型数据的指针变量
int a[n]; 定义数组 a,元素类型为 int,元素个数是 n
int (*p)[n]; p是指向数组的指针,数组有 n个整型数
int f(); f是函数,返回值是 int
int *p(); p是函数,返回值是指针,该指针指向整型数据
int (*p)(); p是函数指针,所指向的函数返回整型数据
int **p; p是指针,指向一个指向整型数据的指针指针目录344
指针运算小结
1,指针变量加 /减运算
2,指针变量赋值
p = &a; 变量 a的地址赋给 p,即指针 p指向 a
p = array; 数组 array首地址赋给 p
p = max; 函数 max的入口地址赋给 p
p1 = p2; 指针 p2的值赋给指针 p1,即 p1,p2所指的数据相同;
3,空指针 p = NULL表示 p不指向任何数据。
在 stdio.h中,NULL被定义为 0,#define NULL 0
注意:空指针不指向任何数据,与 p未赋值不同。
指针目录345
指针运算小结
4,指针变量相减。
当 p1,p2指向同一数组的元素时,p2-p1等于 p1,p2
之间的元素个数。
5,两个指针的比较当 p1,p2指向同一数组的元素时可以比较,如 p1 > p2。
6,空类型指针 void *
void *p,表示 p是空类型指针,它可以指向任何数据类型。
空类型指针与其他类型指针之间赋值时,应进行强制类型转换,例
char *p1; void *p2; p1 = (char *)p2;
指针目录346
结构体结构体
概 述
结构体变量定义引用
结构体数组
结构体指针
链 表
共用体
枚举类型目录347
概述
数组中元素属于同一类型,有时需要将不同类型的数据合成一个整体(结构体 )
结构体( structure) 是一种数据类型,它把互相联系的数据组合成一个整体 。例:
一个学生的学号、姓名、性别、年龄、成绩、地址,是互相联系的数据,在 C语言中用“结构体( structure) ‖
来定义。
结构体目录348
结构体类型的定义
struct student
{ int num; /* 学号 */
char name[20]; /* 姓名 */
char sex; /* 性别 */
int age; /* 年龄 */
float score; /* 成绩 */
char addr[30]; /* 地址 */
};
其中 struct 是关键字,student是定义的结构体类型名。
结构体中的每一项数据,称为结构体“成员” 或“分量”
结构体目录349
结构体变量的定义
1,先定义结构体类型,再定义变量。
struct student student1,student2;
2,在定义类型的同时定义变量
struct student
{

} student1,student2;
3,直接定义变量
struct
{

} student1,student2;
结构体目录350
结构体变量的定义
1,说明:
1) 请注意区分类型和变量两个不同的概念。
2) 结构体成员单独引用时相当于普通变量。
3) 结构体成员名可以与变量名重名,互不影响。
4) 成员可以是另一个结构体变量。例如结构体目录351
结构体变量的定义
struct date /* 日期结构 */
{ int month;
int day;
int year;
};
struct student
{ int num;
char name[20];
char sex;
int age;
struct date birthday; /* 成员是另一个结构体变量 */
char addr[30];
} student1,student2;
结构体目录352
结构体变量的引用
1,一般情况下,不能将一个结构体变量作为整体来引用,
只能引用其中的成员。
引用结构体成员的方式:
结构体变量名,成员名其中,是“成员运算符”(分量运算符)
例如:
student1.num,student1.name
student1.sex,student1.age
student1.score,sutdent1.addr
结构体目录353
结构体变量的引用
2,当成员是另一个结构体变量时,应一级一级地引用成员。例如:
student1.birthday.month;
student1.birthday.day;
student1.birthday.year;
3,仅在以下两种情况下,可以把结构体变量作为一个整体来访问。
( 1) 结构体变量整体赋值,例,
student2 = student1;
( 2) 取结构体变量地址,例,
printf("%x",&student1);
结构体目录354
结构体变量的初始化
可以在定义结构体变量的同时赋初值。例如:
struct student
{
long int num; /* 学号 */
char name[20]; /* 姓名 */
char sex; /* 性别 */
char addr[20]; /* 地址 */
} a = {89031,"Li Lin",'M',"123 Beijing Road"};
结构体目录355
结构体数组 的定义
struct student
{int num;char name[20];char sex;
int age;float score;char addr[30];};
struct student stu[3];
结构体目录356
结构体数组的初始化
struct student
{int num; char name[20];
char sex; int age;
float score; char addr[30];
}
stu[3] = {
{101,"Li Lin",'M',18,87.5,"103 Bejing Road―},
{101,"Zhang Fun",'M',19,99,"130 Shahai
Road―},
{101,"Wang Min",'F',20,78.5,"10Zhongshan
Road―}
};
结构体目录357
结构体数组举例
输入 50个学生的 5门课的成绩并计算总分、平均分,
输出学生成绩。
(学生信息包括:班级、学号、姓名、数学、语文、英语、政治、计算机、总分、平均分)
解决方法:
1,定义一结构体类型;
2,定义一个结构体数组;
3,输入 50个学生的信息;
4,计算总分、平均分;
5,输出 50个学生的成绩信息。
结构体目录358
结构体指针
结构体指针:指向结构体变量的指针。
结构体
struct student
{
long num;
char name[20];
char sex;
float score;
};
struct student stu_1;
/*结构体变量 */
struct student *p;
/*结构体指针 */
p = &stu_1;
目录359
结构体指针
结构体指针,通过指向运算符- >引用结构体中的成员。例,
p->num
p->name
p->sex
p->score
教材中:( *p),num方式几乎不用。
结构体目录360
指向结构体数组的指针
struct student stu[3];
struct student *p = stu; /* p指向 stu[0] */
p = &stu;
p->num;
/* 引用 stu[0].num */
p++;
p->num;
/* 引用 stu[1].num */
p++;
p->num;
/* 引用 stu[2].num */
结构体目录361
用结构指针作函数的参数
1,结构体变量的成员作函数的参数。
与普通变量作函数参数的用法相同。值传送,不能修改实参的值。
2,结构体变量作函数的参数。
将结构体变量的全部成员值传送给函数,效率低,
不能修改实参的值。
3,结构体指针作函数的参数。
将结构体变量的地址传送给函数,效率高,可以修改实参的值。
结构体目录362
链 表结构体链表概述建立链表输出元素删除元素插入元素综合操作目录363
数组:必须事先定义固定的长度,不能适应数据动态增减的情况。
链表动态地进行存储分配,可以适应数据动态增减的情况,且可以方便地插入、删除数据项。
A B C
^
D
HEAD
1,引进链表原因
A B C D
结构体链表操作目录364
2,术语介绍
,头指针” head指向链表第一个元素。
链表元素称为,结点” 。 结点中包含两部分内容:
1.结点数据;
2.指向下一个结点的指针
表尾结点的指针为空( NULL)
A B C
^
D
HEAD
结构体链表操作目录365
3,链表结点类型定义
链表结点用 C结构体描述:
struct node
{int num;
struct node *next;
};
A
结构体链表操作结点数据域,可根据实际应用设定结点指针域,存放下一结点的地址目录366
4,链表相关函数
1,void * malloc(unsigned size)
在动态存储区分配长度为 size的连续空间,并返回指向该空间起始地址的指针。
如,p=(NODE *)malloc(20);
2,void free(void * p)
释放指针 p指向的内存空间。
如,free(p)
20B
P
结构体链表操作目录367
1,建立链表思路
2,建立链表其它结点:
1,生成(申请)新结点 p;
2,新结点 data域赋值,next域赋 NULL ;
3,将新接点链接到链表表尾。
1
^
p
head
^
2
p
1,建立链表头结点 HEAD:
1,生成(申请)新结点 p;
2,新结点 data域赋值,next域赋 NULL;
3,将 HEAD指针指向新接点。
3,重复 2即可依次建立链表各结点。
结构体链表操作目录368
2,分解语句介绍
1,建立链表头结点 HEAD:
1,申请 p;
p=(struct node *)malloc(sizeof(struct node ));
2,新结点赋值 ;
p->num=?A‘ ; p->next=NULL;
3,HEAD指向新接点。 head=p; q=p;(q指向链表尾结点 )
2,建立链表其它结点:
1,生成 p; (同上 )
2,新结点赋值; (同上 )
3,新接点链接到表尾。 q.next=p; q=p;
3,重复 2即可依次建立链表各结点。
^
1
head ^
2
q
^
3
pq
结构体链表操作目录369
3.链表的建立过程演示单击鼠标开始 …
A B C D E
head ^
F
END
结构体链表操作目录370
4,建立链表函数代码
类型和常量定义
struct node
{
int num;
struct student *next;
};
#define LEN sizeof(struct node)
#define NULL 0
结构体链表操作目录371
struct student * creat( )
{ struct student *head = NULL;,*p,*q;
int num;
scanf("%d",&num);
while (num != 0)
{ p = (struct student *)malloc(LEN); p->next = NULL;
if (head = NULL) { head = p; q=p;} /* 建表头 */
else {q->next = p; q = p;} /* 新结点接到表尾 */
scanf(―%d‖,&num);
}
return (head); /* 返回表头指针 */
}
结构体链表操作目录372
只要已知表头结点 Head,就可以通过每个结点的 next域 找到下一个结点,依次访问链表的全部结点。
1,输出元素基本思路
A B C D E
head ^
F
P P P P P P
结构体链表操作目录373
2,具体实现方法
1.用一指针变量 p指向 head;(p=head;)
2.当 p!=NULL时做
输出 p->num;(输出格式自己设定 )
P指向下一个结点元素; (p=p>next;)
结构体链表操作目录374
3.链表元素的输出过程演示单击鼠标开始 …
A B C D E
head ^
F
END
P P P P P P
A B C D E F
结构体链表操作目录375
void print(struct student *head)
{ struct student *p;
printf("\nThe records are:");
p = head;
if (p == NULL) return;
while(p != NULL)
{printf(,%d",p->num);
p = p->next;
}
printf(“\n”);
}
4,输出元素函数代码结构体链表操作目录376
思考题
1,考虑反序建立链表的过程。
2,如何计算链表长度(元素个数)。
3,如何在链表中查找指定的元素。
结构体链表操作目录377
插入元素的三种情况图示结构体链表操作在表中间在表头在表尾目录378
插入元素算法链表操作 结构体目录379
向链表中插入元素代码
struct student * insert(struct student *head,struct
student *stud)
{ struct student *p0=stud,*p1 = head,*p2;
/* p0插入 p1之前,p2之后 */
if(head == NULL) {head = p0; p0->next = NULL;}
else
if(p0->num<p1->num) {head = p0; p0->next = p1;}
else
{while ((p0->num > p1->num)&&(p1!=NULL))
{p2 = p1; p1 = p1->next;}
p2->next = p0; p0->next = p1; }
return (head);
}
链表操作 结构体目录380
链表删除元素结构体链表操作一般情况:
还应分情况考虑:
1,要删除的结点是头结点
2,要删除的结点不是头结点
head
目录381
链表删除删除头结点,
删除其它结点,
结构体链表操作 文件目录382
链表删除结构体链表操作目录383
综合操作
目的:
学习使用 C语言解决实际问题,编写较复杂的程序。
熟悉 C函数调用过程,特别注意:
实参赋值、参数传递和函数返回值的处理。
要求:
利用链表结构完成数据的输入、输出、插入、删除等操作。
结构体链表操作目录384
问题分析主程序建立链表遍历链表插入元素删除元素退出目录385
问题分析主菜单插入元素 删除元素输出链表建立链表退出目录386
主菜单设计
采用选择式菜单,格式如下:
Main Menu
~~~~~~~~~~~~~~
1,Create linklist,
2,Print linklist,
3,Insert element,
4,Delete element.
0,Exit,
~~~~~~~~~~~~~~
Select(0~4),
目录387
主菜单代码设计
main()
{int t=1,k;
while(t) {printf(―1,Create linklist.\n‖);
printf(―2,Print linklist.\n‖);
printf(―3,Insert element.\n);
printf(―4,Delete element.\n);
printf(―0,Exit\n‖);}
printf(―Select(0~4):‖);
scanf(―%d‖,&k);
switch(k) {… 转下页 … }
}
目录388
主菜单代码设计
(根据输入 K的值,转去执行相应代码 )
Case 1:建立链表代码; break;
Case 2:输出链表代码; break;
Case 3:插入元素代码; break;
Case 4:删除元素代码; break;
Case 0:退出系统代码( t=0;)
目录389
程序接口设计即确定函数的调用形式 ( 考虑函数名,函数参数及类型,函数返回值 ) 。
建立链表:
head=creat( );
遍历链表:
print(head);
插入元素:
head=insert(head,num,score);
删除元素:
head=delete(head,num);
目录390
函数详细设计
建立链表:
creat( ) { }
遍历链表:
void print(struct student *head) { }
插入元素:
struct student * insert(struct student *head,int
num,float score)
{ }
删除元素:
struct student *delete(struct student *head,int num)
{ }
目录391
组合代码,上机调试。
将前面分析得到的各函数组合起来,在 TURBO
C环境下编辑、编译和运行。
目录392
共用体的概念
共用体:把不同类型的变量存放到同一段内存单元,或对同一段内存单元的数据按不同类型处理。
请看图示:
结构体
1004H
1003H
1002H
1001H
1000H
0FFFH
目录393
共用体定义
1,类型定义:
union 共用体名

成员列表;
};
2,变量定义:
union 共用体名 变量列表;
3,说明:
1,可将类型和变量定义放在一起;
2,注意区分共用体和结构体的定义。
union data
{
int i ;
char ch ;
float f ;
};
union data a,b,c ;} a,b,c ;
结构体目录394
共用体变量的引用
不能引用共用体变量,只能引用共用体变量的成员。引用方式如下:
共用体变量名,成员名
正确引用方式:
union data a;
a.i=5 ;
a.ch=ch ;
a.f =2.6 ;
错误引用方式:
Printf(―%d‖,a);
结构体目录395
共用体类型数据的特点
1,共用体变量中的值是最后一次存放的成员的值,因为同一内存段不能同时存放几种数据。
2,共用体变量不能初始化 。如:
3,共用体变量地址和它的各成员地址是同一地址。
4,共用体变量不可以作为函数参数,
但指向共用体变量的指针可以。
union data
{
int i;
char ch;
float f;
} a={1,'a',1.5};
结构体目录396
结构 体 和 共用体 区别
1,结构 体 和 共用体 都是由多个不同的数据类型成员组成,但在任何同一时刻,共用体 中只存放一个被选中的成员,而结构 体 的所有成员都存在。
2,对于 共用体 的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了,而对于结构 体 的不同成员赋值是互不影响的。
结构体目录397
共用体举例
main()
{ union{int i;
struct{char first;
char second;
}half;
}num;
num.i=0x4241;
printf(―%c%c\n",num.half.first,num.half.second);
num.half.first='a'; /*共用体 中结构成员赋值 */
num.half.second='b';
printf("%x\n",num.i);
}
结构体
65660x4241
97980x6261
目录398
枚举类型
,枚举”:将变量可能的值一一列举出来。变量的值只能取列举出来的值之一。如表示星期几:
SUNDAY,MONDAY,TUESDAY,WEDNESDAY,THURSDAY,
FRIDAY,SATURDAY。
枚举的说明形式为,
enum 枚举类型名
{标识符,标识符,..,标识符 } 枚举变量 ;
如定义一个“星期”枚举类型变量 day,
enum weekday
{sun,mon,tue,wed,thu,fri,sat} day;
枚举变量 day只能取 sun到 sat之一,如 day = mon;
sun,mon,....,sat称为“枚举元素”。
结构体目录399
枚举类型
枚举元素是常量。例
day = mon;
printf(―%d‖,day); 输出整数 1。
在定义枚举类型时,可以指定枚举常量的值,如
enum weekday {sun=7,mon=1,tue,wed,thu,fri,sat};
枚举值可以作判断,例
if (weekday == mon)....
整型与枚举类型是不同的数据类型,不能直接赋值,如
workday = 2; /* workday是枚举类型 */
结构体目录400
位运算
C语言提供的位运算符:
& 按位与
| 按位或
∧ 按位异或
~ 取反
<< 左移
>> 右移说明:
1,位运算符中除了 ~ 以外,均为二目运算符。
2,运算量只能是整型或字符型的数据,不能为实型数据。
目录401
“按位与”运算符 &
规则:
参加运算的两个运算量,如果两个相应位都为 1,则该位结果值为 1,否则为 0。
例如,X=10001001 Y=11101110
X & Y=10001000
用途,取一个数 x中某些指定位。
位运算目录402
“按位或”运算符 |
规则:
参加运算的两个运算量,如果两个相应位中有个为 1,
则该位结果值为 1,否则为 0。
例如 X=10001001 Y=11101110
X | Y=11101111
用途,常用来对一个数据的某些位置 1。
位运算目录403
“异或”运算符 ∧
规则:
参加运算的两个运算量,如果两个相应位为“异”
(值不同),则该位结果值为 1,否则为 0。
例如 X=10001001 Y=11101110
X^Y=01100111
应用,使特定位翻转 。
位运算目录404
“取反”运算符 ~
规则:
对一个二进制数按位取反,即将 0变为 1,1变为 0。
例,0100001110010111 取反 1011110001101000
注意:
~ 运算符的优先级别比算术运算符、关系运算符、逻辑运符和其它运算符都高。
例,使一个数 a的最低位为零,可以表示成:
a & ~ 1
因为,~1=1111111111111110。
位运算目录405
左移运算符《
规则:
将一个数的各二进制全部左移若干位。(左丢弃,右补 0)
例,a=a<<2
将 a的二进制数左移 2位,右补 0。
说明:
若左移时舍弃的高位不包含 1,则数每左移一位,相当该数乘以 2。
8<<2=?
00001000<<2
=00100000
位运算目录406
右移运算符》
规则:
将一个数的各二进制位全部右移若干位。(正数左补
0/负数左补 1,右丢弃)
例,a=a>>2
将 a的二进制数右移 2位。
说明:
将一操作数右移一位,相当于将其除以 2。
8>>2=?
00001000>>2
=00000010
位运算目录407
文 件
1,在 stdio.h中,FILE类型定义,
typedef struct
{ int _fd; /* 文件号 */
int _cleft; /* 缓冲区中剩下的字节数 */
int _mode; /* 文件操作模式 */
char * _nextc; /* 下一个字节的位置 */
char * _buff; /*文件缓冲区位置 */
}FILE;
2,定义文件指针变量的一般形式:
1,FILE *文件结构指针变量名
2,例如 FILE *fp;
3,注意:只有通过文件指针,才能调用相应的文件。
目录408
文件 的打开与关闭
1.,打开”文件的含义:
以某中方式从磁盘上查找指定的文件或创建一个新文件。
2,文件打开函数:
FILE * fopen( 文件名,使用文件方式);
文件名可以包含驱动器、路径、文件名、扩展名。
3,常用以下方式打开文件:
FILE *fp;
if ((fp=fopen("文件名 ",“方式 ")) = =NULL )
{ printf("cannot open this file\n");
exit(0);
}
文件目录409
文件的读写
1,int fputc(int c,FILE *fp)
把字符 c写入文件 fp,成功时返回字符 c的 ASCII码,
失败时返回 EOF。
如,fputc(ch,fp);
2,int fgetc(FILE *fp)
从文件 fp中读一个字符,返回读得的字符。
如,ch=fgetc(fp) ;
文件目录410
文件的读写举例
#include "stdio.h"
void main()
{FILE *fp;
char ch;
fp = fopen("test","r");
if (fp == NULL)
{printf("can not open test\n"); exit(0);}
ch = fgetc(fp);
while(ch != EOF)
{putchar(ch);ch = fgetc(fp);}
fclose(fp);
}
文件目录411
其他文件读写函数
1,fread()/ fwrite()
2,fprintf()/ fsacnf()
3,putw() /getw()
文件