1.1.什么是计算机程序
第一章 程序设计和 C 语言
程.序.:一组计算机能识别和执行的 指令 。只要让计算机执行这个程序,计算机就会 自动地、有条不紊地 进行工作计算机的一切操作都是由 程序 控制的,离开程序,计算机将一事无成。
1.2什么是计算机语言
计算机语言 :人和计算机交流信息的、计算机和人都能识别的语言。计算机语言发展阶段: 机器语言 (由 0 和 1 组成的指令)
符号语言 (用英文字母和数字表示指令)
高级语言 (接近于人的自然语言和数学语言)
面向过程的语言(非结构化的语言、结构化语言) ;面向对象的语言
1.3C 语言的发展及其特点
C 语言是一种用途广泛、功能强大、使用灵活的过程性编程语言,既可用于编写应用软件,又能用于编写系统软件。因此 C 语言问世以后得到迅速推广。
C 语言主要特点:
语言简洁、紧凑,使用方便、灵活。 (只有 37 个关键字、 9 种控制语句;程序书写形式自由,源程序短)
运算符丰富。( 34 种运算符;把括号、赋值、强制类型转换等都作为运算符处理;表达式类型多样化)
数据类型丰富。 (包括 : 整型、浮点型、字符型、数组类型、指针类型、结构体类型、共用体类型; C99 又扩充了复数浮点类型、超长整型、布尔类型;指针类型数据,能用来实现各种复杂的数据结构的运算。 )
具有结构化的控制语句。 (如 if else 语句、 while 语句、 do while 语句、 switch 语句、 for 语句用函数作为程序
的模块单位,便于实现程序的模块化; C 语言是完全模块化和结构化的语言)
语法限制不太严格,程序设计自由度大。 (对数组下标越界不做检查;对变量的类型使用比较灵活,例如,整型量与字符型数据可以通用; C 语言允许程序编写者有较大的自由度,因此放宽了语法检查)
允许直接访问物理地址,能进行位操作,可以直接对硬件进行操作。 ( C 语言具有高级语言的功能和低级语言的许多功能,可用来编写系统软件;这种双重性,使它既是成功的系统描述语言,又是通用的程序设计语言)
用 C 语言编写的程序可移植性好。 ( C 的编译系统简洁,很容易移植到新系统;在新系统上运行时,可直接编译“标准链接库”中的大部分功能,不需要修改源代码;几乎所有计算机系统都可以使用 C 语言)
生成目标代码质量高,程序执行效率高。
1.4.1最简单的 C 语言程序举例
C 语言允许用两种注释方式: //:单行注释,可单独占一行,可出现在一行中其他内容的右侧。
/* */ :块式注释,可包含多行。
1.4.2C 语言程序的结构
C 语言程序的结构特点:
1、一个程序由一个或多个源程序文件组成(小程序往往只包括一个源程序文件,一个源程序文件中可以包括三个部分:预处理指令、全局声明、函数定义。 )
2、函数是 C 程序的主要组成部分 (一个 C 程序是由一个或多个函数组成的; 必须包含一个 main 函数(只能有一个);每个函数都用来实现一个或几个特定功能;被调用的函数可以是库函数,也可以是自己编制设计的函数。 )
3、一个函数包括两个部分(函数首部、函数体(声明部分、执行部分) )
4、程序总是从 main 函数开始执行
5、C 程序对计算机的操作由 C 语句完成( C 程序书写格式是比较自由的:一行内可以写几个语句;一个语句可以分写在多行上。 )
6、数据声明和语句最后必须有分号 7、C 语言本身不提供输入输出语句 8、程序应当包含注释,增加可读性
1.6 程序设计的任务
1.问题分析 2.设计算法 3.编写程序 4.对源程序进行编辑、编译和连接 5.运行程序,分析结 6.编写程序文档
第二章 算法——程序的灵魂
一个程序主要包括以下两方面的信息:
(1)对数据的描述。 在程序中要指定用到哪些数据以及这些数据的类型和数据的组织形式,这就是数据结构。
(2)对操作的描述。 即要求计算机进行操作的步骤,也就是算法。
数据是操作的对象,操作的目的是对数据进行加工处理,以得到期望的结果。
著名计算机科学家沃思 (Nikiklaus Wirth) 提出一个公式: 算法 + 数据结构 = 程序
一个程序除了算法和数据结构这主要要素外,还应当采用结构化程序设计方法进行程序设计,并且用某一种计算机语言表示。
算法、数据结构、 程序设计方法 和语言工具是一个程序设计人员应具备的知识。
2.1 什么是算法
广义地说,为解决一个问题而采取的方法和步骤,就称为“ 算法 ”。计算机算法可分为两大类别:数值运算算法
(目的是求数值解) ;非数值运算算法(包括面十分广泛,常见的是用于事务管理领域)
2.3 算法的特性
一个有效算法应该具有以下特点:
(1)有穷性。一个算法应包含有限的操作步骤,而不能是无限的。
(2)确定性。算法中的每一个步骤都应当是确定的,而不应当是含糊的、模棱两可的。
(3)有零个或多个输入。所谓输入是指在执行算法时需要从外界取得必要的信息。
(4)有一个或多个输出。算法的目的是为了求解, “解” 就是输出。没有输出的算法是没有意义的。
(5)有效性。算法中的每一个步骤都应当能有效地执行,并得到确定的结果。流程图 是表示算法的较好的工具。
2.4.3 三种基本结构和改进的流程图
三种基本结构: ( 1)顺序结构 ( 2)选择结构 ( 3)循环结构 ①当型循环结构 ②直到型循环结构
2.4.5用伪代码表示算法
伪代码是用介于自然语言和计算机语言之间的文字和符号来描述算法。用伪代码写算法并无固定的、严格的语法规则,可以用英文,也可以中英文混用。
2.4.6用计算机语言表示算法
要完成一项工作,包括 设计算法 和实现算法 两个部分。设计算法的目的是为了实现算法。
例 2.19 将例 2.17 表示的算法(求多项式
#include
{ int sign=1;
double deno = 2.0,sum = 1.0, term; while (deno <= 100)
{ sign = -sign;
term = sign/deno; sum = sum+term; deno = deno+1;
1 1 1 1
2 3 4
1 1
99 100
的值)用 C 语言表示。
}
printf ("%f\n",sum); return 0;
}
2.5 结构化程序设计方法
采取以下方法保证得到结构化的程序: ( 1)自顶向下; ( 2)逐步细化; ( 3)模块化设计; ( 4)结构化编码。
3.1 顺序程序设计举例
第三章 最简单的 C 程序设计
例 3.1 有人用温度计测量出用华氏法表示的温度 ( 如 F ,今要求把它转换为以摄氏法表示的温度 ( 如 C) 。
解题思路:找到二者间的转换公式 c
5 ( ff 9
32 )
代表华氏温度, c 代表摄氏温度
c 5 ( f 9
算法: (N-S 图)
#include
{
float f,c; 定义 f 和 c 为单精度浮点型变量
f=64.0; 指定 f 的值
c=(5.0/9)*(f-32); 计算 c 的值
printf(“f=%f\nc=%f\n”,f,c); 输出 f 和 c 的值return 0;
}
例 3.2 计算存款利息。有 1000 元,想存一年。有三种方法可选: (1) 活期,年利率为 r1 (2) 一年期定期,年利率为 r2 (3)存两次半年定期,年利率为 r3,请分别计算出一年后按三种方法所得到的本息和。
解题思路:确定计算本息和的公式。从数学知识可知:若存款额为 p0,则:活期存款一年后本息和为: p1=p0(1+r1) ;一年期定期存款,一年后本息和为: p2=p0(1+r2) ;两次半年定期存款,一年后本息和为: p3=p0 ( 1+r3/2 )( 1+r3/2 )
算法:
#include
{float p0=1000, r1=0.0036,r2=0.0225,r3=0.0198, p1, p2, p3;
p1 = p0 * (1 + r1); p2 = p0 * (1 + r2);
p3 = p0 * (1 + r3/2) * (1 + r3/2);
printf( ” %f\n%f\n%f\n ” ,p1, p2, p3); return 0;
}
3.2.1常量与变量
1.常量:在程序运行过程中,其值不能被改变的量。 整型常量 :如 1000,12345,0, -345; 实型常量 :十进制小数形式:如 0.34 -56.79 0.0,指数形式:如 12.34e3 ( 代表 12.34 103);字符常量 :如’ ?’,转义字符:如’ \n’;字符串常量:如” boy ”;符号常量: #define PI 3.1416
2.变量:在程序运行期间,变量的值是可以改变的。变量必须 先定义 , 后使用, 定义变量时指定该变量的 名字 和类型。变量名 和变量值 是两个不同的概念,变量名实际上是以一个名字代表的一个 存储地址。
从变量中取值,实际上是通过变量名找到相应的内存地址,从该存储单元中读取数据。
3.常变量: const int a=3;
4.标识符:一个对象的名字。C语言规定标识符只能由 字母 、数字 和下划线 3 种字符组成,且 第一个字符必须为字母或下划线
合法的标识符:如 sum, average, _total, Class, day, BASIC, li_ling
不合法的标识符: M.D.John ,¥ 123,# 33, 3D64 , a> b
3.2.2数据类型
所谓 类型 ,就是对数据分配存储单元的安排,包括存储单元的长度 (占多少字节 )以及数据的存储形式。不同的类型分配不同的长度和存储形式。
C 语言允许使用的数据类型:
整型类型:基本整型 (int 型):占 2 个或 4 个字节
短整型 (short int) : VC++6.0 中占 2 个字节长整型 (long int) : VC++6.0 中占 4 个字节双长整型 (long long int) : C99 新增的
字符型布尔型
浮点类型(单精度浮点型、双精度浮点型、复数浮点型)
空类型
派生类型(指针类型、数组类型、结构体类型、共用体类型、函数类型)
3.2.3整型数据
整型变量的符号属性:整型变量的值的范围包括负数到正数;可以将变量定义为“无符号”类型; 扩充的整形类型:
3.2.3 字符数据类型
有符号基本整型 [signed] int;
无符号基本整型 unsigned int;
有符号短整型 [signed] short [int]; 无符号短整型 unsigned short [int]; 有符号长整型 [signed] long [int];
无符号长整型 unsigned long [int]
有符号双长整型 [signed] long long [int];
无符号双长整型 unsigned long long [int]
字符是按其代码 (整数 )形式存储的, C99 把字符型数据作为整数类型的一种。
1.字符与字符代码:大多数系统采用 ASCII 字符集
字母: A ~Z, a ~z
数字: 0~ 9
专门符号: 29 个: ! ” # & ‘ ( ) * 等空格符:空格、水平制表符、换行等
不能显示的字符:空 (null) 字符 ( 以‘ \0 ’表示 ) 、警告 ( 以‘ \a ’表示 ) 、退格 ( 以‘ \b ’表示) 、回车 ( 以‘ \r ’表示 ) 等
字符’ 1’和整数 1 是不同的概念 :
字符’ 1’只是代表一个形状为’ 1’的符号,在需要时按原样输出,在内存中以 ASCII 码形式存储,占 1 个
字节 [0 0 1 1 0 0 0 1] ;
整数 1 是以整数存储方式 ( 二进制补码方式 ) 存储的,占 2 个或 4 个字节 [0 0 0 0 0 0 0 0 |0 0 0 0 0 0 0 1]
2.字符变量 : 用类型符 char 定义字符变量
3.2.5浮点型数据
浮点型数据是用来表示具有小数点的实数。
float 型( 单精度浮点型 ) :编译系统为 float 型变量分配 4 个字节; 数值以规范化的二进制数指数形式存放。
double 型( 双精度浮点型 ) :编译系统为 double 型变量分配 8 个字节; 15 位有效数字long double( 长双精度 ) 型
3.2.6怎样确定常量的类型
字符常量:由单撇号括起来的单个字符或转义字符。
整型常量:不带小数点的数值。系统根据数值的大小确定 int 型还是 long 型等。
浮点型常量:凡以小数形式或指数形式出现的实数。 C 编译系统把浮点型常量都按双精度处理;分配 8 个字节
3.2.7运算符和表达式
3、算术表达式和运算符的优先级与结合性:
用算术运算符和括号将运算对象(也称操作数)连接起来的、符合C语法规则的式子,称为C算术表达式运算对象包括常量、变量、函数等
C语言规定了运算符的优先级和结合性
4、不同类型数据间的混合运算:
( 1) +、- 、* 、/ 运算的两个数中有一个数为 float 或 double 型,结果是 double 型。系统将 float 型数据都先转换为 double 型,然后进行运算
( 2)如果 int 型与 float 或 double 型数据进行运算,先把 int 型和 float 型数据转换为 double 型,然后进行运算,结果是 double 型
( 3)字符型数据与整型数据进行运算,就是把字符的 ASCII 代码与整型数据进行运算例 3.3 给定一个大写字母,要求用小写字母输出。
#include
int main ( )
{
char c1,c2; c1= ’ A’ ; c2=c1+32;
printf("%c\n",c2);
printf( ” %d\n” ,c2); return 0;
}
5、强制类型转换运算符
强制类型转换运算符的一般形式为: (类型名)(表达式)
(double)a (将a转换成 double 类型)
(int) (x+y) (将 x+y 的值转换成 int 型)
(float)(5%3) (将 5%3的值转换成 float 型)
有两种类型转换:系统自动进行的类型转换;强制类型转换
3.3.1 C 语句的作用与分类
C语句分为以下 5 类:
(1)控制语句: if 、switch 、for 、while 、do while 、continue 、break 、return 、goto 等
(2)函数调用语句 (3) 表达式语句 (4) 空语句 (5) 复合语句
在 C 程序中,最常用的语句是:赋值语句;输入输出语句。其中最基本的是赋值语句。例 3.4 给出三角形的三边长,求三角形面积。
#include
{ double a,b,c,s,area; a=3.67;
b=5.43;
c=6.21;
s=(a+b+c)/2;
area=sqrt(s*(s-a)(s-b)(s-c)); printf(“a=%f\tb=%f\t%f\n”,a,b,c); printf(“area=%f\n”,area);
return 0;
}
3.4.1数据的输入输出举例
例 3.5 求 ax2 + bx + c = 0 方程的根。 a、b、c 由键盘输入,设 b 2 -4ac >0
#include
{double a,b,c,disc,x1,x2,p,q; scanf("%lf%lf%lf",&a,&b,&c); disc=bb-4ac;
p=-b/(2.0a); q=sqrt(disc)/(2.0*a); x1=p+q; x2=p-q;
printf(“x1=%7.2f\nx2=%7.2f\n”,x1,x2); return 0;
}
3.4.2有关数据输入输出的概念
几乎每一个 C 程序都包含输入输出,输入输出是程序中最基本的操作之一。
(1)所谓输入输出是以计算机主机为主体而言的:
从计算机向输出设备 ( 如显示器、打印机等 ) 输出数据称为输出
从输入设备(如键盘、磁盘、光盘、扫描仪等)向计算机输入数据称为输入
(2)C语言本身不提供输入输出语句,输入和输出操作是由 C标准函数库中的函数来实现的。 printf 和 scanf 不是C语言的关键字,而只是库函数的名字
(3)在使用输入输出函数时,要在程序文件的开头用预编译指令 #include
3.4.3用 printf 函数输出数据
在 C 程序中用来实现输出和输入的,主要是 printf 函数和 scanf 函数。这两个函数是格式输入输出函数,用这两个函数时,必须指定格式。
1.printf 函数的一般格式, printf (格式控制,输出表列)例如: printf( ” i=%d,c=%c\n ” ,i,c);
2.常用格式字符:d格式符:用来输出一个有符号的十进制整数。可以在格式声明中指定输出数据的域宽
printf( ” %5d%5d\n”,12,-345) ; %d输出 int 型数据; %ld 输出 long 型数据。 c格式符。用来输出一个字符, char ch= ’ a’; printf( ”%c” ,ch) ;或 printf( ” %5c” ,ch)
s格式符。用来输出一个字符串, printf (” %s” , ” CHINA”);
f 格式符。用来输出实数,以小数形式输出。
①不指定数据宽度和小数位数,用 %f;
②指定数据宽度和小数位数。用 %m.nf;
③输出的数据向左对齐,用 %-m.nf
e 格式符。指定以指数形式输出实数。 %e, VC++给出小数位数为6位,指数部分占 5 列,小数
点前必须有而且只有 1 位非零数字。 printf( ” %e” ,123.456) ;输出: 1.234560
e+002。 %m.ne, printf( ” %13.2e” ,123.456); 输出: 1.23e+002 ( 前面有 4
个空格 )
3.4.4用 scanf 函数输入数据
1.scanf 函数的一般形式: scanf (格式控制,地址表列)
2.scanf 函数中的格式声明:与 printf 函数中的格式声明相似;以%开始,以一个格式字符结束,中间可以插入附加的字符, scanf(“a=%f,b=%f,c=%f”,&a,&b,&c) ;
3.使用 scanf 函数时应注意的问题
3.4.5字符数据的输入输出
1.用 putchar 函数输出一个字符,从计算机向显示器输出一个字符, putchar 函数的一般形式为: putchar©
例 3.8 先后输出 BOY三个字符。#include
int main ( )
{
char a=‘B’,b=‘O’,c=‘Y’; putchar(a); putchar(b); putchar©;
putchar (’\n’);
return 0;
}
2.用 getchar 函数输入一个字符,向计算机输入一个字符, getchar 函数的一般形式为: getchar( )
例 3.9 从键盘输入 BOY三个字符,然后把它们输出到屏幕。#include
int main ( )
{ char a,b,c; a=getchar(); b=getchar(); c=getchar();
putchar(a); putchar(b); putchar©; putchar(’\n’);
return 0;
}
4.1 选择结构和条件判断
第四章 选择结构程序设计
C语言有两种选择语句: (1)if 语句,实现两个分支的选择结构; (2)switch 语句,实现多分支的选择结构例 4.1 在例 3.5 的基础上对程序进行改进。题目要求是求 ax + bx + c = 0 方程的根。
2 2
由键盘输入 a,b,c 。假设 a,b,c 的值任意,并不保证 b - 4ac ≥0。需要在程序中进行判别,如果 b
≥0 ,就计算并输出方程的两个实根,否则就输出“方程无实根”的信息。
#include
{
double a,b,c,disc,x1,x2,p,q; scanf("%lf%lf%lf",&a,&b,&c);
disc=bb-4ac;
if (disc<0)
printf( “ has not real roots\n ”); else
{ p=-b/(2.0a); q=sqrt(disc)/(2.0*a); x1=p+q;
x2=p-q;
printf( “ real roots:\nx1=%7.2f\nx2=%7.2f\n ” ,x1,x2);
}
return 0;
}
4.2.1用 if 语句处理选择结构举例
例 4.2 输入两个实数,按代数值由小到大的顺序输出这两个数。
#include
{ float a,b,t; scanf("%f,%f",&a,&b); if(a>b)
{ t=a; a=b; b=t;
}
printf("%5.2f,%5.2f\n",a,b); return 0;
}
例 4.3 输入 3 个数 a, b, c,要求按由小到大的顺序输出。#include
int main()
{ float a,b,c,t; scanf("%f,%f,%f",&a,&b,&c); if(a>b)
{ t=a; a=b; b=t; } if(a>c)
{ t=a; a=c; c=t; } if(b>c)
{ t=b; b=c; c=t; } printf("%5.2f,%5.2f,%5.2f\n",a,b,c); return 0;
}
4.2.2if 语句的一般形式
if ( 表达式 ) 语句 1 [ else 语句 2 ]
最常用的 3 种 if 语句形式:
1.if ( 表达式 ) 语句 1 ( 没有 else 子句 )
2.if ( 表达式 ) 语句 1
else 语句 2 ( 有 else 子句 )
3.if (表达式1) 语 句 1 else if (表达式2) 语句2 else if (表达式3) 语句3
┆
else if (表达式m) 语句m
else 语句 m+1 ( 在 else 部分又嵌套了多层的 if 语句)
说明: (1) 整个 if 语句可写在多行上,也可写在一行上,但都是一个整体,属于同一个语句;
(2)“语句 1” “语句 m”是 if 中的内嵌语句,内嵌语句也可以是一个 if 语句;
(3)“语句 1” “语句 m”可以是简单的语句,也可以是复合语句。
4.3.1关系运算符及其优先次序
关系运算符:用来对两个数值进行比较的比较运算符
C语言提供6种关系运算符: ①< ( 小于) ②< =( 小于或等于 ) ③>大于 ) ④> =大于或等于 )【优先级相同 (高)】
⑤ == ( 等于 ) ⑥ != ( 不等于 ) 【优先级相同(低) 】关系、算术、赋值运算符的优先级:算数运算符 > 关系运算符 > 赋值运算符
4.3.2关系表达式
关系表达式:用关系运算符将两个数值或数值表达式连接起来的式子;关系表达式的值是一个逻辑值,即“真” 或“假”;在 C的逻辑运算中,以“1”代表“真” ,以“0”代表“假”
4.4.1逻辑运算符及其优先次序
3 种逻辑运算符: &&(逻辑与) || (逻辑或) ! (逻辑非)
&&和|| 是双目 ( 元) 运算符
! 是一目 ( 元) 运算符
逻辑表达式(用逻辑运算符将关系表达式或其他逻辑量连接起来的式子)
逻辑运算的真值表
逻辑运算符的优先次序: ! → && → || (! 为三者中最高)
与其他运算符的优先次序:赋值运算符 < &&和|| < 关系运算符 < 算数运算符 < !
4.4.2逻辑表达式
逻辑表达式的值应该是逻辑量“真”或“假”
编译系统在表示逻辑运算结果时,以数值 1 代表“真”,以 0 代表“假” 但在判断一个量是否为“真”时,以 0 代表“假”,以非 0 代表“真” 注意:将一个非零的数值认作为“真”
修改后的逻辑运算真值表
4.4.3逻辑型变量
这是 C99 所增加的一种数据类型, 可以将关系运算和逻辑运算的结果存到一个逻辑型变量中, 以便于分析和运算
4.5 条件运算符和条件表达式
有一种 if 语句,当被判别的表达式的值为“真”或“假” 时,都执行一个赋值语句且向同一个变量赋值如: if (a>b)
else
max=a;
max=b;
条件运算符的执行顺序:①求解表达式 1;②若为非 0(真)则求解表达式 2,此时表达式 2 的值就作为整个条件表达式的值;③若表达式 1 的值为 0(假),则求解表达式 3,表达式 3 的值就是整个条件表达式的值
条件运算符优先于赋值运算符;条件运算符的结合方向为“自右至左” 。
例 4.4 输入一个字符,判别它是否大写字母,如果是,将它转换成小写字母;如果不是,不转换。然后输出最后得到的字符。 #include
int main()
{
char ch; scanf("%c",&ch);
ch=(ch>=‘A’ && ch<='Z ‘ )?(ch+32):ch;
printf("%c\n",ch); return 0;
}
4.6 选择结构的嵌套
在 if 语句中又包含一个或多个 if 语句称为 if 语句的嵌套。一般形式:
if( ) else 总是与它上面最 if ()
if( ) 语句 1 近的未配对的 if 配对 {
else 语句 2 if ()语句 1 内嵌 if
else 内嵌 if }
if( ) 语句 3 else 语句 2 {}限定了内嵌 if 范围
else 语句 4
-1 ( x < 0 )
例 4.5 有一函数 : y = 0 ( x = 0 )编一程序,输入一个 x 值,要求输出相应的 y 值。
1 ( x > 0 )
解题思路:方法 1 (1) 先后用 3 个独立的 if 语句处理:
输入 x scanf("%d",&x) ; 若 x < 0, 则 y =-1 if(x<0) y = -1 ; 若 x = 0, 则 y = 0 if(x0) y = 0 ;
若 x > 0, 则 y = 1 if(x>0) y = 1 ;
输出 x 和 y printf(“x=%d,y=%d\n”,x,y) ; 方法 2 (2) 用一个嵌套的 if 语句处理:
输入 x scanf("%d",&x) ;
若 x < 0, 则 y = -1 if(x<0) y=-1 ; 否则 else
若 x = 0, 则 y = 0 if(x0) y=0 ; 否则 y = 1 else y=1 ;
输出 x 和 y printf(“x=%d,y=%d\n”,x,y) ;
4.7用 switch 语句实现多分支选择结构
例 4.6 、例 4.7
switch 语句的作用是根据表达式的值,使流程跳转到不同的语句 . switch 语句的一般形式
4.8选择结构程序综合举例
例 4.8 写一程序,判断某一年是否闰年。
#include
{ int year,leap;
printf(“enter year:”); scanf("%d",&year); if (year%40)
if(year%1000) if(year%400==0) leap=1;
else leap=0; else leap=1;
else leap=0;
if (leap) printf("%d is “,year); else printf(”%d is not ",year); printf(“a leap year.\n”);
return 0;
}
例 4.9 求 ax + bx + c = 0 方程的解。
#include
{
double a,b,c,disc,x1,x2,realpart,imagpart; scanf("%lf,%lf,%lf",&a,&b,&c);
printf(“The equation “); if(fabs(a)<=1e-6)
printf(“is not a quadratic\n”); else
{disc=bb-4ac; if(fabs(disc)<=1e-6)
printf(“has two equal roots:%8.4f\n”,-b/(2a));
else if(disc>1e-6)
{x1=(-b+sqrt(disc))/(2a);
x2=(-b-sqrt(disc))/(2a);
printf(“has distinct real roots:%8.4fand %8.4f\n”,x1,x2);
}
else
{ realpart=-b/(2a); imagpart=sqrt(-disc)/(2a); printf(” has complex roots:\n”);
printf("%8.4f+%8.4fi\n “,realpart,imagpart);
printf("%8.4f-%8.4fi\n",realpart,imagpart);
}
}
return 0;
}
例 4.10 运输公司对用户计算运输费用。路程 (s km )越远,每吨·千米运费越低。标准如下: #include
s < 250 没有折扣 int main()
250≤ s < 500 2 %折扣 {
500≤ s < 1000 5 %折扣 int c,s;
1000≤ s < 2000 8 %折扣 float p,w,d,f;
2000≤ s < 3000 10 %折扣 printf(“please enter price,weight,discount:”);
3000≤ s 15 %折扣 scanf("%f,%f,%d",&p,&w,&s);
if(s>=3000) c=12;
else c=s/250;
f = p * w * s * (1 - d / 100);
printf( “ freight=%10.2f\n ”,f); return 0; }
第五章 循环结构程序设计
大多数的应用程序都会包含循环结构。循环结构和顺序结构、选择结构是结构化程序设计的 三种基本结构 ,它们是各种复杂程序的基本构造单元。
5.2 用 while 语句实现循环
while 语句的一般形式如下: while ( 表达式 ) 语句 循环体
“真”时执行循环体语句“假”时不执行
while 循环的特点是:先判断条件表达式,后执行循环体语句。
100
例 5.1 求 1+2+3+ +100,即 n
#include
int main()
{
int i=1,sum=0; while (i<=100)
{ sum=sum+i; i++;
}
printf(“sum=%d\n”,sum); return 0;
}
5.3 用 do—— while 语句实现循环
do—while 语句的特点:先无条件地执行循环体,然后判断循环条件是否成立。一般形式为: do
语句
while ( 表达式 );
例 5.2 用 do while 语句求: 1+2+3+ +100
#include
{ int i=1,sum=0; do
{
sum=sum+i; i++;
}while(i<=100); printf(“sum=%d\n”,sum); return 0;
}
例 5.3 while 和 do—while 循环的比较。
int i,sum=0; 当 while 后面 int i,sum=0;
printf( “i=?”); 的表达式的 printf( “ i=?” );
scanf(“%d”,&i); 第一次的值 scanf(“%d”,&i);
while(i<=10) 为“真”时, do
{ 两种循环得到 {
sum=sum+i; 的结果相同; sum=sum+i;
i++; 否则不相同。 i++;
} }while(i<=10);
for 语句的执行过程:
(1)先求解表达式 1
(2)求解表达式 2,若其值为真,执行循环体,然后执行下面第 (3)步。若为假,则结束循环,转到第 (5) 步
(3)求解表达式 3
(4)转回上面步骤 (2) 继续执行
(5)循环结束,执行 for 语句下面的一个语句
5.5 循环的嵌套
一个循环体内又包含另一个完整的循环结构, 称为 循环的嵌套 。内嵌的循环中还可以嵌套循环, 这就是多层循环。
3 种循环 (while 循环、 do while 循环和 for 循环 )可以互相嵌套。
5.7用 break 语句提前终止循环
break 语句可以用来从循环体内跳出循环体,即提前结束循环,接着执行循环下面的语句
例 5.4 在全系 1000 学生中,征集慈善募捐,当总数达到 10 万元时就结束,统计此时捐款的人数,以及平均每人捐款的数目。
#include
{ float amount,aver,total; int i; for (i=1,total=0;i<=1000;i++)
{ printf(“please enter amount:”); scanf("%f",&amount);
total= total+amount;
if (total>=SUM) break;
}
aver=total / i ; printf( “num=%d\naver=%10.2f\n”,i,aver); return 0;
}
5.7.2用 continue语句提前结束本次循环
有时不希望终止整个循环的操作, 而只希望提前结束本次循环, 而接着执行下次循环。 这时可以用 continue 语句。例 5.5 要求输出 100~ 200 之间的不能被 3 整除的数。
for(n=100;n<=200;n++)
{ if (n%3==0)
continue; printf("%d ",n);
}
5.7.3break语句和 continue语句的区别
continue 语句只结束本次循环,而不是终止整个循环的执行
break 语句结束整个循环过程,不再判断执行循环的条件是否成立例 5.6 输出以下 4*5 的矩阵。
1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20
#include
{ int i,j,n=0;
for (i=1;i<=4;i++) 控制输出 4 行
for (j=1;j<=5;j++,n++) 控制每行中输出 5 个数据
{ if (n%5==0) printf ( “\n” );
printf ("%d\t",ij);
}
printf("\n"); return 0;
}
5.8 循环程序举例
例 5.7 用 /4 ≈1-1/3 +1/5-1/7+`````` 公式求 的近似值,直到发现某一项的绝对值小于 10-6 为止 (该项不累计加 )。
#include
{ int sign=1; double pi=0,n=1,term=1; while(fabs(term)>=1e-6)
{ pi=pi+term; n=n+2; sign=-sign; term=sign/n;
}
pi=pi
}
例 5.8 求费波那西 (Fibonacci) 数列的前 40 个数。这个数列有如下特点:第 1、2 两个数为 1、1。从第 3 个数开始, 该数是其前面两个数之和。即 :
F1 =1 ( n=1)
F2 =1 ( n=2) Fn =Fn-1 + Fn-2 ( n≥3)
这是一个有趣的古典数学问题:有一对兔子,从出生后第 3 个月起每个月都生一对兔子。小兔子长到第 3 个月后每个月又生一对兔子。假设所有兔子都不死,问每个月的兔子总数为多少?
#include
int main() int main()
{ int f1=1,f2=1,f3; int i; { int f1=1,f2=1; int i; printf("%12d\n%12d\n",f1,f2); for(i=1; i<=20; i++)
for(i=1; i<=38; i++) { printf("%12d %12d “,f1,f2);
{ f3=f1+f2; if(i%20) printf("\n"); printf("%12d\n",f3); f1=f1+f2;
f1=f2; f2=f2+f1;
f2=f3; }
} return 0;
return 0; }
}
例 5.9 输入一个大于 3 的整数 n,判定它是否素数 (prime ,又称质数 )。
#include
{ int n,i;
printf( “n=?"); scanf("%d",&n); for (i=2;i<=n-1;i++)
if(n%i
if(i
例 5.10 求 100~200 间的全部素数。
for(n=101;n<=200;n=n+2)
{ k=sqrt(n);
for (i=2;i<=k;i++)
if (n%i0) break; if (i>=k+1)
{ printf("%d ",n);
m=m+1;
}
if(m%100) printf( “\n”);
}
例 5.11 译密码。为使电文保密,往往按一定规律将其转换成密码,收报人再按约定的规律将其译回原文。解题思路:问题的关键有两个:
(1)决定哪些字符不需要改变,哪些字符需要改变,如果需要改变,应改为哪个字符
处理的方法是:输入一个字符给字符变量 c,先判定它是否字母 (包括大小写 ) ,若不是字母,不改变 c 的值;若是字母,则还要检查它是否’ W’到’ Z’的范围内 (包括大小写字母 )。如不在此范围内,则使变量 c 的值改变为其后第 4 个字母。如果在’ W’到’ Z’的范围内,则应将它转换为 A ~ D( 或 a~ d)之一的字母。
(2)怎样使 c 改变为所指定的字母?办法是改变它的 ASCII 值
例如字符变量 c 的原值是大写字母’ A ’,想使 c 的值改变为’ E’,只需执行“ c=c+4”即可,因为’ A ’的 ASCII
值为 65,而’ E’ 的 ASCII 值为 69,二者相差 4 char c;
c=getchar(); while(c!= ‘ \n’ )
{ if((c>= ‘ a’ && c<= ‘z’ ) || (c>= ‘A ’ && c<=‘ Z’ ))
{ if(c>=‘W’ && c<=‘Z’ || c>=‘w’ && c<=‘z’) c=c-22;
else c=c+4;
}
printf("%c",c); c=getchar();
}
char c;
while((c=getchar())!= ‘ \n’ )
{ if((c>= ‘ A’ && c<= ‘ Z’ ) || (c>= ‘a’ && c<= ‘ z’ ))
{ c=c+4;
if(c>= ‘ Z’ && c<= ‘Z’ +4 || c>‘z’ ) c=c-26;
}
printf("%c",c);
}
第六章利用数组处理批量数据
数组是一组 有序数据的集合 。数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。用一个 数组名和下标 惟一确定数组中的元素。数组中的每一个元素都属于 同一个数据类型
6.1.1怎样定义一维数组
一维数组是数组中最简单的。它的元素只需要用数组名加一个下标,就能惟一确定。要使用数组,必须在程序中先定义数组
定义一维数组的一般形式为:类型符 数组名 [ 常量表达式 ];
数组名的命名规则和变量名相同,如 int a[10];
6.1.2怎样引用一维数组元素
在定义数组并对其中各元素赋值后,就可以引用数组中的元素。注意:只能引用数组元素而不能一次整体调用整个数组全部元素的值。
引用数组元素的表示形式为:数组名[下标] ,如 a[0]=a[5]+a[7]-a[2*3] 合法
例 6.1:对 10 个数组元素依次赋值为 0,1,2,3,4,5,6,7,8,9,要求按逆序输出。
解题思路:定义一个长度为 10 的数组,数组定义为整型。要赋的值是从 0 到 9,可 以用循环来赋值。
用循环按下标从大到小输出这 10 个元素
#include
{ int i,a[10];
for (i=0; i<=9;i++) a[i]=i;
for(i=9;i>=0; i–)
printf("%d “,a[i]); printf(”\n");
return 0;
}
6.1.3一维数组的初始化
6.1.4一维数组程序举例
例 6.2:用数组处理求 Fibonacci 数列问题
解题思路:例 5.8 中用简单变量处理的,缺点不能在内存中保存这些数。假如想直接输出数列中第 25 个数,是很困难的。 如果用数组处理, 每一个数组元素代表数列中的一个数, 依次求出各数并存放在相应的数组元素中。
#include
{ int i; int f[20]={1,1}; for(i=2;i<20;i++)
f[i]=f[i-2]+f[i-1]; for(i=0;i<20;i++)
{ if(i%5==0) printf( “\n” );
printf( “%12d” ,f[i]);
}
printf("\n"); return 0;
}
例 6.3:有 10 个地区的面积,要求对它们按由小到大的顺序排列。
解题思路:排序的规律有两种:一种是“升序” ,从小到大;另一种是“降序” ,从大到小。把题目抽象为:“对 n 个数按升序排序” 。采用起泡法排序。
int a[10]; int i,j,t; printf(“input 10 numbers :\n”);
for (i=0;i<10;i++) scanf("%d",&a[i]); printf("\n");
for(j=0;j<9;j++) for(i=0;i<9-j;i++) if (a[i]>a[i+1])
{t=a[i];a[i]=a[i+1];a[i+1]=t;} printf(“the sorted numbers :\n”); for(i=0;i<10;i++) printf("%d “,a[i]); printf(”\n");
6.2.1怎样定义二维数组
二维数组定义的一般形式为:类型符 数组名 [常量表达式 ][ 常量表达式 ]; 如: float a[3][4],b[5][10]; ;二维数组可被 看作是一种特殊的一维数组: 它的元素又是一个一维数组。 例如,把 a 看作是一个一维数组, 它有 3 个元素: a[0] 、
a[1] 、a[2] ,每个元素又是一个包含 4 个元素的一维数组
6.2.2怎样引用二维数组的元素 二维数组元素的表示形式为:数组名[下标] [下标]
6.2.4 二维数组程序举例
例 6.4 ;将一个二维数组行和列的元素互换,存到另一个二维数组中。
解题思路:可以定义两个数组:数组 a 为 2 行 3 列,存放指定的 6 个数,数组 b 为 3 行 2 列,开始时未赋值;将 a 数组中的元素 a[i][j] 存放到 b 数组中的 b[j][i] 元素中,用嵌套的 for 循环完成。
#include
{ int a[2][3]={ {1,2,3},{4,5,6}};
int b[3][2],i,j; printf(“array a:\n”); for (i=0;i<=1;i++)
{ for (j=0;j<=2;j++)
{ printf("%5d",a[i][j]);
b[j][i]=a[i][j];
}
printf("\n");
}
1 2 3
a
1 4
b 2 5
}
printf(“array b:\n”); for (i=0;i<=2;i++)
{ for(j=0;j<=1;j++)
printf("%5d",b[i][j]); printf("\n");
}
return 0;
4 5 6 3 6
例 6.5:有一个 3× 4 的矩阵,要求编程序求出其中值最大的那个元素的值,以及其所在的行号和列号。
解题思路:采用“打擂台算法” ;先找出任一人站在台上,第 2 人上去与之比武,胜者留在台上;第 3 人与台上的人比武,胜者留台上,败者下台;以后每一个人都是与当时留在台上的人比武,直
到所有人都上台比为止,最后留在台上的是冠军。
先把 a[0][0] 的值赋给变量 max; max 用来存放当前已知的最大值; a[0][1] 与 max 比较,如果a[0][1]>max ,则表示 a[0][1] 是已经比过的数据中值最大的,把它的值赋给 max,取代了 max 的原值;以后依此处理,最后 max 就是最大的值。
int i,j,row=0,colum=0,max;
int a[3][4]={ {1,2,3,4},{9,8,7,6},{-10,10,-5,2}};
max=a[0][0];
for (i=0;i<=2;i++) for (j=0;j<=3;j++)
if (a[i][j]>max)
{ max=a[i][j]; row=i; colum=j; }
printf(“max=%d\nrow=%d\ncolum=%d\n”,max,row,colum);
6.3.1 怎样定义字符数组
用来存放字符数据的数组是字符数组。字符数组中的一个元素存放一个字符。定义字符数组的方法与定义数值型数组的方法类似。
例 6.6 输出一个已知的字符串。
解题思路:定义一个字符数组,并用“初始化列表”对其赋以初值;用循环逐个输出此字符数组中的字符
#include
{ char c[15]={‘I’,’ ‘,‘a’,‘m’,’ ‘,‘a’,’ ‘,‘s’,‘t’,‘u’,‘d’,‘e’,‘n’,‘t’,’.’};
int i; for(i=0;i<15;i++)
printf("%c",c[i]); printf("\n");
return 0;
}
例 6.7:输出一个菱形图。
解题思路:定义一个字符型的二维数组,用“初始化列表”进行初始化;用嵌套的 for 循环输出字符数组中的所有元素。
#include
{ char diamond[][5]={ {’ ‘,’ ‘,’’},{’ ‘,’’,’ ‘,’’},{’’,’ ‘,’ ‘,’ ‘,’’},{’ ‘,’’,’ ‘,’’},{’ ‘,’ ‘,’’}};
int i,j;
for (i=0;i<5;i++)
{for (j=0;j<5;j++) printf("%c",diamond[i][j]);
printf("\n");
}
return 0;
6.3.4字符串和字符串结束标志
在 C 语言中, 是将字符串作为 字符数组 来处理的; 关心的是字符串的 有效长度 而不是字符数组的长度;为了测定字符串的实际长度, C 语言规定了字符串结束标志 ’\0’。(’ \0’代表 ASCII 码为 0 的字符,从 ASCII 码表可以查到, ASCII 码为 0 的字符不是一个可以显示的字符,而是一个“空操作符” ,即它什么也不做;用它作为字符串 结束标志不会产生附加的操作或增加有效字符,只起一个供辨别的标志。 )
6.3.5字符数组的输入输出
字符数组的输入输出可以有两种方法:逐个字符输入输出( %c);整个字符串一次输入输出( %s)。
输出的字符中不包括结束符’ \0’;用 %s 输出字符串时, printf 函数中的输出项是字符数组名,不是数组元素名。
如果一个字符数组中包含多个’ \0 ’,则遇第一个’ \0’时输出就结束。可以用 scanf 函数输入一个字符串。 scanf
函数中的输入项 c 是已定义的字符数组名,输入的字符串应短于已定义的字符数组的长度。
6.3.6善于使用字符串处理函数
在 C 函数库中提供了一些用来专门处理字符串的函数,使用方便
1.puts 函数 ---- 输出字符串的函数,其一般形式为: puts (字符数组 ),作用是将一个字符串输出到终端。
2.gets 函数 ---- 输入字符串的函数,其一般形式为: gets(字符数组 ),作用是输入一个字符串到字符数组
3.strcat 函数 ---- 字符串连接函数,其一般形式为: strcat(字符数组 1,字符数组 2),其作用是把两个字符串连接起
来,把字符串 2 接到字符串 1 的后面,结果放在字符数组 1 中。
使用字符串函数时 ,在程序开头用 #include
4.strcpy 和 strncpy 函数-字符串复制, strcpy 一般形式为: strcpy(字符数组 1,字符串 2),作用是将字符串 2 复制到字符数组 1 中去。
可以用 strncpy 函数将字符串 2 中前面 n 个字符复制到字符数组 1 中去;
strncpy(str1 , str2,2):作用是将 str2 中最前面 2 个字符复制到 str1 中,取代 str1 中原有的最前面 2 个字符;复制的字符个数 n 不应多于 str1 中原有的字符。
5.strcmp 函数 ---- 字符串比较函数, 其一般形式为: strcmp(字符串 1,字符串 2),作用是比较字符串 1 和字符串 2。
6.strlen 函数 ---- 测字符串长度的函数,其一般形式为: strlen (字符数组 ),它是测试字符串长度的函数,函数的值为字符串中的实际长度。
7.strlwr 函数 ---- 转换为小写的函数,其一般形式为: strlwr ( 字符串 ),函数的作用是将字符串中大写字母换成小写字母。
8.strupr 函数 ---- 转换为大写的函数,其一般形式为: strupr ( 字符串 ),函数的作用是将字符串中小写字母换成大写字母。
6.3.7字符数组应用举例
例 6.8 输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。
char string[81],c; int i,num=0,word=0; gets(string);
for (i=0;(c=string[i])!= ‘ \0’ ;i++) if(c== ‘ ’ ) word=0;
else if(word==0)
{ word=1; num++;
}
printf( “ %d words\n ” ,num);
例 6.9 有 3 个字符串 ,要求找出其中最大者。#include
#include
int main ( )
{char str[3][10]; char string[10]; int i; for (i=0;i<3;i++) gets (str[i]);
if ( strcmp(str[0],str[1])>0)
strcpy(string,str[0]); else
strcpy(string,str[1]);
if ( strcmp(str[2],string)>0) strcpy(string,str[2]);
printf("\nthe largest:\n%s\n",string); return 0;
}
第七章 用函数实现模块化程序设计
7.1为什么要用函数
问题 :
如果程序的功能比较多,规模比较大,把所有代码都写在 main 函数中,就会使主函数变得庞杂、头绪不清,阅读和维护变得困难。
有时程序中要多次实现某一功能,就需要多次重复编写实现此功能的程序代码,这使程序冗长,不精炼解决的方法 :用模块化程序设计的思路
采用“组装”的办法简化程序设计的过程;事先编好一批实现各种不同功能的函数;把它们保存在函数库中,需要时直接用。函数就是功能;每一个函数用来实现一个特定的功能;函数的名字应反映其代表的功能。
在设计一个较大的程序时,往往把它分为若干个程序模块,每一个模块包括一个或多个函数,每个函数实现一个特定的功能。C程序可由一个主函数和若干个其他函数构成;主函数调用其他函数,其他函数也可以互相调用;同一个函数可以被一个或多个函数调用任意多次。
可以使用库函数;可以使用自己编写的函数;在程序设计中要善于利用函数,可以减少重复编写程序段的工
作量,同时可以方便地实现模块化的程序设计。例 7.1 输出以下的结果,用函数调用实现。
****************** How do you do!
解题思路:在输出的文字上下分别有一行“ * ”号,显然不必重复写这段代码,用一个函数 print_star 来实现输出一行“ * ”号的功能。再写一个 print_message 函数来输出中间一行文字信息,用主函数
分别调用这两个函数。#include
{ void print_star(); 声明函数
void print_message(); print_star(); print_message(); print_star();
return 0;
}
void print_star() 输出 16 个* 定义函数
{ printf( “******************\n ”); } void print_message() 输出一行文字
{ printf( “ How do you do!\n ”); }
说明:
(1) 一个C程序由一个或多个程序模块组成,每一个程序模块作为一个源程序文件。对较大的程序,一般不希望把所有内容全放在一个文件中,而是将它们分别放在若干个源文件中,由若干个源程 序文件组成一个 C 程序。这样便于分别编写、分别编译,提高调试效率。一个源程序文件可以为多个 C程序共用。
(2) 一个源程序文件由一个或多个函数以及其他有关内容(如预处理指令、数据声明与定义等)组成。一个源程序文件是一个编译单位,在程序编译时是以源程序文件为单位进行编译的,而不是以函数为单位进行编译的。
(3)C程序的执行是从 main 函数开始的,如果在 main 函数中调用其他函数,在调用后流程返回到
main 函数,在 main 函数中结束整个程序的运行。
(4)所有函数都是平行的,即在定义函数时是分别进行的,是互相独立的。一个函数并不从属于另
一个函数,即函数不能嵌套定义。函数间可以互相调用,但不能调用 main 函数。 main 函数是被操作系统调用的。
(5)从用户使用的角度看,函数有两种。
库函数,它是由系统提供的,用户不必自己定义而直接使用它们。应该说明,不同的 C 语言编译系统提供的库函数的数量和功能会有一些不同,当然许多基本的函数是共同的。
用户自己定义的函数。它是用以解决用户专门需要的函数。
(6)从函数的形式看,函数分两类。
①无参函数。无参函数一般用来执行指定的一组操作。无参函数可以带回或不带回函数值,但一般以不带回函数值的居多。
②有参函数。在调用函数时,主调函数在调用被调用函数时,通过参数向被调用函数传递数据, 一般情况下,执行被调用函数时会得到一个函数值,供主调函数使用。
7.2.1为什么要定义函数
C 语言要求,在程序中用到的所有函数,必须“ 先定义,后使用 ”
指定函数 名字、函数 返回值类型 、函数实现的 功能 以及参数的个数与类型 ,将这些信息通知编译系统。指定函数的名字,以便以后按名调用
指定函数类型,即函数返回值的类型
指定函数参数的名字和类型,以便在调用函数时向它们传递数据指定函数的功能。这是最重要的,这是在函数体中解决的
对于库函数,程序设计者只需用 #include 指令把有关的头文件包含到本文件模块中即可。程序设计者需要在程序中自己定义想用的而库函数并没有提供的函数。
7.2.2定义函数的方法
1.定义无参函数,定义无参函数的一般形式为 :
类型名 函数名 () 类型名 函数名 (void)
{ {
函数体 函数体
} }
包括声明部分和语句部分
2.定义有参函数,定义有参函数的一般形式为 :
类型名 函数名(形式参数表列)
{
函数体
}
3.定义空函数,定义空函数的一般形式为 :
类型名 函数名( )
{ }
用空函数占一个位置,以后逐步扩充。好处:程序结构清楚,可读性好,扩充新功能方便,对程序结构影响不大。
7.3.1函数调用的形式
函数调用的一般形式为:函数名(实参表列)
如调用无参函数,则“实参表列”可以没有,但括号不能省略;如实参表列含多个实参,则各参数间用逗号隔开。按函数调用在程序中出现的形式和位置来分,可以有以下 3 种函数调用方式:
第四章 函数调用语句:把函数调用单独作为一个语句,如 printf_star() ;这时不要求函数带回值,只要求函数完成一定的操作。
第五章 函数表达式:函数调用出现在另一个表达式中,如 c=max(a,b); 这时要求函数带回一个确定的值以参加表
达式的运算。
第六章 函数参数:函数调用作为另一函数调用时的实参,如 m= max(a,max(b,c)) ;其中 max(b,c) 是一次函数调用,它的值作为 max另一次调用的实参。
7.3.2函数调用时的数据传递
1.形式参数和实际参数
在调用有参函数时,主调函数和被调用函数之间有数据传递关系
定义函数时函数名后面的变量名称为“形式参数” (简称“形参” )
主调函数中调用一个函数时,函数名后面参数称为“实际参数” (简称“实参” ),实际参数可以是常量、变量或表达式
2.实参和形参间的数据传递
在调用函数过程中,系统会把实参的值传递给被调用函数的形参;或者说,形参从实参得到一个值,该值在函数调用期间有效,可以参加被调函数中的运算
例 7.2 输入两个整数,要求输出其中值较大者。要求用函数来找到大数。解题思路: (1) 函数名应是见名知意,今定名为 max
(2)由于给定的两个数是整数,返回主调函数的值(即较大数)应该是整型
(3)max 函数应当有两个参数,以便从主函数接收两个整数,因此参数的类型应当是整型先编写 max函数:
int max(int x,int y)
{
int z; z=x>y?x:y; return(z);
}
在 max 函数上面 ,再编写主函数
#include
int main()
{ int max(int x,int y); int a,b,c; int max(int x, int y) (max函数)
printf( “two integer numbers: "); {
scanf( “ %d,%d”,&a,&b); int z;
c=max( a,b ) ; 实参可以是常量、变量或表达式 z=x>y?x:y; printf( “max is %d\n ”,c); return(z);
} }
7.3.3函数调用的过程
在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时,函数 max
调用结束,形参单元被释放;实参单元仍保留并维持原值,没有改变。如果在执行一个被调用函数时,形参的值发生改变,不会改变主调函数的实参的值。
7.3.4. 函数的返回值
通常,希望通过函数调用使主调函数能得到一个确定的值,这就是函数值 ( 函数的返回值 )
2.函数的返回值是通过函数中的 return 语句获得的。 一个函数中可以有一个以上的 return 语句,执行到哪一个
return 语句,哪一个就起作用; return 语句后面的括号可以不要。
3.函数值的类型。应当在定义函数时指定函数值的类型。
4.在定义函数时指定的函数类型一般应该和 return 语句中的表达式类型一致。 如果函数值的类型和 return 语句中表达式的值不一致,则以函数类型为准。
例 7.3 将例 7.2 稍作改动,将在 max 函数中定义的变量 z 改为 float 型。函数返回值的类型与指定的函数类型不同,分析其处理方法。
解题思路:如果函数返回值的类型与指定的函数类型不同,按照赋值规则处理。
#include
{ int max(float x,float y);
float a,b; int c; scanf("%f,%f,",&a,&b); c=max(a,b); printf(“max is %d\n”,c); return 0;
}
int max(float x,float y)
{ float z; z=x>y?x:y; return( z ) ;
}
7.4对被调用函数的声明和函数原型
在一个函数中调用另一个函数需要具备如下条件:
(1)被调用函数必须是已经定义的函数(是库函数或用户自己定义的函数)
(2)如果使用库函数,应该在本文件开头加相应的 #include 指令
(3)如果使用自己定义的函数,而该函数的位置在调用它的函数后面,应该声明例 7.4 输入两个实数,用一个函数求出它们之和。
解题思路:用 add 函数实现。首先要定义 add 函数,它为 float 型,它应有两个参数,也应为 float 型。特别要注意的是:要对 add 函数进行声明。
#include
{ float add(float x, float y);
float a,b,c;
printf(“Please enter a and b:”); scanf("%f,%f",&a,&b); c=add(a,b);
printf(“sum is %f\n”,c); return 0;
}
float add(float x,float y)
{ float z;
z=x+y; return(z);
}
函数原型的一般形式有两种: 如 float add(float x, float y);
float add(float, float);
原型说明可以放在文件的开头,这时所有函数都可以使用此函数
7.5函数的嵌套调用
C语言的函数定义是互相平行、 独立的, 即函数不能嵌套定义, 但可以嵌套调用函数, 即调用一个函数的过程中, 又可以调用另一个函数。
main 函数 a 函数 b 函数
①
调用 ⑤
⑨
结束
例 7.5 输入 4 个整数,找出其中最大的数。用函数的嵌套调用来处理。解题思路: main 中调用 max4 函数,找 4 个数中最大者
max4 中再调用 max2,找两个数中的大者
max4 中多次调用 max2,可找 4 个数中的大者,然后把它作为函数值返回 main 函数
main 函数中输出结果
主函数
#include
{ int max4(int a,int b,int c,int d);
int a,b,c,d,max;
printf( “ 4 interger numbers:"); scanf("%d%d%d%d",&a,&b,&c,&d); max= max4 (a,b,c,d); printf(“max=%d \n”,max);
return 0;
}
max4 函数 max2 函数
int max4(int a,int b,int c,int d) int max2(int a,int b)
{ int max2(int a,int b); { if(a>=b)
int m; return a;
m=max2 (a,b); else
m=max2 (m,c); return b;
m=max2 (m,d); }
return(m);
}
int max2(int a,int b)
{ return(a>b?a:b); }
7.6函数的递归调用
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的 递归调用 。C语言的特点之一就在于允许函数的递归调用。
例 7.6 有 5 个学生坐在一起。问第 5 个学生多少岁?他说比第 4 个学生大 2 岁;问第 4 个学生岁数,他说比第 3
个学生大 2 岁;问第 3 个学生,又说比第 2 个学生大 2 岁;问第 2 个学生,说比第 1 个学生大 2 岁;最后问第 1
个学生,他说是 10 岁。请问第 5 个学生多大?
#include
{ int age(int n);
printf(“NO.5,age:%d\n”,age(5)); return 0;
}
int age(int n)
{ int c;
if(n==1) c=10;
else c=age(n-1)+2; return©;
}
例 7.7 用递归方法求n! 。
解题思路:求n!可以用递推方法:即从1开始,乘2,再乘3 一直乘到n。
递推法的特点是从一个已知的事实 ( 如 1!=1) 出发, 按一定规律推出下一个事实 ( 如 2!=1!2) , 再从这个新的已知的事实出发,再向下推出一个新的事实 (3!=32!) 。n!=n*(n-1)! 。
求n!也可以用递归方法,即5!等于4!×5,而4!=3!×4 ,1!=1
可用下面的递归公式表示: n!
n! 1
n ( n 1)
( n 0,1)
( n 1)
#include
int main() {
{ int fac(int n); int f;
int n; int y; if(n<0)
printf(“input an integer number:”); printf(“n<0,data error!”);
scanf("%d",&n); else if(n0 | | n1)
y=fac (n); f=1;
printf("%d!=%d\n",n,y); else f=fac(n-1)*n;
return 0; return(f);
} }
例 7.8 Hanoi (汉诺)塔问题。古代有一个梵塔,塔内有 3 个座 A、B、C,开始时A座上有 64 个盘子,盘子大小不等,大的在下,小的在上。有一个老和尚想把这 64 个盘子从A座移到C座,但规定每次只允许移动一个盘,
且在移动过程中在 3 个座上都始终保持大盘在下,小盘在上。在移动过程中可以利用 B 座。要求编程序输出移动一盘子的步骤。
#include
{ void hanoi(int n,char one,char two,char three); int m;
printf( “ the number of diskes:");
scanf("%d",&m);
printf(“move %d diskes:\n”,m); hanoi (m,‘A’,‘B’,‘C’);
}
void hanoi (int n,char one,char two,char three)
{ void move(char x,char y); if(n==1)
move (one,three); else
{ hanoi (n-1,one,three,two); move (one,three);
hanoi (n-1,two,one,three);
}
}
void move (char x,char y)
{
printf("%c–>%c\n",x,y);
}
7.7.1数组元素作函数实参
例 7.9 输入 10 个数,要求输出其中值最大的元素和该数是第几个数。
#include
{ int max(int x,int y) ; int max(int x,int y) int a[10],m,n,i; { return(x>y?x:y); }
printf( “ 10 integer numbe\nrs"):;
for(i=0;i<10;i++) scanf("%d",&a[i]);
printf("\n");
for(i=1,m=a[0],n=0;i<10;i++)
{ if ( max (m,a[i])>m)
{ m=max (m,a[i]); n=i;
}
}
printf( “ largest number is %\nd",m);
printf( “ %dth numbne“r.
}
,n+1);
7.7.2数组名作函数参数
除了可以用数组元素作为函数参数外,还可以用数组名作函数参数 ( 包括实参和形参) 。用数组元素作实参时,向形参变量传递的是数组元素的值;用数组名作函数实参时,向形参传递的是数组首元素的地址。
例 7.10 有一个一维数组 score ,内放 10 个学生成绩,求平均成绩。
#include
{ float average(float array[10]); float score[10] ,aver; int i; printf(“input 10 scores:\n”); for(i=0;i<10;i++)
scanf("%f",&score[i]); printf("\n");
aver= average (score); printf("%5.2f\n",aver); return 0;
}
float average (float array[10] )
{ int i;
float aver,sum=array[0]; for(i=1;i<10;i++)
sum=sum+array[i]; aver=sum/10; return(aver);
}
例 7.11 有两个班级,分别有 35 名和 30 名学生,调用一个 average 函数,分别求这两个班的学生的平均成绩。
#include
{ float average(float array[ ],int n);
float score1[5]={98.5,97,91.5,60,55}; float score2[10]={67.5,89.5,99,69.5,
77,89.5,76.5,54,60,99.5};
printf( “ %n6.”2af,verage (score1,5)); printf( “ %n6.”2af,verage (score2,10)); return 0;
}
调用形式为 average(score1,5) 时 调用形式为 average(score2,10) 时
)
void sort (int array [],int n)
{ int i,j,k,t;
for(i=0;i< n-1;i++)
{ k=i;
for(j=i+1;j< n;j++)
if( array [j]< array [k]) k=j; t= array [k];
array [k]= array [i];
array [i]=t;
}
}
7.7.3多维数组名作函数参数
例 7.13 有一个3 ×4的矩阵,求所有元素中的最大值。
#include
int main() { int i,j,max;
{ int max_value(int array[][4]); max = array [0][0]; int a[3][4]={ {1,3,5,7},{2,4,6,8},{15,17,34,12}}; for (i=0;i<3;i++)
printf( “ Max value is %n d” ,max_valuea()); for(j=0;j<4;j++)
return 0; if ( array [i][j]>max)
} max = array [i][j];
return (max);
}
7.8.1局部变量
定义变量可能有三种情况:在函数的开头定义;在函数内的复合语句内定义;在函数的外部定义。
在一个函数内部定义的变量只在本函数范围内有效;在复合语句内定义的变量只在本复合语句范围内有效;在函数内部或复合语句内部定义的变量称为 “局部变量 ”。
7.8.2全局变量
在函数内定义的变量是局部变量, 而在函数之外定义的变量称为 外部变量, 外部变量是全局变量 (也称全程变量 ) 。全局变量可以为本文件中其他函数所共用,有效范围为从定义变量的位置开始到本源文件结束。
例 7.14 有一个一维数组,内放 10 个学生成绩,写一个函数,当主函数调用此函数后,能求出平均分、最高分和最低分。
#include
{ float average(float array[ ],int n); float ave,score[10]; int i; printf(“Please enter 10 scores:\n”); for(i=0;i<10;i++)
scanf("%f",&score[i]);
ave=average(score,10); printf(“max=%6.2f\nmin=%6.2f\n
average=%6.2f\n”,Max,Min,ave);
return 0;
}
float average(float array[ ],int n)
{ int i; float aver,sum=array[0]; Max=Min=array[0]; for(i=1;i
else if(array[i]
aver=sum/n; return(aver);
}
例 7.15 若外部变量与局部变量同名,分析结果。
#include
int main()
{ int max(int a,int b); int a=8;
printf( “m n ” ,max(a,b));
return 0;
}
int max(int a,int b)
{ int c;
c=a>b?a:b; return©;
}
7.9.1动态存储方式与静态存储方式
从变量的作用域的角度来观察,变量可以分为 全局变量 和局部变量。
从变量值存在的时间 (即生存期 )观察,变量的存储有两种不同的方式: 静态存储方式 和动态存储方式静态存储方式是指在程序运行期间由系统分配固定的存储空间的方式。
动态存储方式是在程序运行期间根据需要进行动态的分配存储空间的方式。用户区
程序区 全局变量全部存放在静态存储区中
程序开始执行时给全局变量分配存储区,程序执行完毕就释放。静态存储区 在程序执行过程中占据固定的存储单元
将数据存放在此区
动态存储区 ①函数形式参数②函数中定义的没有用关键字 static 声明的变量
③函数调用时的现场保护和返回地址等存放在动态存储区
函数调用开始时分配,函数结束时释放。在程序执行过程中,这种分配和释放是动态的
每一个变量和函数都有两个属性: 数据类型 和数据的 存储类别
数据类型,如整型、浮点型等;
存储类别指的是数据在内存中存储的方式 ( 如静态存储和动态存储 )
存储类别包括:自动的、静态的、寄存器的、外部的;根据变量的存储类别,可以知道变量的作用域和生存期。
7.9.2局部变量的存储类别
1.自动变量 (auto 变量 )
局部变量,如果不专门声明存储类别,都是动态地分配存储空间的。调用函数时,系统会给局部变量分配存储空
间,调用结束时就自动释放空间。因此这类局部变量称为自动变量。自动变量用关键字 auto 作存储类别的声明。
int f(int a)
┇
}
2.静态局部变量 (static 局部变量 )
希望函数中的局部变量在函数调用结束后不消失而继续 保留原值 ,即其占用的存储单元不释放,在下一次再调用
该函数时,该变量已有值 ( 就是上一次函数调用结束时的值 ),这时就应该指定该局部变量为 “静态局部变量 ”,用关键字 static 进行声明。
例 7.16 考察静态局部变量的值。
#include
int main() { auto int b=0;
{ int f(int); static c=3;
int a=2,i; b=b+1;
for(i=0;i<3;i++) c=c+1;
printf( “n %”d,f(a)); return(a+b+c); return 0; }
}
例 7.17 输出 1 到 5 的阶乘值。
#include
{ int fac(int n); int i;
for(i=1;i<=5;i++)
printf( “ %d!n=%”d ,i,fac(i)); return 0;
}
int fac(int n)
{ static int f=1; f=f*n; return(f);
}
3.寄存器变量 (register 变量)
一般情况下,变量(包括静态存储方式和动态存储方式)的值是存放在内存中的。寄存器变量 允许将局部变量的值放在 CPU 中的寄存器中。
现在的计算机能够识别使用频繁的变量,从而自动地将这些变量放在寄存器中,而不需要程序设计者指定。
7.9.3全局变量的存储类别
全局变量都是存放在静态存储区中的。因此它们的生存期是固定的,存在于程序的整个运行过程。
一般来说, 外部变量是在函数的外部定义的全局变量, 它的作用域是 从变量的定义处开始 ,到本程序 文件的末尾 。在此作用域内,全局变量可以为程序中各个函数所引用。
1.在一个文件内扩展外部变量的作用域
外部变量有效的作用范围只限于定义处到本文件结束。
如果用关键字 extern 对某变量作 “外部变量声明 ”,则可以从 “声明 ”处起,合法地使用该外部变量。
例 7.18 调用函数,求 3 个整数中的大者。
#include
{ int max( );
extern int A,B,C;
scanf( “ %d %d %d ” ,&A,&B,&C);
printf(“max is %d\n”,max()); return 0;
}
int A ,B ,C; int max( )
{ int m;
m=A>B?A:B;
if (C>m) m=C; return(m);
}
2.将外部变量的作用域扩展到其他文件
如果一个程序包含两个文件, 在两个文件中都要用到同一个外部变量 Num ,不能分别在两个文件中各自定义一个外部变量 Num
应在任一个文件中定义外部变量 Num ,而在另一文件中用 extern 对 Num 作“外部变量声明 ”
在编译和连接时,系统会由此知道 Num 有“外部链接 ”,可以从别处找到已定义的外部变量 Num ,并将在另一文件中定义的外部变量 num 的作用域 扩展 到本文件
例 7.19 给定 b 的值,输入 a 和m,求 ab 和 am 的值。文 件 file1.c :
#include
int main()
{ int power(int);
int b=3,c,d,m; scanf("%d,%d",&A,&m); c=A
printf("%d*%d=%d\n",A,b,c);
d=power(m); printf("%d**%d=%d\n",A,m,d); return 0;
}
文件 file2.c : extern A; int power(int n)
{ int i,y=1;
for(i=1;i<=n;i++) y*=A;
return(y);
}
3.将外部变量的作用域限制在本文件中
有时在程序设计中希望某些外部变量只限于被本文件引用。这时可以在定义外部变量时加一个 static 声明。
file1.c file2.c
static int A; extern A;
int main ( ) 只能用于 void fun (int n) 本文件仍
{ 本文件 { 然不能用
A=A*n;
}
}
说明 :
不要误认为对外部变量加 static 声明后才采取静态存储方式,而不加 static 的是采取动态存储。 声明局部变量的存储类型和声明全局变量的存储类型的含义是不同的:对于局部变量来说,声明存储类型的作用是指定变量存储的区域以及由此产生的生存期的问题,而对于全局变量来说,声明存储类型的作用是变量作用域的扩展问题。
用 static
(1)对局部变量 用 static 声明,把它分配在静态存储区,该变量在整个程序执行期间不释放,其所分配的空间
(2)对全局变量 用 static 声明,则该变量的作用域只限于本文件模块 ( 即被声明的文件中 ) 。
7.9.4存储类别小结
对一个数据的定义,需要指定两种属性:数据类型和存储类别,分别使用两个关键字,例如:
static int a;
auto char c; 自动变量,在函数内定义
register int d; 寄存器变量,在函数内定义
可以用 extern 声明已定义的外部变量,例如:
extern b; 将已定义的外部变量 b 的作用域扩展至此
( 1)从作用域角度分,有局部变量和全局变量。它们采用的存储类别如下:
自动变量
局部变量 静态局部变量
按作用域角度分 寄存器变量 全局变量 静态外部变量
外部变量
第二章 从变量存在的时间区分 , 有动态存储和静态存储两种类型。 静态存储是程序整个运行时间都存在 , 而动态存储则是在调用函数时临时分配单元。
按生存期分
自动变量
动态存储 寄存器变量形式参数
静态局部变量
静态存储 静态外部变量外部变量
第三章 从变量值存放的位置来区分 , 可分为 :
( 4 ) 关于作用域和生存期的概念
对一个变量的属性可以从两个方面分析:
作用域:如果一个变量在某个文件或函数范围内是有效的,就称该范围为该变量的 作用域生存期:如果一个变量值在某一时刻是存在的,则认为这一时刻属于该变量的 生存期
作用域是从 空间 的角度,生存期是从 时间 的角度
二者有联系但不是同一回事
(5) static 对局部变量和全局变量的作用不同
局部变量使变量由动态存储方式改变为静态存储方式;全局变量使变量局部化 ( 局部于本文件 ) ,但仍为静态存储方式。从作用域角度看,凡有 static 声明的,其作用域都是局限的,或者是局限于本函数内 ( 静态局部变量 ) , 或者局限于本文件内 ( 静态外部变量 ) 。
7.10关于变量的声明和定义
一般为了叙述方便,把建立存储空间的变量声明称 定义 ,而把不需要建立存储空间的声明称为 声明
函数中出现的对变量的声明 ( 除了用 extern 声明的以外 ) 都是定义,函数中对其他函数的声明不是函数的定义。
7.11.1内部函数
如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加 static ,即 :static 类型名 函数名 ( 形参表 )
内部函数又称静态函数,因为它是用 static 声明的。通常把只能由本文件使用的函数和外部变量放在文件的开头,前面都冠以 static 使之局部化,其他文件不能引用。提高了程序的可靠性。
7.11.2外部函数
如果在定义函数时,在函数首部的最左端加关键字 extern ,则此函数是外部函数,可供其他文件调用。如函数首部可以为 extern int fun (int a, int b) 如果在定义函数时省略 extern ,则默认为外部函数。
例 7.20 有一个字符串,内有若干个字符,今输入一个字符,要求程序将字符串中该字符删去。用外部函数实现。
#include
{ extern void enter_string(char str[]);
extern void delete_string(char str[],char ch); extern void print_string(char str[]);
char c,str[80];
enter_string(str);
scanf( “ %c ” ,&c); delete_string(str,c);
print_string(str); return 0;
}
void enter_string(char str[80])
{ gets(str); } file2 (文件 2) void delete_string(char str[],char ch)
{ int i,j;
for(i=j=0;str[i]!=’\0’;i++)
if(str[i]!=ch) str[j++]=str[i];
str[j]=’\0’; file3 (文件 3 )
}
void print_string(char str[])
{ printf("%s\n",str); } file4 (文件 4)