Download presentation
Presentation is loading. Please wait.
1
C 语言程序设计 西北大学 信息学院 计算机文化基础课教学课件
2
C 程序设计 第一章 C语言简介 第二章 数据类型、运算符和表达式 第三章 语句与流程控制 第四章 数组 第五章 函数与程序结构
第一章 C语言简介 第二章 数据类型、运算符和表达式 第三章 语句与流程控制 第四章 数组 第五章 函数与程序结构 第六章 指 针 第七章 结构体与共用体 第八章 文件概述 第九章 编译预处理
3
第一章 C语言简介 一、C语言的发展 机器语言 与计算机对话是从低级语言开始逐步发展起来的。 低级语言 汇编语言 FORTRAN语言
第一章 C语言简介 一、C语言的发展 与计算机对话是从低级语言开始逐步发展起来的。 机器语言 低级语言 汇编语言 高级 语言 FORTRAN语言 ALGOL 60语言 COBOL语言 BASIC语言 PASCAL语言 C/C++语言 ADA语言 PROLOG语言 Java语言 它是使用最早的高级语言,广泛用于科学计算。 是面向问题的高级语言,它离硬件比较远,不宜用来编写系统程序。 使用了十分接近于自然语言英语的语句,很容易理解,在事务处理中有着广泛的应用。 一种交互式语言,由于它简单易懂,具有交互功能,成为微机上配置最广泛的高级语言。 第一个系统地体现了结构化程序设计概念的高级语言。 移植力强,编译质量高,可直接访问硬件的高级语言。 便于实现嵌入式应用的高级语言。 一种逻辑程序设计语言,广泛使用于人工智能领域。 面向对象程序设计语言。
4
C语言是广泛流行的计算机高级语言,C语言已不仅用来编写系统软件,也可用来编写应用软件。以前操作系统及其它系统软件主要是用汇编语言来编写,由于汇编语言依赖于机器硬件,程序的可读性和可移植性都很差。
ALGOL 60 (1960年) CPL语言 (1963年) C语言的发展 BCPL 语言(1967年) B语言(1970年) C语言(1972-1973年)
5
二、 C语言的特点 ①语言简洁、紧凑,使用方便、灵活。 ②运算符丰富,适用的范围也很广泛。
③数据类型丰富,提供了整型、实型、字符型、数组类型、指针类型、结构体类型、共用体类型等数据类型。 ④具有结构化的控制语句 。 ⑤编程限制少,程序设计自由度大 。 ⑥ 可直接对硬件操作,C语言允许直接访问物理地址,能进行位操作 。 ⑦生成目标程序质量高,程序执行效率高。 ⑧具有很好的可移植性。
6
三、C语言的程序结构及特点 例 1 main() { printf("This is a c program .\n"); }
此程序的结果是输出下面的一行信息: This is a c program .
7
例 2 sum is 579 main() /*求两数之和*/ { int a,b,sum; /*定义变量*/
例 2 main() /*求两数之和*/ { int a,b,sum; /*定义变量*/ a=123;b=456; /*给变量赋值*/ sum=a+b; printf("sum is %d\n",sum); /*输出结果*/ } 例 2 程序的功能是求两个整数的和,并将其输出。 程序的结果是输出如下一行信息: sum is 579
8
变量定义和语句之后必须有一个分号(;)。
例 3 main() /*主函数*/ { int max(); /*函数说明*/ int a,b,c; /*定义变量*/ scanf("%d,%d",&a,&b); /*输入变量a和b*/ c=max(a,b);/*调用max函数,将返回值赋给c*/ printf("max=%d\n",c); /*输出c的值*/ } int max(x,y) /* max函数的定义,函数值为整型,x,y为形式参数*/ int x,y; /*对形参x,y作类型定义*/ {int z; /*定义max中用到的变量z*/ if (x>y) z=x; else z=y; return(z) ;/*将z的值作为函数max返回值带回调用处*/ C函数从main()开始执行。 变量定义和语句之后必须有一个分号(;)。 一行内可写几个语句,一个语句可分写在多行。 可以用/*……*/作注释。 C语言没有输入输出语句。
9
int max(x,y) int x,y; 此程序的执行结果如下: 10,20 (输入10,20) max=20 (输出c的值)
10,20 (输入10,20) max=20 (输出c的值) C 程序是由函数组成的,每一个源程序至少包含一个main()函数,也可以包含一个main函数和若干个其它函数。 因此,函数是C程序的基本单位。被调用的函数可以是系统提供的库函数,也可以是用户自定义的函数。 一个函数是由函数的说明部分和函数体两部分组成。 ①函数的说明部分 函数类型 函数名 函数参数 形参类型 形参 int max(x,y) int x,y;
10
※特例:空函数 dump() { } ②函数体 (括在一对大括弧{------}中的部分) 变量定义(int a,b,c;) 函数体
②函数体 (括在一对大括弧{------}中的部分) 变量定义(int a,b,c;) 函数体 执行部分(由若干语句组成) ※特例:空函数 dump() { }
11
四、源程序的执行过程 源程序:用高级语言所提供的语句和函数写出的语 句序列叫源程序。 程序从输入和运行一般需如下几个步骤: 编 辑 编 译
连接 运行 输入 程序 获得源 机器码 文件 可执行 f.c f.obj f.exe 从编辑到执行的过程示意图
12
五、在Turbo C下运行C程序的步骤 1、在Turbo C下运行C程序的步骤 调用Turbo C程序(Turbo C 的启动):
在windows下:tc 的运行和一般文件的运行方法相同 (快捷方式、菜单方式、命令方式均可) 在DOS提示符下:键入 tc并按回车,即可运行Turbo C 编辑源文件: 在Turbo C窗口中,根据需要输入或者修改源文件。 编译源程序: 按“F9”键可对当前打开的源文件进行编译 。 运行: 按“F10”键,启动系统菜单,将光标移到“RUN”, 按回车键 。 退出Turbo C: 按下“ALT+X”键,退出Turbo C的集成环境, 回到操作系统状态。
13
2、Turbo C集成开发环境 在TC中程序的执行过程如图 编 辑 编 译 连接 运行 输入 程序 源程序 机器码 文件 可执行 文件
f.c f.obj f.exe ALT+F9 Ctrl+F9 从编辑到执行的操作过程示意图
14
Turbo C 2.0的初启屏幕如图 Turbo C的主屏幕
File Edit Run Compile Project Options Debug Break/watch Line 1Col 1Insert Indent Tab Fill Unindent C:NONAME.C F1---HelpF5---ZoomF6---Switch F7---Trace F8---Step F9---Make F10---Menu Edit Message Turbo C的主屏幕
15
⑴文件菜单(File) ⑵编辑命令(Edit) Load F3 Pick Alt-F3 New Save F2 Write to
Directory Change dir OS shell Quit Alt-X 文件菜单选择项 ⑵编辑命令(Edit)
16
⑶运行菜单(Run) ⑷编译菜单(Compile) Run Ctrl-F9 Program reset Ctrl-F2
Go to cursor F4 Trace into F7 Step over F8 User screen Alt-F5 运行菜单选项 ⑷编译菜单(Compile) Compile to OBJ Make EXE file Link EXE file Build all Primary C file Get Info 编译菜单选项
17
⑸工程菜单(Project) Project Name Break make on Auto dependencies
Clear Project Clear Message ⑹选择项菜单(Options) Compiler Linker Enviroment Directories Arguments Save options Retrieve options 选择项菜单 Include directories: C:\TC\INCLUDE Library directories: C:\TC\LIB Output directory: Turbo C directory: C:\TC Pick file name: Current pick file: 路径设置窗口
18
参考第8页 ⑺调试菜单(Debug) 调试菜单选择项 (8) 断点/观测菜单(Break/watch) 断点/监测菜单选择项
Evaluate Ctrl-F4 Call stack Ctrl-F3 Find function Refresh display Display swapping Source debugging 调试菜单选择项 (8) 断点/观测菜单(Break/watch) Add watch Ctrl-F7 Delete watch Edit watch Remove all watches Toggle breakpoint Ctrl-F8 Clear all breakpoints View next breakpoint 断点/监测菜单选择项 参考第8页
19
第二章 数据类型、运算符和表达式 一、 C语言的数据类型 数据类型 C语言中有以下几个基本数据类型: char(字符型) int(整型)
第二章 数据类型、运算符和表达式 一、 C语言的数据类型 整型 字符型 实型(浮点型) 枚举类型 数组类型 结构体类型 共用体类型 单精度型 双精度型 指针类型 空类型 构造类型 基本类型 数据类型 C语言中有以下几个基本数据类型: char(字符型) int(整型) float(单精度浮点型) double(双精度浮点型) 其中:char、int、float、double是关键字。
20
short(短型) long(长型) signed(有符号) unsigned(无符号) 例如: short int 表示短整型。
另外,还有4个修饰词可以出现在上面几个基本类型之前,从而改变原来的含义,它们是: 例如: short int 表示短整型。 unsigned char 表示无符号字符型。 long int 表示长整型。 unsigned short int 表示无符号短整型。 修饰符long一般指存储空间相对int型扩大一倍,而short一般指存储空间相对int型缩小一倍。但不同C编译系统具体规定是不同的。如:Turbo C中的int与short int 所占用内存位数一样(都为16个二进制位)
21
Turbo C语言基本数据类型、字宽和范围
表9.1 基本数据类型、字宽和范围 Turbo C语言基本数据类型、字宽和范围 类型 所占位数 数的范围 [signed] char 8 -128~127 unsigned char 0~255 [signed] int 16 -32768~32767 short [int] long [int] 32 ~ unsigned [int] 0~65535 unsigned short [int] unsigned long [int] 0~ float 约6~7位有效数字 double 64 约15~16位有效数字 在IBM PC MS-C中,单精度范围:10-38到1038 双精度范围:10-308到10308
22
二、 常量 1、数值常量 常量:程序中其值不发生变化的量。 数值常量 字符常量 C语言中有 字符串常量 符号常量
常量:程序中其值不发生变化的量。 整型 实型 十进制 八进制(由数字0开头) 十六进制( 由0x 或0X 开头) 常用形式( , , ) 指数形式(0.55e5 , E-3 , 4e+2 数值常量 C语言中有 字符常量 (用单引号括起来的一个字符。如: 'a', 'D') 字符串常量 (用双引号括起来的一串字符。如:"abcd") 注意: 'a' 和"a"的区别 符号常量 (用一个标识符代表一个常量) 1、数值常量 是通常使用的常数,包括整型和实型常量。
23
(1) 整型常量 C语言程序中可以使用十进制、八进制和十六进制来表示整型常量。在使用时不能有小数部分。
十进制整型常量,由正、负号和0至9十个数字组成,没有小数部分。 如 : int型 32、345、12 、-65、-32768、32767 long int型 234L、 l、32L、32l、 L、 l unsigned int型 345u、238U、65535u unsigned long int型 256ul、 UL、 uL 八进制整型常量:037u、0364L、 ul 十六进制整型常量: 0x1f 、0x1FL 、0xFul、0x10L
24
(2)实型常量 实型常量就是我们日常使用的带小数的常数,也叫浮点数。在C语言中,只有十进制表示。它有两种表示形式:小数表示法和指数表示法。 ①小数表示法:它由整数和小数两部分组成,这两部分可以省略其中的一个部分,但不能同时都省略(小数点不能省略)。 (都是double型常量) ②指数表示法:方法是在小数表示法后面加字母E(或e)表示指数。 1e E E e-2 (都是double型常量) 注意:指数部分可正可负,但必须是整数。 例1 整型数:125 , , 0x , -35 实型数: , , 45.8E , e+18
25
注意: 用指数形式表示的浮点数必须有尾数,指数部分必须是整数。 如:e12 , .e43 , 0.25e4.5 , e 等是错误的。 在浮点数常量的后面用字母F(或f)表示float(单精度浮点)类型。 如:1e-2f表示float型。 而字母L(或l)表示long double(长精度浮点)型 , 如:3.2L表示long double型。 如果在浮点数常量的后面不加字母,则表示是一个double(双精度浮点)型常量。 2.1e-2 (都是double型常量)
26
2、字符常量 字符常量是由一个字母或转义字符两边用单引号括起来表示,例如: 'a'、'D'、'\n' 等。
字符常量在计算机内存放的值,为该字符ASCII编码值。 例如:'0'其编码值为48,而不对应数值0。 'A'的ASCII码值为65。 字符常量也可以用它的ASCII码值来表示,具体表示方法为: 八进制用'\ddd'表示,其中ddd代表三位八进制数。 例如:'\101'代表字母'A','\60'代表字符'0'(零)。 十六进制用'\xhh'表示,其中hh代表两位十六进制数。 例如:'\x41'代表字母'A','\x30'代表字符'0'(零)。
27
转义字符:常用反斜线 “\”开头后跟一个字符,但含义改变。见下表:
字符形式 功能 \n 回车换行(Enter) \t 横向跳格(即跳到下一个输出区) \v 竖向跳格 \b 退格(Backspace) \r 回车 \f 走纸换页 \\ 反斜杠字符“\” \’ 单引号字符(‘) \” 双引号字符(‘) \ddd 到3位8进制数所代表的字符 \xhh 到2位16进制数所代表的字符
28
3、字符串常量 例如: "How are you! " 表示字符串 How are you! 。 " " 表示空字符串。
字符串常量是由一对双引号括起来的零个或多个字符序列。 例如: "How are you! " 表示字符串 How are you! 。 " " 表示空字符串。 "a" 表示字符串a。 在字符串中也可使用转义字符 例如:"Please enter \"Y\"or\"N\":" 表示字符串:Please enter "Y" or "N": 字符串中可以包含空字符、空格字符、转义字符和其它字符,也可以包含汉字等文字符号。 例如:"请输入x和y两个数据! " 表示字符串:请输入x和y两个数据!
29
字符串常量与字符常量的区别: 一个字符常量在内存中存放,只占一个字节。而字符串常量是用一个一维字符数组来存放的,即在内存中用多个连续的字节存放 , 字符串的结尾加一个结束符(\0), 其 ASCII码值为0 。 'a'与"a"的存储空间示意图 ' a' " a"
30
4 、符号常量 例如:用PI代表圆周率π,即3.1415926。 定义符号常量的方法是:
在C语言中我们还可以用一个与常量相关的标识符(标识符的概念在后面介绍)来代替常量出现在程序中,这种相关的标识符称为符号常量。 例如:用PI代表圆周率π,即 。 定义符号常量的方法是: #define <标识符> <常量> 例如:定义符号常量PI的命令方法为: #define PI 使用符号常量对程序设计有许多好处: 一是增加程序的可读性; 二是增强可维护性。
31
printf ("total=%d",total); }
符号常量 符号常量应用实例 #define PRICE 30 main( ) { int num, total; num=10; total=num*PRICE; printf ("total=%d",total); }
32
printf ("Input the radius: "); scanf ("%d",&r); area=PI*r*r;
计算圆的面积 符号常量 #define PI main( ) { int area, r; printf ("Input the radius: "); scanf ("%d",&r); area=PI*r*r; printf (“area=is: %d \n", area); }
33
三、 变量 (即:名字、类型和值) 1、标识符 变量:程序中其值可发生变化的量。 变量的三个基本要素: 变量名:每一个变量都应有一个名字。
变量的数据类型:每一个变量都应具有一种数据类型 (在定义时指定)内存中占据一定的存储空间 。 变量的值:变量对应的存贮空间中所存放的数。 (即:名字、类型和值) 1、标识符 是由程序员定义的单词,用它来命名程序中的一些实体对象(如符号常量名、变量名、函数名、类型名、数组名等)。
34
C语言规定标识符的组成规则: 是由大小写字母、数字字符(0~9)和下划线( _ )三种字符组成,且第一个字符必须为字母或下划线。
①大小写字母不一样, 即标识符a1和A1是两个不同的标识符。 ②长度任意(最少一个字符)。 ③不能采用系统关键字(保留字)。 关键字是系统已定义的单词,它们都是系统已定义的保留字,用户不能再对它们定义。 注意:这些关键字都用的是小写字母。
35
常用的32个关键字,它们都是系统已定义的保留字
auto break case char continue const default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned viod volatile while
36
2、变量名及变量定义 (1)变量名 ①命名变量名时应尽量做到“见名知意”,这样有助于记忆,又增加了程序的可读性。
C语言变量名命名规定同标识符。强调一些有关命名变量名时应注意的几个问题。 ①命名变量名时应尽量做到“见名知意”,这样有助于记忆,又增加了程序的可读性。 ②下划线( _ )符号一般是系统函数常用的开始符号,故一般不要用它作为变量名的第一个字符。 ③不能用数字符号(0~9)作名字的开始字符。 ④系统规定的保留字不可再作为变量名。 ⑤大写字母与小写字母表示不同的名字。如area、Area、aREA、Area、ArEa、areA等等,是不同的变量名。 ⑥习惯上一般变量名用小写字母命名,而符号常量名用大写字母命名。
37
(2)变量的定义 C语言规定对使用的变量必须先定义,后使用。 目的:保证程序中变量名的正确使用。 可分配相应的存储空间。
C语言规定对使用的变量必须先定义,后使用。 目的:保证程序中变量名的正确使用。 可分配相应的存储空间。 便于检查变量所进行的运算是否合法。 定义一个或多个变量可使用一个说明语句完成,其格式如下: <类型> <变量名表>; 其中:类型包括基本数据类型和构造数据类型两类, 例如:int、char、float等类型。 变量名表可以是一个变量名,也可以是多个变量名。 当为多个变量名时,变量名之间用逗号分割。 例如:int lower,upper,step; (定义三个整型变量lower、upper和step) char c1,c2,c3,c4; (定义四个字符变量c1、c2、c3和c4。) float x,y,z; (定义三个实型变量x、y和z。)
38
3、变量值 char c1; x=10;y=20; c1='H'; 变量存在两个有用的值。一个是变量所表示的数据值,另一个是变量的地址值。
c1='a'; 变量赋初值:在定义变量的同时给变量赋一个初始值。该变量被称为已初始化的变量。 例如:int a=10; char c='a' ; 例:int x,y; char c1; x=10;y=20; c1='H'; 变量赋值,方法: <变量名> = <表达式>;
39
不能写成:int a=b=d=15; 输出结果是什么?
float f=5.55; char c='a'; int a=15, b=15, d=15; 不能写成:int a=b=d=15; 请分析下面程序: main() { int a; printf("\n%d",a); } 输出结果是什么? 没有赋初值的变量,其值为一个不定的值。引用该变量,就会产生莫名奇妙的结果。
40
①整型变量:(四种类型) 整型变量的定义格式: 类型 变量名表列; 例 main() { int a,b,c,d; unsigned u;
类型 所占位数 数的范围 基本型(int) ~32767 短整型(short int) ~32767 长整型(long int) ~ 无符号整型(unsigned int) ~65535 无符号短型(unsigned short) ~65535 无符号长整型(unsigned long) ~ 无符号型 整型变量的定义格式: 类型 变量名表列; 例 main() { int a,b,c,d; unsigned u; a=12;b=-24; u=10; c=a+u;d=b+u; printf("a+u=%d,b+u=%d\n",c,d); }
41
②实型变量:(两类) 类型 所占位数 数的范围 单精度实型(float) 32 10 ~10
-38 38 -308 308 类型 所占位数 数的范围 单精度实型(float) ~10 双精度实型(double) ~10 单精度实型提供7位有效数字,双精度实型提供15~16位有效数字。 如:float a; a= (最后两位小数不起作用) 而:double a; a= (全部接收) 例 main() { float a,b; a= e5; b=a+20; printf(“%f”, b) } 输出结果是什么?
42
③字符变量:char 理论上 a+20的值应为12345678920,
而实际输出:b的值与a的值相等,为( ) 这主要因为float型变量只能保证7位有效数字,后面的数是无意义的,并不精确的表示该数。 ③字符变量:char 用来存放一个字符常量。占一个字节(8位),存放该字符的ASCII码值。 如: char c1,c2; c1='a';c2='b';
43
C语言中允许字符型数据与整型数据互相赋值。
例 main() {char c1,c2; c1=97;c2=98; printf("%c %c",c1,c2); } 97 98 c1 c2 c1 c2 c1='a';c2='b'; 例 main() {char c1,c2; c1='a';c2='b'; c1=c1-32;c2=c2-32; printf("%c %c",c1,c2); } 'a'='A'+32 ASCII码表中大小写字母之间具有: C语言中允许字符型数据与整型数据互相赋值。 如: int i; i='a'; char c; c=97;
44
例 main() {int i; char c; i='a'; c=97; printf("%c,%d\n",c,c);
printf("%c,%d\n",i,i); } i c 输出结果:a, 97 a, 97
45
四、 枚举类型 1、枚举类型和枚举变量 enum day{Sun=7,Mon=1,Tue,Wed,Thu,Fri,Sat};
枚举是一种构造的数据类型。它是若干个有名字的整型常量的集合(即枚举类型是一种特殊的整型类型)。 所谓枚举就是变量在定义时,将它所有可能的取值都一一列举出来。这种在定义时就明确规定变量只能取哪几个值,而不能取其它值的数据类型叫枚举类型。 1、枚举类型和枚举变量 枚举类型的定义格式如下: enum <枚举名> { <枚举表> } ; 例如: enum day { Sun,Mon,Tue,Wed,Thu,Fri,Sat } ; enum day{Sun=7,Mon=1,Tue,Wed,Thu,Fri,Sat};
46
enum <枚举名> <枚举变量名表>;
枚举变量的定义格式如下: enum <枚举名> <枚举变量名表>; 例如: enum day d1,d2,d3; 例如: enum color {RED,BLUE,YELLOW,BLACK,WHITE}; enum color c1,c2,c3; 其中,c1,c2,c3是三个具有color枚举名的枚举变量。 枚举变量的定义和枚举类型的定义也可放在一个语句中, 如: enum day {Sun=7,Mon=1,Tue,Wed,Thu,Fri,Sat} d1,d2,d3; 2 、枚举变量的值 枚举变量的值,是该枚举变量所属枚举类型的枚举表中某一个枚举符。
47
对于枚举类型变量只能通过赋值的方法为其赋值。
d1=Tue; d2=Sun; c1=RED; c2=YELLOW; 而下面的赋值是错误的: d1=BLACK; /*因为d1枚举变量对应的枚举符中没有BLACK。*/ c1=3; /*因为c1枚举变量对应的枚举符中没有3。*/ 可采用强制类型方式进行赋值。例如: c3=(enum color)(3); 等价于: c3=BLACK; 某个枚举变量的输出值总是整型数值,而不是枚举符。
48
例: main( ) { enum color {RED,BLUE,YELLOW,BLACK,WHITE}; enum color x1, x2; x1=YELLOW; x2=RED; printf("x1=%d, x2=%d",x1,x2); } 运行结果为: x1=2, x2=0 对逻辑运算可以定义如下枚举类型: enum Booler {FALSE, TRUE}; 例如: enum Booler L1, L2; L1=FALSE; L2=TRUE;
49
五、运算符和表达式 1、算术运算符和算术表达式 (1)基本算术运算符:
+ (加)、 - (减) 、 * (乘) 、 / (除) 和 % (求余数) 这五个运算符的优先级为: *、/ 和%同级,但比+和-高。即先乘除后加减。 两个整数相除,结果为一整数;分子小于分母,结果为零。 例如: 5/2 结果为 2 2/5 结果为 0 这五个运算符的结合性为:自左至右。 例如: *2 第一步先计算10+6,得结果16,第二步计算4*2,得结果8,然后用第一步计算的结果减第二步计算的结果,得结果8。
50
余数计算方法: 求两个数“<操作数1>%<操作数2>”的余数计算方法如下:
余数=<操作数1>-<操作数2>*<整商> 其中,<整商>是<操作数1>除以<操作数2>所取的整数商。 例如: 5%3 余数是2 5%8 余数是5 -5%3 余数是 –2 (注意:符号位取<操作数1>的符号) 5%-3 余数是 (注意:符号位取<操作数1>的符号)
51
(2)算术表达式 (3)数据类型转换: 例如: 5+3*(6-2) 表达式的类型为int 型。
算术表达式是由算术运算符和操作数组成的表达式。表达式的值是一个数值,表达式的类型具体由运算符和操作数确定。 例如: 5+3*(6-2) 表达式的类型为int 型。 /2.0 表达式的类型为double型。 (3)数据类型转换: C语言允许不同类型的操作数据进行混合运算,但运算时系统先将操作数转换成同一类型数据,然后进行运算。 类型转换分为: 强制转换 隐含转换
52
转换规则 强制类型转换:格式如下 (类型名) (表达式) 例如: int a; float x,y;
(类型名) (表达式) 例如: int a; float x,y; (double) a、(int)(x+y)、(float)(5%3)、(int)x+y 隐含转换 高级 double float 转换规则 long 必定的 转换 必定的 转换 unsigned 低级 int char,short
53
例如: 若有 int i, float f, double d, long e
则 10+'a'+i*f-d/e 表达式运算次序为: ①将'a'转换成97,然后10+'a'运算。 ② 将i和f都转换成double型,然后i*f运算。 ③ 将 ①的结果转换为double型,然后与 ②的结果相加。 ④ 将e转换成double型,然后d/e运算。 ⑤ 用③的结果减 ④的结果。 小结: 用算术运算符和括号“( )”将运算对象连接起来,符合C语法规则的式子称为算术表达式。 运算对象:常量、变量、函数等。 优先级:先乘除后加减。 结合率:自左至右。
54
2、赋值运算符和赋值表达式 赋值符号:= (将赋值符右边表达式的值赋给赋值符左边的一个变量。)
赋值符号:= (将赋值符右边表达式的值赋给赋值符左边的一个变量。) 赋值运算符的数据类型若不一致,则要进行类型转换。转换方式为: 将实型数据赋给整型变量时,舍弃实数的小数部分。 如:int i; i=5.65; i 的值为 5。 将整型数据赋给实型变量时,数值不变。 float f ; f=23; (先23→ 再存储在f 中) double d; d=23; (先23→ 再存储在d中) 将字符数据赋给整型变量时,将字符数据放到整型变量低8位中。 若字符最高位为1,整型变量的高8位置1,否则高8位置0。 若把字符处理为无符号的量,整型变量的高8位置0。 两种情况:
55
设int i;char c; i=c; (1)所用系统将字符处理为带符号量或对signed char型变量赋值,则: 高位为0 c='0' i的值为48 i 高位为1 c='\376' i的值为-2 i (2)所用系统将字符处理为无符号量或对unsigned char型变量赋值,则:
56
高位为0 c= '0' i的值为48 i 高位为1 c='\376' i的值为254 i 将整型数据赋给长整型变量时,将整型数据放入长整型变量的低16位,若整型数为负数,长整型变量的高16位置1,否则长整型变量的高16位置0。 将长整型数据赋给整型变量时,取长整型数据低16位。
57
将unsigned int 赋给 long int 时,将unsigned int 放入long int的低16位,高16位置0。
unsigned int 赋给 int 将 unsigned long 赋给 long 时,原样赋值。 unsigned short 赋给 short 应注意数值的范围,超出则数据出错 例如:若unsigned int a=65535; int b; b=a; 则:b的值为-1。 b a int 赋给 unsigned int 将 long 赋给 unsigned long 时,原样赋值。 short 赋给 unsigned short 在负数时,数值发生变化。
58
例如:main() { unsigned int a; int b=-1; a=b; printf( "a=%u“ ,a); }
b a a=65535 复合的赋值运算符:(共十个) += , -= , *= , /= , %= , <<= , >>= , &= , ^= , |= 位运算 例如: a+= 等价于 a=a+3 x*=y+8 等价于 x=x*(y+8) x%= 等价于 x=x%3 注:若右边为表达式 应加圆括号“( )”!
59
格式为:<变量> <赋值运算符> <表达式>
赋值表达式: 由赋值运算符将一个变量和一个表达式连接起来的式子。 格式为:<变量> <赋值运算符> <表达式> 赋 值 给 变 量 例如:int x,y,z; x=y=z=5+6; 例如: int x=3,y=4; x*=y+1; ( 等价与 x=x*(y+1);) 它是将字符‘a’的ASCII码值97, 赋给int(整)型变量x。 int x; x='a';
60
若x的值是8, 则表达式x*=x-=x+=x的值?
赋值运算符结合率为:“自右而左”。 例如:a=b=c= a=(b=(c=5)) a,b,c值都是5 a=5+(c=6) c值为6, a值为5+6 (即11) a=(b=4)+(c=6) b值为4,c值为6,a值为4+6 (即10) a=(b=10)/(c=2) a值为5 a+=a– =a*a 若 a为5,则赋值表达式的值为–40。 计算方法: a+=(a- =a*a) a=a-a*a (即a= 5-5*5,即a=-20) a=a+(上述表达式结果) 即a= (-20)+(-20) 即a= = - 40 若x的值是8, 则表达式x*=x-=x+=x的值?
61
赋值运算符结合率为:“自右而左”。 x*=x-=x+=x 若x的值是8, 则赋值表达式的值为。 计算方法: x*= (x-= (x+=x) ) x=x + x 即x=8 + 8 x=x-(上述表达式结果) 即x =(16)-(16) x= 0 x=x*(上述表达式结果) 即x=(0)*(0) x= 0
62
关系运算符用于两个数值之间的比较运算。C语言提供6种关系运算符,它们是:
3、关系运算符和关系表达式 关系运算符用于两个数值之间的比较运算。C语言提供6种关系运算符,它们是: <、<=、>、>=、==、!= 优先级相同 优先级相同 高 低 关系运算符的结合率为:“自左而右”。 关系运算符、算术运算符和赋值运算符的优先级为: 高 低 算术运算符 关系运算符 赋值运算符
63
例如: c>a+b 等效于 c>(a+b) a>b!=c 等效于 (a>b)!=c
关系表达式: 由关系运算符和操作数组成的表达式称为关系表达式。关系表达式的值是一个逻辑型的值,即只有两个值(真和假)。C语言是用1作为真,用0作为假。但是进行运算时,非0即认为真,0才认为假。而表达式的结果为真时,给出真值1。为假时,给出假值0。
64
例如: 若 a=3,b=2,c=1 4、 逻辑运算符和逻辑表达式 例如:有int x=2,y=3,z=5; 则:x>y 结果为0。
z>=y 结果为1。 z==y 结果为0。 例如: 若 a=3,b=2,c=1 f=a>b>c 则f的值为0。 4、 逻辑运算符和逻辑表达式 C语言提供3种逻辑运算符: 逻辑运算符 结合率 优先级 && 与 自左至右 中 || 或 自左至右 低 ! 非 自右至左 高
65
用逻辑运算符将关系表达式或逻辑量连接起来的式子。运算结果为:“真”或“假”值。系统在运算时以非0为“真”值,以0为“假”值。
逻辑表达式: 用逻辑运算符将关系表达式或逻辑量连接起来的式子。运算结果为:“真”或“假”值。系统在运算时以非0为“真”值,以0为“假”值。 逻辑运算: a b && 1 非0 与运算 a b || 1 非 0 非0 或运算 1 a !a 非运算 例如: 4&&0||2 的值为1 5&&!0 的值为1
66
注意: 逻辑、关系、算术和赋值运算符的优先级为: 运算符 优先级 关系运算符
运算符 优先级 逻辑非! 高 算术运算符 关系运算符 &&和|| 赋值运算符 低 例如:(a>b)&&(x>y) 可以写成 a>b&&x>y (a==b)||(x==y) 可以写成 a==b||x==y (!a)||(a>b) 可以写成 !a||a>b 5>3&&2||8<4-! 的值为 1 'c'&&'d' 的值为 注意: 在逻辑表达式求解时,有可能出现某些逻辑运算符不被执行,但整个表达式的结果已经得到。
67
若a为0,则b和c不再判断。表达式结果为0,即“假”值。
&&运算 ||运算 a b c 非0 1 a b c 非0 1 a||b||c 若a为1,则b和c不再判断。表达式结果为1,即“真”值。 (m=a>b)&&(n=c>4) 设 m, n 原值为m=1, n=1, a=1,b=2,c=3。 则 m为0 , n 为 1。 请问表达式 x>0&&x<10 的含义是什么?
68
5、自增、自减运算符 自增、自减运算符:++ , -- (使变量的值增1或减1) 结合率:自右至左。
自增、自减运算符:++ , -- (使变量的值增1或减1) 结合率:自右至左。 例如: ++i , --i (先自增或自减) i++ , i-- (后自增或自减) 若有 int i=3,j; 则: ① j=++i; i的值为3+1 即4 , j的值为4; ②j=i++; j的值为3 , i的值为3+1=4 。 又如:i=3; printf("%d",++i); /*输出为4*/ printf ("%d", i++); /*输出为3*/ 注意: ①该运算符只能用于变量,不能用于常量或表达式;
69
②结合率:自右至左 如:5++, (a+ b)++都是不合法的; -i++ 相当于 -(i++) i+++j 相当于 (i++)+j
例如:main() { int x=8,y,z; y=(++x)+(x++)+(++x); z=(--x)+(x--)+(++x); printf(“y=%d,z=%d,x=%d”,y,z,x); } 则经过运算,y的值为30、z的值为33。而变量x的值为10。
70
② 计算的结果带入表达式中,计算表达式的值。 ③ 再将所有后自增、自减运算抽出进行计算。
对于含有自增和自减运算的表达式,C语言系统一般按以下三步来完成计算 ① 将所有先自增、自减运算抽出进行计算。 ② 计算的结果带入表达式中,计算表达式的值。 ③ 再将所有后自增、自减运算抽出进行计算。 上例中的表达式y=(++x)+(x++)+(++x); C语言系统先进行两次x的自增计算,使x的值由8变为10。然后将10带入表达式中计算(即计算“x+x+x”),得结果30,并赋给变量y。最后再进行一次x的后自增计算,使变量x的值由10变为11。 注意: 当自增、自减运算出现在函数的参数中时,它们不按该方法进行计算。在函数中的计算方法由C语言系统采用扫描格式决定。函数的参数中都避免使用自增、自减运算符。
71
六、其它运算符及表达式 1、位操作运算符及表达式 例如:127&3 运算结果: 01111111 (127的二进制)
(1) 逻辑位运算符 单目逻辑位运算符:~(按位求反) 按位求反(~)是将各个二进制位由1变0,由0变1。 双目逻辑位运算符:&(按位与)、|(按位或)和^(按位异或) 在双目逻辑位运算符中,&高于^,而^又高于|。 例如:127&3 运算结果: (127的二进制) & (3的二进制) (按位与的结果) 例如:127^7运算结果为: (127的二进制) ^ (7的二进制) (按位异或的结果)
72
例如:127|7运算结果为: 01111111 (127的二进制) | 00000111 (7的二进制) 01111111 (按位或的结果)
(127的二进制) | (7的二进制) (按位或的结果) main() { unsigned x=6; x=x<<2; printf(“x=%u”,x); } 运行结果为: x=24 (2)移位运算符 两个双目移位运算符是:<<(左移)和>>(右移)。 即x的原值为: 左移二位后为: 后面补两个零
73
>>(右移)实例 main() { int x=6; x=x>>2; printf(“x=%d”,x); }
右移二位后为: 左边移出的空位用正符号位值零填充
74
负数右移 main() { int x=-6; x=x>>2; printf(“x=%d”,x); } 运行结果为:x=-2
右移二位后为: 左边移出的空位用负符号位值1填充 (-6的补码) (-2的补码)
75
如: a>b?a:b+1 相当于 (a>b)?a:(b+1)
2、 条件运算符和条件表达式 条件运算符: ? : 它是一个三目运算符。 条件表达式的一般格式为: 表达式1?表达式2:表达式3 运算过程:表达式1的结果为真(非0)时,表达式2的计算结果作为条件表达式的值;否则,取表达式3的计算结果为条件表达式的值。 如: a>b?a:b 条件运算符的优先级低于逻辑、关系、算术运算符高于赋值运算符。 如: a>b?a:b 相当于 (a>b)?a:(b+1)
76
注意:条件表达式中的表达式1、表达式2、表达式3可以是不同的类型。
条件运算符的结合率为:“自右至左”。 如: a>b?a:c>d?c:d 相当于 a>b?a:(c>d?c:d) 注意:条件表达式中的表达式1、表达式2、表达式3可以是不同的类型。 如: main() {float p; char x,y; scanf("%c%c",&x,&y); p=x>y?1:1.5; printf("\n%f",p); } x>y 真(非0) 假(0) p= p=1.5 输出p值
77
3、逗号运算符和逗号表达式 4、sizeof运算符 逗号运算符: , 格式: 表达式1, 表达式2, 表达式3, , 表达式n
逗号运算符: , 格式: 表达式1, 表达式2, 表达式3, , 表达式n 优先级: 最低 从左向右计算每个表达式的值,逗号表达式的值为表达式n的值。 例如: y=(x=3,5+6,x+5) 逗号表达式的值为8。 4、sizeof运算符 其格式为: sizeof 变量名 或 sizeof(类型名) 功能:是返回变量名或给定的类型名所占内存字节的个数。
78
main() {int x=1,y=2,z; z=x+y+3; printf("%d,%d,%d",x,y,z);
z=(x++,x+=y,x+y); } 1, 2, 6 4, 2, 6 该程序在Turbo C系统下,运行结果为: 1, 2, 6 4, 2, 6
79
printf("%d, %d, %d, %d", sizeof f, sizeof a,
main() {float f; int a; printf("%d, %d, %d, %d", sizeof f, sizeof a, sizeof(double), sizeof(char)); } 该程序在Turbo C系统下,运行结果为: 4,2,8,1 5、运算符的结合性和优先级 每一种运算符都有一个优先级,优先级是用来标志运算符在表达式中的运算顺序的。下表中列出了常用的运算符优先级。
80
优先级 运算符 结合性 1 () 从左至右 2 !、++、--、&、*、-、(类型)、sizeof ~(按位求反) 从右至左 3 *、/、% 4 +、- 5 <<、>> 6 <、<=、>、>= 7 ==、!= 8 &(按位与) 9 ^(按位异或) 10 |(按位或) 11 && 12 || 13 ? : 14 =、+=、-=、*=、/=、%=、&=、^=、|=、<<=、>>= 15 ,
81
七、数据的输入和输出 1、数据输出 C语言数据输出,是由调用输出函数来完成。 putchar函数 功能:向终端输出一个字符。
C语言中没有提供对数据的输入和输出语句。但提供了一些可完成数据输入输出的标准函数,4个基本输入和输出函数为:getchar()、putchar()、printf()和scanf() 。 1、数据输出 C语言数据输出,是由调用输出函数来完成。 putchar函数 功能:向终端输出一个字符。 格式: putchar (ch) 函数名 参数
82
结果为: BOY 例 #include "stdio.h" main() { char a,b,c; a='B';b='O';c='Y';
putchar(a);putchar(b);putchar(c);putchar('\n'); } 结果为: BOY 格式输出函数printf() 功能:输出若干个任意类型的数据。 格式:printf("格式控制",参数1,参数2,参数3, ) 由格式说明和 普通字符构成 输出数据。由 表达式构成。 格式说明:由%后跟一个格式字符组成。中间可插入l、m、.n和-几个附加符号。 普通字符:照原样输出。
83
d 以带符号的十进制形式输出整数(正数不输出符号)。
格式字符: 格式字符 作 用 d 以带符号的十进制形式输出整数(正数不输出符号)。 o 以8进制无符号形式输出整数(不输出前导符0)。 x 以16进制无符号形式输出整数(不输出前导符0x)。 u 以无符号十进制形式输出整数。 c 以无符号形式输出,只输出一个字符。 s 输出字符串。 f 以小数形式输出单、双精度数,隐含输出6位小数。 e 以标准指数形式输出单、双精度数,数字部分小数位数为6位。 g 选用%f或%e格式中输出宽度较短的一种格式,不输出无意义的0。 例如:printf("a=%d b=%d",a,b); (设 a=12;b=15;) 输出结果为: a=12 b=15
84
输出为:-1,177777,ffff a=-1, 177777, ffff, 65535 b=-2, 177776, fffe, 65534
例: int a=-1; printf("%d,%o,%x",a,a,a); a=-1 输出为:-1,177777,ffff 例:main() {unsined int a=65535; int b= -2; printf("a=%d,%o,%x,%u\n",a,a,a,a); printf("b=%d,%o,%x,%u\n",b,b,b,b); } Test b=-2 a=65535 结果为 a=-1, , ffff, 65535 b=-2, , fffe, 65534
85
附加格式说明字符: 例: long a=135790; printf("%ld",a ); 135790
字 符 作 用 字母 l 用于长整型数据,可加在格式符d、o、x、u前面。 m(一个正整数) 数据最小宽度。 .n(一个正整数) 对实数,表示输出n位小数;对字符串,表示截取的字符个数。 一 输出的数字或字符在域内向左靠。 例: long a=135790; printf("%ld",a ); Test 135790 结果为
86
234, 234,a, a 指定输出宽度。数据宽度不够, 前面补空格,超过则原样输出。
例:main() {int i=234; char c= 'a'; printf("%d,%5d,%c,%3c",i,i,c,c); } 234, ,a, a 结果为 指定输出宽度。数据宽度不够, 前面补空格,超过则原样输出。 例:main() {printf("%3s,%7.2s,%.4s,%-5.3s\n", "CHINA", "CHINA", "CHINA", "CHINA"); } 输出为: CHINA, CH,CHIN,CHI 例:main() {float f= ; printf("%f %10f %10.2f %.2f %-10.2f\n",f,f,f,f,f); } 输出为:
87
格式控制字符串中用连续两个%,表示输出一个%。 如:printf(“%f%%”,1.0/3); 输出为:0.333333%
例:main() {float f= ; printf("%e %10e %10.2e %.2e %-10.2e\n",f,f,f,f,f); } 输出结果为: e e e e e+002 13列 10列 9列 例: main() {float f= ; printf("%f %10e %g\n",f,f,f); } 输出为: e 13列 10列 格式控制字符串中用连续两个%,表示输出一个%。 如:printf(“%f%%”,1.0/3); 输出为: %
88
只能接收 一个字符! 2、数据输入 C语言数据输入,是由调用输入函数来完成。 getchar函数 功能: 从输入设备输入一个字符。
C语言数据输入,是由调用输入函数来完成。 getchar函数 功能: 从输入设备输入一个字符。 格式: getchar () 例 #include "stdio.h" main() {char c; c=getchar(); putchar(c); } 只能接收 一个字符! 格式输入函数scanf() 功能:输入若干个任意类型的数据。 格式:scanf("格式控制",参数1,参数2,参数3, ) 由格式说明和 普通字符构成 变量的地址或字 符串的首地址。
89
在两个输入数据之 间,以一个或多个 空格间隔,也可以 用回车、跳格键(Tab)
格式说明:由%后跟一个格式字符组成。中间可插入l、h、m、*几个附加字符。 普通字符:照原样输入。 在两个输入数据之 间,以一个或多个 空格间隔,也可以 用回车、跳格键(Tab) 例如:main() { int a,b,c; scanf("%d%d%d", &a,&b,&c); printf("%d,%d,%d\n",a,b,c); } 输入格式为: 若用:scanf("%d,%d,%d", &a,&b,&c); 则输入格式为:5,6,7 若用:scanf("%d:%d: %d", &a,&b,&c); 则输入格式为:5:6: 7
90
scanf("%d%c%f", &a,&b,&c); printf("%d,%c,%f\n",a,b,c); }
格式字符: 格式字符 作 用 d 用来输入十进制整数。 o 用来输入8进制整数。 x 用来输入16进制整数。 c 用来输入单个字符。 用来输入字符串,在输入时以非空白字符开始,以第一个空白字符 结束。字符串以串结束标志'\0'作为其最后一个字符。 f 用来输入实数,可以用小数形式或指数形式输入。 e 与f作用相同,e与f可以互相替代。 s 例如:main() { int a; char b; float c; scanf("%d%c%f", &a,&b,&c); printf("%d,%c,%f\n",a,b,c); } 对unsigned型数据 可以用d,o,x格式输入
91
附加格式说明字符: 例 : scanf("%3d%3d",&a,&b); a b c
字 符 作 用 字母l 用于输入长整型数据,可加在格式符d、o、x、f、e前面。 字母h 用于输入短整型数据(short型) 。 m(一个正整数) 指定输入数据所占宽度。 * 表示本输入项在读入后不赋给相应的变量。 例 : scanf("%3d%3d",&a,&b); 若输入数据格式为:123456 则将123赋给a,456赋给b。 例: scanf("%c%c%c",&c1,&c2,&c3); 若输入数据格式为: a b c 则将a赋给c1, 赋给c2,b赋给c3。 a b c 例: scanf("%d%c%f",&a,&b,&c); 若输入数据格式为:1234a123o.26 例: scanf("%2d %*3d %2d",&a,&b); 若输入数据格式为: 则将12赋给a, 67赋给b。
92
第三章 语句与流程控制 一、最简单的C程序设计 1、C语句概述
第三章 语句与流程控制 一、最简单的C程序设计 1、C语句概述 C语言的语句用来向计算机系统发出操作指令。每一个为实现特定目的的程序都包含若干个C语句。
93
C语句的五种分类: 控制语句:完成一定的控制功能(9条)。 函数调用语句:由一次函数调用加一个分号构成。 如:scanf("%d\n",&a); 表达式语句:由表达式加一个分号构成。 如:i=i+1; 空语句:由一个分号构成。 复合语句;由一对大括号“{}”组成,相当于一个语句。 如:{z=x+y; b=15; printf("%d",b); }
94
C语句分为基本语句和复合语句,基本语句归纳如下:
变量定义语句 定义语句 类型定义语句 数据描述语句 函数声明语句 声明语句 变量声明语句 表达式语句 基本语句 基本功能语句 空语句 函数调用语句 if语句 选择结构 if….else语句 if…else…if…语 C语言语句 多分支结构 switch语句 while语句 流程控制语句 循环结构 do…while语句 for语句 continue语句 break语句 转向语句 return 语句 goto语句 复合语句
95
2、程序的三种基本结构 顺序结构:从前向后顺序执行程序。 选择结构:根据判断条件的结果选择执行程序。 A A B B P A B 真 假
顺序结构:从前向后顺序执行程序。 选择结构:根据判断条件的结果选择执行程序。 A A N-S流程图 流程图 B B P A B 真 假 流程图 N-S流程图
96
另:由选择结构派生出的多分支选择结构: K k=ki k=kn k=k1 k=k2 A1 A2 Ai An
97
循环结构:根据条件反复的执行某一段程序若干次。 当型循环结构:
假 P 当P为真 真 流程图 A A N-S流程图 直到型循环结构: A A 流程图 假 直到P为真 P 真 N-S流程图
98
3、程序举例 输入三角形的三边长,求三角形的面积。 (设输入的三边长a,b,c能构成三角形)
#include "math.h" main() { float a, b, c, s, area; scanf(" %f %f %f ", &a ,&b ,&c); s=1.0/2*(a+b+c); area=sqrt(s*(s-a)*(s-b)*(s-c)); printf("a=%7.2f, b=%7.2f, c=%7.2f, s=%7.2f,\n",a,b,c,s); printf("area=%7.2f\n",area); } 从键盘输入一个大写字母,要求改用小写字母输出。 求ax2+bx+c=0方程的根。a,b,c由键盘输入,设b2-4ac>0
99
二、顺序结构语句 1、表达式语句:是在表达式后边跟一个分号所构成的语句。 2、空语句:空语句是仅由一个分号表示的语句。其格式为: ;
C语言语句是以分号作为语句的结束符,其中有四种语句是顺序执行的,它们是:表达式语句;空语句;函数调用语句和复合语句。 1、表达式语句:是在表达式后边跟一个分号所构成的语句。 2、空语句:空语句是仅由一个分号表示的语句。其格式为: ; 3、函数调用语句:由一次函数调用加一个分号构成。 4、复合语句:复合语句是由一对花括号括起来的若干语句组成。 例 财务人员给员工发工资时经常遇到这样一个问题,即根据每个人的工资额(以元作为单位)计算出各种面值的钞票的张数,且要求总张数最少。 例如,某职工工资为3436元,发放方案为:100元34张,20元1张,10元1张,5元1张,1元1张。
100
#include “stdio.h” main( )
{ int gz; int m100,m50,m20,m10,m5,m2,m1; printf("请输入工资:"); scanf( "%d", &gz); printf("工资=%d\n" ,gz ); m100=gz/100;gz=gz%100; /*求100元的张数之后,gz中才存放剩余的金额数*/ m50=gz/50;gz=gz%50; m20=gz/20;gz=gz%20; m10=gz/10;gz=gz%10; m5=gz/5;gz=gz%5; m2=gz/2; m1=gz%2;
101
三、选择结构语句 printf(“100元:%d张\n 50元:%d张\n”, m100,m50);
printf(“ 1元:%d张\n”, m1); } 三、选择结构语句 C语言提供了两种选择结构语句,if语句和switch语句 。 、 if语句,if条件语句的三种形式: ①带else的if语句: ②不带else的if语句: ③条件语句的嵌套 。 2、开关语句(switch) :一个多分支语句。
102
1、 if 语句 作用:判定给定的条件,决定执行不同的两段程序之一。 ⑴带else的if 语句的格式:
括号“()”不能缺省 只能用一个语句, 若有多个语句可用 复合语句{}。 执行过程: 表达式 语句1 语句2 真(非0) 假(0)
103
[例10.] 输入三个实数,按代数值由小到大次序输出这三个数。
[例10.] 输入三个实数,按代数值由小到大次序输出这三个数。 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;b=t;} if (b>c) {t=b;b=c;c=t;} printf("%5.2f,%5.2f,%5.2f",a,b,c); } a b c a b c 小 大 (3)if语句的嵌套: 在if语句的语句1或语句2部分,若使用if语句,称为if语句的嵌套。 如:if (表达式1) if (表达式2) 语句1 else 语句2 else if (表达式3) 语句3 else 语句4 内嵌if语句
104
⑵ if 语句可以省略else部分,变为如下格式: if (表达式) 语句 执行过程:表达式结果为真,执行表达式后面的语句,否则
例:if (x>y) z=x; else z=y; 例:if (x>y) printf("%d,%d",x,y); else printf("%d,%d",y,x); ⑵ if 语句可以省略else部分,变为如下格式: if (表达式) 语句 执行过程:表达式结果为真,执行表达式后面的语句,否则 不执行该语句,而执行if语句下面的语句。 [例] 输入两个实数,按代数值由小到大次序输出这两个数 main() {float a,b,t; scanf("%f,%f",&a,&b); if (a>b) {t=a; a=b; b=t;} printf("%5.2f,%5.2f",a,b); } t t a a b b
105
注意: if (x>=0) if (x>0) y=1; else y=0; else y=-1; -1 (x<0)
main() { int x,y; scanf("%d",&x); if (x<0) y=-1; else if (x==0) y=0; else y=1; printf("x=%d,y=%d\n",x,y); } 也可将if 语句改为: if (x>=0) if (x>0) y=1; else y=0; else y=-1; 注意: If语句嵌套使用时,if与else是以最近匹配规则来匹配。
106
2、 switch 语句 一般为整型或 字符型表达式 格式:switch(表达式) {case 常量表达式1:语句序列1
case 常量表达式i:语句序列i case 常量表达式n:语句序列n default: 语句序列n+1 } 执行过程为:当表达式的值等于常量表达式i的值,则从语句序列i开始执行到语句序列n+1为止 。若表达式的值不等于任何一个常量表达式的值,则只执行default后面的语句。 一般在每个语句序列之后加一个break语句,这样在执行语句序列i之后,使流程跳出switch结构,实现多分支选择结构。 可以是一个 语句,也可以 是几个语句。
107
例:编写一个能进行两个操作数加减乘除四则运算的计数器模拟程序。
输入:两个操作数和运算符 计算:根据运算符确定运算 输出:运算结果 '+'进行加运算。 '-'进行减运算。 '*'进行乘运算。 '/ '进行除运算。 main() {char op; float x,y; printf("input a arithmetic expression:"); scanf("%f%c%f",&x,&op,&y); switch (op) { case '+' : printf("=%f\n",x+y); break; case '-' : printf("=%f\n",x-y); break; case '*' : printf("=%f\n",x*y); break; case '/' : if (y!=0.0) printf("=%f\n",x/y); else printf("Divisor is zero\n"); break; default: printf("Illegal operator\n"); } }
108
switch语句 的嵌套 执行结果为: m=4, n=5 例:分析下列程序的输出结果。 main()
{int i=1,j=0,m=1,n=2; switch(i++) {case 1: m++;n++; case 2: switch(++j) {case 1: m++; case 2: n++;} case 3: m++;n++;break; case 4: m++;n++; } printf("m=%d,n=%d\n",m,n); switch语句 的嵌套 执行结果为: m=4, n=5
109
3、程序举例 写程序,判某一年是否闰年。 输入:年(year) 计算:判是否闰年 能被4整除,但不能被100整除。
写程序,判某一年是否闰年。 输入:年(year) 计算:判是否闰年 输出:闰年或非闰年(leap) 能被4整除,但不能被100整除。 能被4整除,又能被400整除。
110
main() { int year, leap; scanf(“%d”,&year); if (year%4==0) {if (year%100==0) {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); }
111
求ax2+bx+c=0方程的解。 a=0,不是二次方程。 输入:a,b,c b2-4ac=0,有两个相等实根。 计算:求解方程
输出:两个实根或两个复根
112
#include "math.h" main() {float a,b,c,disc,x1,x2,realpart,imagpart; scanf("%f,%f,%f",&a,&b,&c); printf("The equation"); if (fabs(a)<=1e-6) printf("is not quadratic"); else disc=b*b-4*a*c; if (fabs(disc)<=1e-6) printf("has two equal roots:%8.4f\n",-b/(2*a));
113
else if (disc>1e-6) { x1=(-b+sqrt(disc))/(2*a); x2=(-b-sqrt(disc))/(2*a); printf(" has distinct real roots:%8.4f and%8.4f\n",x1,x2); } else { realpart=-b/(2*a); imagpart=sqrt(-disc)/(2*a); printf("has complex roots:\n"); printf("%8.4f+%8.4fi\n",realpart,imagpart); printf("%8.4f-%8.4fi\n",realpart,imagpart);
114
给出一百分制成绩,要求输出成绩等级‘A’、’B’、‘C’、‘D’、
60~69分为‘D’ ,60分以下为‘E’ 。
115
main() { int x; scanf("%d",&x); x=x/10; if (x>10||x<0) printf("成绩输入有错,请重新输入\n"); else if (x>=9) printf("A\n"); else if (x>=8) printf("B\n"); else if (x>=7) printf("C\n"); else if (x>=6) printf("D\n"); else printf("E\n"); }
116
main() { int x; scanf("%d",&x); x=x/10; if (x>10||x<0) printf("成绩输入有错,请重新输入\n"); else switch (x) {case 10: case 9: printf("A\n");break; case 8: printf("B\n");break; case 7: printf("C\n");break; case 6: printf("D\n");break; default: printf("E\n"); }
117
四、循 环 控 制 循环控制结构在程序中是指对某段程序或某条语句根据条件重复执行。C语言提供了while、do-while和for三种支持循环控制结构的语句。 1、 while语句 while语句是支持“当型”循环控制结构的语句。 一般格式为: while (表达式) 语句 执行过程: 若有多个语句可 用复合语句{} 表达式 语句 真(非0) 假(0) 当表达式为真 语句 N-S流程图
118
printf("sum=%d\n",sum);
100 例 求∑i i=1 输入:无 计算:1+2+3++100 输出:计算的和 sum=0;i=1 当i<=100 sum=sum+i i=i+1 main() {int sum=0,i=1; while (i<=100) {sum=sum+i; i++; } printf("sum=%d\n",sum); 练习:计算 +102
119
do_while语句是支持“直到型”循环控制结构的语句。 一般格式为: do 语句 while (表达式); 执行过程:
若有多个语句可 用复合语句{} 注意: 表达式 语句 真(非0) 假(0) 直到表达式为真 语句 N-S流程图
120
例 求∑n。 main() {int sum=0,i=1; do {sum=sum+i; i++; }
100 例 求∑n。 n=1 输入:无 计算:1+2+3++100 输出:计算的和 sum=0;i=1 sum=sum+i i=i+1 当i>100 main() {int sum=0,i=1; do {sum=sum+i; i++; } while (i<=100) ; printf("sum=%d\n",sum); 注意: 两者的不同
121
我们看到:当i<=10时,两程序的结果相同。 而当i>10时,两程序的结果就不同了。
while和do_while语句的比较: main() {int sum=0, i; scanf("%d",&i); do {sum=sum+i; i++; } while (i<=10); printf("sum=%d\n",sum); main() {int sum=0, i; scanf("%d",&i); while (i<=10) {sum=sum+i; i++; } printf("sum=%d\n",sum); 我们看到:当i<=10时,两程序的结果相同。 而当i>10时,两程序的结果就不同了。
122
3、 for 语句 for语句是一种使用比while语句更加灵活的循环控制语句。
执行过程: 表达式2 语句 真(非0) 表达式1 表达式3 假(0) 先求解表达式1; 求解表达式2,若为真(非0)值,则 执行语句,然后求解表达式3,再 转到求解表达式2。若为假(0)值, 则结束for语句的执行。
123
练习:计算 12+22+32+ +102 例如: main() {int i,sum=0;
for (i=1;i<=100;i++) sum=sum+i; printf(“sum=%d\n”,sum); } 和下面程序比较,我们可以看出功能一样. main() {int sum=0;i=1; while (i<=100) {sum=sum+i; i++; } printf("sum=%d\n",sum); 练习:计算 +102
124
while, do_while, for : while (表达式) {语句 } do {语句} while (i<=10); 语句
表达式2 语句 真(非0) 表达式1 表达式3 假(0) while (表达式) {语句 } do {语句} while (i<=10); for (表达式1;表达式2;表达式3) 语句
125
For语句三个表达式的使用说明: 三个表达式都可缺省,但分号(;)不能省略。若表达式1缺省,则从表达式2开始执行。若表达式2缺省,则认为表达式2始终为真,循环无终止地进行下去。 例如: for ( ;i<=100;i++) sum=sum+i; 省略表达式2 省略表达式1 for (i=1; ;i++) sum=sum+i; for (i=1;i<=100; ) {sum=sum+i;i++;} 省略表达式1,3 省略表达式3 for ( ;i<=100; ) {sum=sum+i;i++;} for ( ; ; ) 语句 循环无终止地进行下去
126
三个表达式可以是C语言的任意型表达式。
例如: for ( sum=0,i=1;i<=100;i++) sum=sum+i; 逗号表达式 i=0; j=100 当i<=j k=i+j; i++; j--; for (i=0, j=100 ;i<=j; i++, j--) k =i+j; 逗号表达式 for (i=0 ;(c=getchar())!='\n';i+=c) ; 逗号表达式 空语句 for (i=0, j=100 ; ++i, i<=j; j--) k=i+j;
127
例:用for语句编写一个计算1+3+5+ +(2*i-1)的程 序,其中 i=1, 2, 3, , 100。
输入:无 计算:1+3+5++(2*i-1) 输出:计算的和 sum=0;i=1 当i<=100 sum=sum+(2*i-1) i=i+1 main() {int sum=0,i; for (i=1;i<=100;i++) sum+=2*i-1; printf("sum=%d\n",sum); } 练习:任给n,计算 1!+2!+3!+ +n!
128
1 t=t*i =1*1 =1! sum=sum+t=0+1!=1 !
例: 任给n,计算1!+2!+3!+ +n! 。 sum=0;i=1;t=1; 当i<=n t=t*i sum=sum+t i++ 输入:n 计算: 1!+2!+3!+ +n! 输出:计算的和 sum=0 i=1 t =1 对数列的任一项都是在其前一项的基础上计算出来的, 设t为第i-1项的阶乘,则第i项的阶乘值为t*i; sum为前i-1项的和,则前i项的和为sum+t*i。 i t=t*i sum=sum+t 1 t=t*i =1*1 =1! sum=sum+t=0+1!=1 ! 2 t=t*i =1!*2=2! sum=sum+t=sum+2!=1!+2! 3 t=t*i=2!*3=3! sum=sum+t=sum+3!=1!+2!+3! 4 t=t*i=3!*4=4! sum=sum+t=sum+4!=1!+2!+ 3!+4!
129
printf("sum=%ld\n",sum); }
例: 任给n,计算1!+2!+3!+ +n! 。 main() {long sum,t; int i,n; printf("input n="); scanf("%d",&n); t=1;sum=0; for (i=1;i<=n;i++) {t=t*i; sum=sum+t;} printf("sum=%ld\n",sum); } sum=0;i=1;t=1; 当i<=n t=t*i sum=sum+t i++ {sum=sum+t; t=t*i;} {sum=sum+t*i; t=t*i;}
130
4、 循环语句的嵌套 一个循环语句内又包含另一个完整的循环语句,称为循环语句的嵌套。内嵌的循环语句一般称为内循环,包含内循环的循环语句称为外循环。内循环再嵌套内层循环,就够成了多重循环。 例:分析下列程序的输出结果: main() {int i=1,a=0; for ( ;a<=5;i++) { do {i++;a++;} while (i<3); i++; } printf("a=%d,i=%d\n",a,i); } ①外循环第一次结束时:i=5, a=2; ②外循环第二次结束时:i=5+3, a=2+1; ③ :i=11, a=4; ④ :i=14, a=5; ⑤ :i=17, a=6; 结果:a=6,i=17
131
多重循环程序设计时,应注意以下几点: 三种循环不仅可以自身嵌套,而且可以互相嵌套。 嵌套时,要在一个循环体内包含另一个完整的循环结构。既无论那种嵌套关系,都必须将一个完整的循环语句全部放在某个循环体内。而不能使两个循环语句相互交叉。 运行时,应注意内嵌的语句执行过程。 如 for (i=1;i<=n;i++ ) { j=1; while (j<=m ) { printf("a");j++; } } 内嵌循环 外层循环 正确格式 外层循环 内嵌循环 不正确格式 该语句执行多少次?
132
5、break语句和continue语句 ⑴break语句 break语句的功能是: 在switch语句中使流程跳出switch结构。
在循环语句中使流程跳出当前循环。 例:将从键盘上输入的若干个正整数求和,遇到负数则终止 程序,并且输入的数不超过10个。 i=1; 当i<=10 输入x x<0 sum+=x i=i+1 真 假 break 输入:正整数 计算:求累加和 输出:和
133
例: #define M 10 main() {int i,x,sum; sum=0; for (i=1;i<=M;i++)
{printf("\ninput x="); scanf("%d",&x); if (x<0) break; sum+=x; } printf("%d",sum); i=1; 当i<=10 输入x x<0 sum+=x i=i+1 真 假 break
134
for (i=100;i<=200;i++) { if (i%3== 0) continue; printf("%d,",i);
例:编程把100~200之间的不能被3整除的数输出。 main() { int i; for (i=100;i<=200;i++) { if (i%3== 0) continue; printf("%d,",i); }
135
printf("sum=%d\n",sum);
6、 goto语句 功能:无条件的转向语句标号所指的语句去执行。 格式:goto 语句标号; 遵循标识符 定名规则 100 例 求∑i i=1 main() {int sum=0;i=1; loop: if (i<=100) {sum+=i; i++; goto loop; } printf("sum=%d\n",sum);
136
素数(质数)就是只 能被1和它自身 整除的正整数。
读入m i=2 i<m m%i==0 break i++ i==m 输出m是素数 真 假 输出m非素数 例:判断数字m是否为素数(质数)。 main() {int i, m; scanf("%d", &m); for (i=2;i<m;i++) if (m%i==0) break; if (i==m) printf("%d is a prime number\n", m); else printf("%d is not a prime number\n", m); } 素数(质数)就是只 能被1和它自身 整除的正整数。
137
printf( "%d is a prime number\n", m); else
k= i=2 i<=k m%i==0 break i++ i>=k+1 真 假 输出m是素数 输出m非素数 #include "math.h", main() {int i, m, k; scanf("%d", &m); k=sqrt(m); for (i=2; i<=k; i++) if (m%i==0) break; if (i>=k+1) printf( "%d is a prime number\n", m); else printf("%d is not a prime number\n", m); }
138
if (i==m) printf("%d,", m); }
m%i==0 break i++ i==m 输出m m++ 真 假 例:求2到32766之间的素数。 输入:无 计算: 求素数 输出:输出素数 main() {int i,m; for (m=2;m<=32766;m++) {for (i=2; i<m; i++) if (m%i==0) break; if (i==m) printf("%d,", m); }
139
第四章 数组 前面我们使用了基本数据类型(如整型、实型、字符型),数组是C语言提供的一种构造类型数据,即由基本数据类型按一定规则组成。 数组是有序数据的集合。数组中的每一个元素都属于同一个数据类型。用一个统一的数组名和下标来唯一地确定数组中的元素。 只能包含数值和符号常量。 表示数组包含的元素个数。 既数组的长度,从0开始。 一、一维数组的定义和引用 一维数组的定义方式: 类型说明符 数组名[常量表达式]; 如:int char float等。 遵循标识符定名规则
140
如:int a[10]; 定义了一个一维数组a,它包含了10个具有整型数据类型的元素。既a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9]。 一维数组的引用: C语言规定只能逐个引用数组元素,不能一次引用整个数组。而且必须先定义数组,然后才能使用数组元素。 数组元素的表示形式: 数组名[下标] a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9] 例如. main() {int i,a[10]; for (i=0;i<=9;i++) a[i]=i; for (i=9;i>=0;i--) printf("%d ",a[i]); } 下标可以是表达式 结果:
141
所谓初始化就是在定义数组的同时,给数组元素赋初始值。C语言规定,只有静态存储和外部存储数组才能初始化。方法见下例:
一维数组的初始化: 所谓初始化就是在定义数组的同时,给数组元素赋初始值。C语言规定,只有静态存储和外部存储数组才能初始化。方法见下例: static int a[10]={0,1,2,3,4,5,6,7,8,9}; a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 静态存储 static int b[10]={0,1,2,3,4}; (后5个元素赋值0。) b[0] b[1] b[2] b[3] b[4]b[5] b[6] b[7] b[8] b[9] static int c[10]={0,0,0,0,0,0,0,0,0,0}; static int c[10] 等价 c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]
142
static int d[]={1,2,3,4,5}; 1 2 3 4 5 可以不指定数组长度,系 统根据后面花括号里的数
d[0] d[1] d[2] d[3] d[4] static int d[]={1,2,3,4,5}; 可以不指定数组长度,系 统根据后面花括号里的数 据决定数组的长度。 一维数组程序举例: 例 给数组a中存入10个整数,在保证数据不丢失的情况下,将数组中的最大数存入a[0]位置。 真 假 i=0; 当i<=9 a[0]<a[i] t=a[0] a[0]=a[i] a[i]=t 输入:10个整数 计算:求最大数 输出:a数组
143
#include " stdio.h " main() { int i, t, a[10]; for (i=0;i<=9;i++) { printf("Please put in %d th numbler:\n" ,i); scanf( "%d", &a[i]); } for (i=0;i<=9;i++) printf("%d ",a[i]); for (i=1;i<=9;i++) if (a[0]< a[i]) { t=a[0]; a[0]=a[i]; a[i] = t; }
144
例 用起泡法对数排序(由小到大)。 起泡法:即是相临两个数比较,将小的调到前头。如:
一维数组程序举例: 例 用起泡法对数排序(由小到大)。 起泡法:即是相临两个数比较,将小的调到前头。如: 985420 895420 859420 854920 854290 854209 第一趟 得到最 大数。 第1次 第2次 第3次 第4次 第5次 结果 85420 58420 54820 54280 54208 5 4 2 8 9 第二趟 第1次 第2次 第3次 第4次 结果
145
即:如果有n个数,则要进行n-1趟比较。在第j趟比较中 要进行n-j次两两比较。
5420 4520 4250 4205 第三趟 420 402 第1次 第2次 结果 第四趟 第1次 第2次 第3次 结果 2 4 5 8 9 最后结果 20 02 第1次 结果 第五趟 即:如果有n个数,则要进行n-1趟比较。在第j趟比较中 要进行n-j次两两比较。
146
例 main() {int a[11],i,j,t; printf("input 10 numbers:\n"); for (i=1;i<11;i++) scanf("%d",&a[i]); printf("\n"); for (j=1;j<=9;j++) for (i=1;i<=10-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"); printf("%d ",a[i]); } 输入n个数给a数组 for j=1 to n-1 for i=1 to n-j a[i]>a[i+1] 真 假 t=a[i] a[i]=a[i+1] a[i+1]=t 输出a[1]到a[n]
147
二、字符数组使用 字符数组的定义、初始化和引用方式: 定义、初始化和引用方法与前面介绍一样。如: char c[10],b[30];
字符数组的定义、初始化和引用方式: 定义、初始化和引用方法与前面介绍一样。如: char c[10],b[30]; static char c[10]={'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'}; I a m h a p p y c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9] static char c[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'} c p r o g r a m c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] static char c[10]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm'} c p r o g r a m c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9] 空格
148
字符数组程序举例: 例 输出一个字符。 main() int i; for (i=0;i<10;i++)
例 输出一个字符。 main() {static char c[10]={'I', ' ', 'a', 'm', ' ', 'a', ' ', 'b', 'o', 'y'}; int i; for (i=0;i<10;i++) printf("%c",c[i]); } 运行结果: I am a boy
149
字符串和字符串结束标志: 例如: " I am happy " 在内存的存放为 字符串:用双引号括起来的一串字符。
字符串结束标志:C语言规定,以字符'\0'作为字符串结束标志。 系统自动在 最后附加 例如: " I am happy " 在内存的存放为 I a m h a p p y \0 C语言中将字符串作为字符数组来处理(即将字符串存放在字符数组中)。当字符串存放在字符数组中时,系统会自动附加一个字符'\0'。 用字符串常量初 始化字符数组 如: static char c[]={"I am happy"}; I a m h a p p y c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]c[10] \0
150
花括号也可省略 系统自动在 最后附加 系统不加 字符'\0'。
static char c[10]={"china"}; c h i n a c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9] \0 花括号也可省略 注意:下面两例的不同 static char c[]="I am happy"; 系统自动在 最后附加 I a m h a p p y c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]c[10] \0 static char c[]= {'I', ' ', 'a', 'm', ' ', 'h', 'a', 'p', 'p', 'y'}; I a m h a p p y c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9] 系统不加 字符'\0'。
151
将整个字符串一次输入输出。用格式"%s"。 {static char c[10]; scanf("%s",c);
字符数组的输入输出: 逐个字符输入输出:用格式"%c"。 如:main() {static char c[10]; int i; for (i=0;i<10;i++) scanf("%c",&c[i]); printf("%c",c[i]); } 数组元素 将整个字符串一次输入输出。用格式"%s"。 如: main() {static char c[10]; scanf("%s",c); printf("%s",c); } 输出时遇到第一个 "\0"字符结束输出。 输入字符串时,不要 超过数组长度。 数组名
152
输出结果: How C语言在输入多个字符串时,以空格分隔。 如: static char c[5],d[5],e[5];
scanf("%s%s%s", c, d, e); H o w c[0] c[1] c[2] c[3] c[4] \0 a r e d[0] d[1] d[2] d[3] d[4] \0 输入数据:How are you? 则结果为 y o u ? e[0] e[1] e[2] e[3] e[4] \0 下面程序输入数据为:How are you? main() {static char c[20]; scanf("%s",c); printf(“%s”,c); } 输出结果: How
153
字符串处理函数: puts(字符数组) 将一个字符串输出到终端。如: static char str[]="China\nXi’an"; puts(str); 输出结果为:China Xi’an gets(字符数组) 从终端输入一个字符串到字符数组,并且得到一个函数值,该函数值是字符数组的起始地址。 如:gets(str); strcat(字符数组1,字符数组2) 连接两个字符数组中的字符串,把字符串2接到字符串1的后面,结果放在字符数组1中,函数调用后得到一个函数值——字符数组1的地址。例如:
154
输出:People’s Republic of China
static char str1[30]="People’s Republic of "; static char str2[]="China"; printf("%s",strcat(str1,str2)); 输出:People’s Republic of China strcpy(字符数组1,字符串2,N) 将字符串2拷贝到字符数组1中。例如: static char str[30]="People’s Republic of "; People’s Republic of \0 …… strcpy(str, "China "); China\0’s Republic of \0…… strcpy(str, "happy ",2); ha\0na\0’s Republic of \0……
155
printf("%d",strlen(str)); 结果为:5
strcmp(字符串1,字符串2) 字符串1与字符串2比较,比较规则是两个字符串自左至右逐个字符相比(按ASCII吗值大小比较),直到出现不同的字符或遇到\0为止。如全部字符相同,则认为相等;若出现不同的字符,则以第一个不同的字符的比较结果为准。比较结果由函数带回。 如果:字符串1==字符串2,函数值为0。 字符串1>字符串2,函数值为一正整数。 字符串1<字符串2,函数值为一负整数。 strlen(字符数组) 求字符串长度。如: static char str[20]="China"; printf("%d",strlen(str)); 结果为:5
156
strlwr(字符串) 将字符串中的大写字母转换成小写字母。 strupr(字符串) 将字符串中的小写字母转换成大写字母。 字符数组程序思考题: 1 、输入一行字符,统计其中有多少个单词,单词之间用空格分割开。 2、 有三个字符串,要求找出其中最大者。
157
三、 二维数组的定义和引用 二维数组的定义方式: 类型说明符 数组名[常量表达式][常量表达式]; 例如:
二维数组的定义方式: 类型说明符 数组名[常量表达式][常量表达式]; 例如: float a[3][4]; /*定义a为3行4列的实型数组*/ int b[5][2]; /*定义b为5行2列的整型数组*/ char c[2][3]; /*定义c为2行3列的字符数组*/ 二维数组元素在内存中的排放顺序是:按行存放。如数组a[3][4]的存放为 内存 a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3]
158
二维数组的引用: 与一维数组一样,只能逐个引用数组元素,不能一次引用整个数组。而且必须先定义二维数组,然后才能使用数组元素。 二维数组元素的表示形式: 数组名[下标][下标] 如: a[2][1]=a[0][0]+a[0][1]; 在引用数组元素时,应注意下标值不要超出已定义数组的范围。 下标可以是表达式 二维数组的初始化: 如:static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}; 内存 a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3]
159
分行赋初值: static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3] 部分元素赋初值: static int a[3][4]={{1},{5},{9}}; a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3] static int a[3][4]={{1},{0,5},{0,0,11}}; a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3]
160
static int a[3][4]={{1},{9}};
a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3] static int a[3][4]={{1},{},{9}}; a[0,0] a[0,1] a[0,2] a[0,3] a[1,0] a[1,1] a[1,2] a[1,3] a[2,0] a[2,1] a[2,2] a[2,3] C语言规定一维长度可省略,但二维长度不能省略。系统会根据顺序赋初值或分行赋初值自动分配存储空间。如: static int a[ ][4]={1,2,3,4,5,6,7,8,9,10,11,12}; 系统根据顺序赋初值自动分配3行4列存储空间。 static int a[ ][2]={{0,3},{},{0,12}}; 系统根据分行赋初值自动分配 3行2列存储空间。
161
几点注意: 在数组定义时,一般要加“静态存储”static或“外部存储”extern。它可以使未赋初值的数组元素为零值。 数组引用的下标,从零开始。 可以把二维数组看作是一种特殊的一维数组:它的元素又是一个一维数组。如:float a[3][4]; a[0] a[0][0] a[0][1] a[0][2] a[0][3] a a[1] a[1][0] a[1][1] a[1][2] a[1][3] a[2] a[2][0] a[2][1] a[2][2] a[2][3] C语言还允许使用多维数组,它们的定义和引用方法与二维数组的定义和引用相同。例如: float a[2][2][2]; a为一个三维数组,在内存中的排列顺序为: a[0][0][0] a[0][0][1] a[0][1][0] a[0][1][1] a[1][0][0] a[1][0][1] a[1][1][0] a[1][1][1]
162
{static int a[2][3]={{1,2,3},{4,5,6}},b[3][2],i,j;
二维数组程序举例: 例 将一个二维数组行和列元素互换,存到另一个二维数组中。 1 4 2 5 3 6 a= b= main() {static int a[2][3]={{1,2,3},{4,5,6}},b[3][2],i,j; printf("array a:\n"); for (i=0;i<=1;i++) {for (j=0;j<=2;j++) {printf("%5d",a[i][j]); b[j][i]=a[i][j];} printf("\n"); } printf("array b:\n"); 输出a数组 元素转换
163
例 有一个3*4的矩阵,要求编程序以求出其中值最大的那个元素的值,以及其所在的行号和列号。
for (i=0;i<=2;i++) { for (j=0;j<=1;j++) printf("%5d",b[i][j]); printf("\n"); } 输出b数组 a[i][j]>max max=a[i][j] row=i column=j 真 假 for j=0 to 3 for i=0 to 2 输出:max,row和column max=a[0][0] 例 有一个3*4的矩阵,要求编程序以求出其中值最大的那个元素的值,以及其所在的行号和列号。
164
colum=j; } printf("max=%d,row=%d,colum=%d\n",max,row,colum); }
main() { int i,j,row=0,column=0,max; static 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,row=%d,colum=%d\n",max,row,colum); } 结果: max=10, row=2, colum=1
165
* * * * * 二维数组程序举例: 例 输出一个钻石图形 main()
例 输出一个钻石图形 main() {static char diamond[][5]={{' ', ' ', ' * ',},{' ', ' * ', ' ', ' * '}, {' * ', ' ', ' ', ' ', ' * '},{' ', ' * ', ' ', ' * '},{' ', ' ', ' * '}}; int i,j; for (i=0;i<5;i++) {for (j=0;j<5;j++) printf("%c",diamond[i][j]); printf("\n");} } * * * * * 运行结果:
166
四、程序设计方法 1、枚举原则 本节主要介绍程序设计的三条基本思维原则,即枚举原则、归纳原则和抽象原则。
本节主要介绍程序设计的三条基本思维原则,即枚举原则、归纳原则和抽象原则。 1、枚举原则 枚举法,也称穷举法,就是逐一列举这个集合里的各个元素,并加以判断,直到求得所需要的解,是人们常用的一种思维方法。 主要两条原则: ①确定搜索范围 ②选择搜索策略 例 求整数a和b的最小公倍数i。 如果i为a和b的最小公倍数,则必能被a和b整除,同时必须是自然数,所以其取值范围为1-∞。
167
{printf(“%d\n”,i); break; }
main( ) { int a, b, i=0; printf(“ Input numbers:\n”); scanf( “%d, %d”, &a, &b); while(1) { i++; if (i%a==0) if(i%b==0) {printf(“%d\n”,i); break; } } [方法一]: i从1开始,依次增加1,直到第一个能被a和b整除为止,这个i就是a和b的最小公倍数。于是可编制程序如下: 假设a=7,b=5,则循环体将被执行35次。
168
printf(“Input numbers:\n”); scanf(“%d,%d”,&a,&b); while(1) { i+=a;
main( ) { int a , b, i=0; printf(“Input numbers:\n”); scanf(“%d,%d”,&a,&b); while(1) { i+=a; if (i%b= =0) { printf(“%d\n”,i); break; } } [方法二]:令i从a开始,而不是从1开始,使i每次增加a而不是增加1,这就保证了i总是a的倍数。因此,每次只要判断i能否被b整除就可以了。一旦判断成立,i就是a和b的最小公倍数。 对同样的a=7,b=5,循环次数仅为方法一的1/7(5次)。
169
2、归纳原则 归纳法,是从大量的特殊性中总结出规律性或一般性的结论。在程序设计上主要表现为递归和迭代,把一个复杂的计算过程化为简单的、很容易用循环来实现的多次重复过程。 归纳法的数学模型具有如下统一的形式: ①确定初始值; ②确定递推公式,并反复使用这个递推公式,直到求出问题的答案为止。 例 n! 例 菲波那契数列F(n)
170
printf(“%d!=%.0f\n”,n,p); } 求n! 用p来存放阶乘值,显然其初值为1,递推公式为:
程序如下: main( ) {int i,n; float p=1; scanf(“%d”,&n); for(i=1;i<=n;i++) p=p*i; printf(“%d!=%.0f\n”,n,p); } 求n! 用p来存放阶乘值,显然其初值为1,递推公式为: p=p*i (i从1变化到n)
171
3、抽象原则 为解决一个复杂的问题而设计出一个抽象算法,再将算法求精,使之更加细化,清晰。直到能轻松用C语言语句写出程序为止。
例 从键盘上任意输入N个整数,将其从大到小排序输出。 排序问题是程序设计中的典型问题,必须首先明确两点: (1)排序过程中,数据间要进行多次比较、交换,所以,必须使用数组存储这些数。 (2)排序方法很多,其中选择排序法和冒泡排序法是最常用的方法。下面先介绍选择排序法。
172
程序框架: printf ("Input data:"); for (i=0;i<N;i++) scanf("%d",&a[i]);
#define N 10 main( ) { int a[N],i; printf ("Input data:"); for (i=0;i<N;i++) scanf("%d",&a[i]); /*通过键盘给数组元素输入值*/ 将数组a中的N个数从大到小排序; 输出排序结果; } 选择排序的基本过程是: 第0步: 将a[0]依次与a[1],a[2],…,a[N-1]比较,必要时交换 第一步:将a[1]依次与a[2],a[3],…,a[N-1]比较,必要时交换 …… 第N-2步:将a[N-2]与a[N-1]比较,必要时交换。
173
printf (“please input number:”); for (i=0;i<N;i++) /*通过键盘给数组元素输入值*/
#define N 10 main( ) { int a[N], i, j, t; printf (“please input number:”); for (i=0;i<N;i++) /*通过键盘给数组元素输入值*/ scanf(“%d”,&a[i]); for(i=0;i<N-1;i++) /*排序*/ for(j=i+1;j<N;j++) if(a[i]<a[j]) {t=a[i];a[i]=a[j];a[j]=t;} /*数组元素值交换*/ for(i=0;i<N;i++) /*输出排序结果*/ { printf(“%6d”,a[i]); if((i+1)%5==0) /*每行输出5个元素*/ printf(“\n”); } 完整程序如下:
174
#include<stdio.h> main( ) {int i, n; float a,max,min;
printf(“请输入数据个数n:”); scanf(“%d”,&n); printf(“请输入第1个数:”); scanf(“%f”,&a); max=min=a; 假设任给n个数为a1,a2……an,,用max和min分别存放最大值和最小值。先令max=a1,min=a1然后将max和min依次与a1,a2……an比较,若发现max<ai,则令max=ai,若min>ai则令min=ai,全部比较完后,max和min就是ai中的最大的值和最小值。程序如下:
175
for(i=2;i<=n;i++) { printf(“请输入第%d个数:” ,i); scanf(“%f”,&a); if (max<a) max=a; if(min>a) min=a; } printf(“max=%f,min=%f\n”,max,min);
176
四 、思考题 1) 用—≈1- — + — - — + ┄公式求的近似值,直到最后一项的绝对值小于10-6为止。
1) 用—≈1- — + — - — + ┄公式求的近似值,直到最后一项的绝对值小于10-6为止。 2) 求 Fibonacci 数列:1,1,2,3,5,8,……的前个数。 3) 判m是否素数。 4) 求100~200间的全部素数。
177
第五章 函数及程序结构 main() c a b d e f g h h i g e
第五章 函数及程序结构 一个C程序可由一个主函数和若干个函数构成。由主函数调用其它函数,其它函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。C程序的执行从main()函数开始,调用其它函数后回到main()函数,在main()函数中结束整个程序的运行。 main() 函数调用示意图 c a b d e f g h h i g e 函数使我们设计程序的复杂程度得以简化。我们可以将一个任务划分为若干个功能模块,每一个功能模块用一个函数来完成。功能相同的模块只编一个函数。常用的功能模块编写成函数,放在函数库中供公共选用,如:printf()、gets()等函数。
178
printf("*****************************\n"); print_message()
例 main() {printstar(); print_message(); printstar(); } printstar() { printf("*****************************\n"); print_message() printf(" How do you do!\n"); 运行结果为: ****************************** How do you do!
179
#include "stdio.h" main() {char c; c=getchar(); printstr();
从用户的角度看: 标准函数(库函数)。由系统提供 用户自定义函数。 用户自己编制 函数分为 从函数的形式看: 函数分为 无参函数 有参函数 #include "stdio.h" main() {char c; c=getchar(); printstr(); printf("%c",c); } 无参函数 自定义函数 例如 标准函数 有参函数
180
实验6 数组说明、定义和使用和及排序、搜索算法(14周)
一、实验目的 掌握C语言中实现循环的常用的算法(如穷举法等)和数组有关的算法(特别是排序法)。 掌握C语言中一维和二维数组的定义、赋值和使用; 掌握常用的排序、搜索算法; 二、实验内容 1、调试 例6.1(P141) 例6.2(P142) 例6.3(P143) 例6.4(P148)例6.5(P149) 例6.6(P152)例6.7(P157) 例6.8(P160) 2、编写并调试习题 习题 1-9(P167) 三、程序清单 四、结果及分析
181
一、 函数定义的一般形式 无参函数的定义形式 类型标识符 函数名() 类型标识符规定了一 {说明部分 个函数返回给调用函 数的数据的类型。
类型标识符 函数名() {说明部分 语句} 有参函数的定义形式 类型标识符 函数名(形式参数表列) 形式参数说明 类型标识符规定了一 个函数返回给调用函 数的数据的类型。 C语言规定: 省略类型标识符 的函数,一律按 整型处理。
182
int x,y; { int z; z=x>y?x:y; return(z); } 例如: int max(x,y) 形式参数表列
形式参数说明 返回给调用 函数的数据 C语言可以有空函数,形式为: 类型标识符 函数名() {} 例如:dummy() {}
183
二、函数参数和函数的值 例如: main() {int a,b,c; main() {int a,b,c;
形式参数和实际参数:形式参数用于接收从调用函数传递来的数据。在未出现调用时,它们并不占内存中的存储单元,只有调用时才被分配内存单元。 二、函数参数和函数的值 例如: main() {int a,b,c; main() {int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); scanf("%d,%d",&a,&b); c=max(a,b); printf("max is %d",c); } printf("max is %d",c); } a b c 6 5 6 max(x,y) int x,y; max(x,y) int x,y; 6 5 6 {int z; {int z; x y z z=x>y?x:y; z=x>y?x:y; return(z); } return(z); }
184
6 5 a b x y {… } 说明:实参可以是常量、变量或表达式。 必须指定形参的类型,且实参与形参的类型应一致。
必须指定形参的类型,且实参与形参的类型应一致。 C语言规定,实参变量对形参变量的数据传递是“值 传递”,由实参传递给形参,而不能由形参传回来给 实参。 6 5 a b x y 在定义形参类型时,还可以采用以下形式: int max(int x,int y); {… }
185
max is 2 函数的返回值 通过函数调用使主调函数能得到一个确定的值,这个值就是函数的返回值。
通过函数调用使主调函数能得到一个确定的值,这个值就是函数的返回值。 函数的返回值是通过函数中的return语句获得的。 return语句将被调函数中的一个确定的值带回主调函数中去。如果主调函数不要返回值,则可以不要return语句。 函数值的类型应该和return语句中表达式的类型一致。如果不一致,以函数类型为准。对数值型数据,可以自动进行类型转换。例如: max(float x,float y) {float z; z=x>y?x:y; return(z); } main() {float a,b; int c; scanf("%f,%f",&a,&b); c=max(a,b); printf("max is %d\n",c); } 实型 整型 运行时若输入 1.5,2.5 则输出结果为: max is 2
186
printf("%d,%d,%d\n",a,b,c); }
main() { int a,b,c; a=printstar(); b=print_message(); c=printstar(); printf("%d,%d,%d\n",a,b,c); } 如果被调函数中没有return语句,此时函数带回一个不确定的值。例如: C语言规定,可以用“void”定义“无类型”(或称“空类型”)。这样,系统就使函数不带回任何值。 如有 void printstar() {…} 则:a=printstar(); 在编译时系统会给出出错信息。
187
{if (x>y) return(x); else return(y); }
一个函数中可以有一个以上的return语句,执行到那一个return语句,那一个语句起作用。如: max(x,y) int x,y; {if (x>y) return(x); else return(y); } return后面的值可以是一个表达式。如: max(x,y) int x,y; { return(x>y?x:y); } 表达式
188
如果函数类型是void的话,那么return相当与结束该函数,并不返回值。 不管有没有返回值,遇到return该函数就结束了。
main函数一般不要用void类型,不是语法不允许,是与移植有关。void main()是很不规范的写法。
189
三、函数的调用 main() {int i=2,p; p=f(i,++i); printf("%d",p); } int f(a,b)
int a,b; {int c; if (a>b) c=1; else if (a==b) c=0; else c=-1; return(c); 一般形式为:函数名(实参表列) (实参与形参按顺序对应,一一传递数据。) 例 Turbo C实参求值按: 自右至左顺序。 等价于 p=f(3,3); 如果要传递自左至右 顺序可采用如下格式: j=i; k=++i; p=f(j,k); 等价于 p=f(2,3);
190
函数的调用方式 函数语句。如: printstar(); 函数表达式。(即函数出现在表达式中。) c=2*max(a,b); 函数参数。函数调用作为一个函数的实参。如: m=max(a,max(b,c)); printf("%d",max(a,b)); 几点说明: 被调用函数必须存在。 如果使用库函数,在文件开始用include命令将调用有关库函数所用到信息包含到本文件来。
191
例 main() {float add(); float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b);
如果使用自定义函数,一般应该在主调函数中对被调函数进行说明。一般格式为: 类型标识符 被调用函数的函数名( ) 例 对被调函数进行说明 main() {float add(); float a,b,c; scanf("%f,%f",&a,&b); c=add(a,b); printf("sum is %f",c); } 定义add函数 float add(x,y) float x,y; {float z; z=x+y; return(z); }
192
main() {float a,b,c; scanf (“%f,%f”,&a,&b;) float add(x,y) float x,y;
如果在主调函数中没有对被调函数进行说明,系统自动按整型说明。 如果被调函数定义出现在主调函数之前,可以不进行说明。 如果已在所有函数之前,在文件的开头,在函数的外部已说明了函数类型,则在各个主调函数中不必对所调用的函数再进行类型说明。 主调函数不必 说明add函数。 例如: main() {float a,b,c; scanf (“%f,%f”,&a,&b;) c=add(a,b); printf("sum is %f",c); } float add(x,y) float x,y; {float z; z=x+y; return(z);}
193
四、函数的嵌套调用 C语言允许在调用一个函数的过程中,又调用另一个函数。这称为函数的嵌套调用。 a函数 b函数 main函数 调用a函数
C语言允许在调用一个函数的过程中,又调用另一个函数。这称为函数的嵌套调用。 a函数 b函数 main函数 调用a函数 调用b函数 b函数 结束 a函数 结束 结 束
194
五、函数的递归调用 int f(x) int x; int f2(t) int f1(x) int t; int x; {int y,z;
在调用一个函数的过程中又出现直接或间接地调用该函数本身。这称为函数的递归调用。 例如: 直接调用 间接调用 int f(x) int x; {int y,z; z=f(y); return(2*z); } int f2(t) int t; {int a,c; c=f1(a); return(3+c); } int f1(x) int x; {int y,z; z=f2(y); return(a*z); }
195
递归函数的定义,不应出现无终止的递归调用。而应定 义为有限次数、有终止的递归调用函数。
递归函数的定义,不应出现无终止的递归调用。而应定 义为有限次数、有终止的递归调用函数。 对于一个问题,只要能够知道递归定义式,及边界条件(即递归终止的条件),就可以编写一个递归函数。例如: 递归定义式 边界条件 (n=0) n*(n-1) ! (n>0) n! 可以定义为 n*(n-1)! n!= n i=1 n-1 i 可以定义为 n+ i (n=1) n+i (n>1) i= xn 可以定义为 x*x n xn= (n=0) x*x n (n>0)
196
用递归函数的方法计算s=83 printf("s=%d",sum(3)) 显示:s=512 main() { int sum();
返回512 main() printf("s=%d",sum(3)) 512 return(8*sum(2)) 返回64 return(8*sum(1)) 返回8 显示:s=512 return(8*sum(0)) main() { int sum(); printf("s=%d",sum(3)); } int sum(int n) {if (n>0) return(8*sum(n-1)); else return(1); 返回1 return(1)
197
六、数组作为函数的参数 a b C语言允许用数组元素作为实参,其用法与变量相同。同时也允许用数组名做实参和形参。 数组元素做函数实参
C语言允许用数组元素作为实参,其用法与变量相同。同时也允许用数组名做实参和形参。 数组元素做函数实参 用法与变量相同,也是“值传递”。 数组名做实参和形参 数组名做实参和形参,是将实参数组的起始地址传递给形参数组,这样两个数组就共占用同一段内存单元。 例如:若实参数组a的起始地址为2000,则传递给形参数组b的值是地址2000。 a 2000 …… …… b
198
例: 有一个一维数组score,内放10个学生成绩,求平均成绩。
数组名做形参 main() {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("average score is %5.2f",aver); } float average(array) 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); } 数组元素做实参 数组名做实参
199
用数组名做实参和形参,应注意以下几点: 在主调函数和被调用函数中分别定义数组。 实参数组与形参数组类型应一致。 实参数组与形参数组大小可以一致也可以不一致,C编译对形参数组大小不作检查,只是将实参数组的首地址传递给形参数组。 实参和形参是地址传递,因而形参数组中各元素的值发生变化会使实参数组元素的值同时发生变化。 例 用选择法对数组中10个整数由小到大排序。 选择法:先将10个数中最小的数与a[0]对换;再将a[1]到a[9]中最小的数与a[1]对换;……,每比较一轮,找出一个未经排序数中最小的一个。共应比较9轮。
200
array a void sort(array,n) int array[]; for (j=1;j<=n-1;j++)
int n; {int i,j,t; for(i=0;i<n-1;i++) { for (j=i+1;j<n;j++) if (array[i]>array[j]) { t=array[i]; array[i]=array[j]; array[j]=t;} } main() {int a[10],i; printf("enter the array\n"); for (i=0;i<10;i++) scanf("%d",&a[i]); for (j=1;j<=n-1;j++) for (i=0;i<n-j;i++) if (a[i]>a[i+1]) {t=a[i];a[i]=a[i+1];a[i+1]=t;} sort(a,10); printf("the sorted array:\n"); for (i=0;i<10;i++) printf("%d,",a[i]); printf("\n"); } array a a[0]a[1]a[2]1[3]a[4]a[5]a[6]a[7]a[8]a[9]
201
array a void sort(array,n) int array[]; sort(a,10);
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;} } main() {int a[10],i; printf("enter the array\n"); for (i=0;i<10;i++) scanf("%d",&a[i]); sort(a,10); printf("the sorted array:\n"); for (i=0;i<10;i++) printf("%d,",a[i]); printf("\n"); } a[0]a[1]a[2]1[3]a[4]a[5]a[6]a[7]a[8]a[9] array a
202
例: 有一个3*4的矩阵,求其中的最大元素。 max_value(array) int array[ ][4]; {int i,j,max;
例: 有一个3*4的矩阵,求其中的最大元素。 max_value(array) int array[ ][4]; {int i,j,max; max=array[0][0]; for (i=0;i<3;i++) for (j=0;j<4;j++) if (array[i][j]>max) max=array[i][j]; return(max); } main() {static int a[3][4]={{1,3,5,7},{2,4,6,8},{15,17,34,12}}; printf("max value is %d\n",max_value(a)); 多维数组名作为实参和形参, 在被调用函数中对形参数组定 义时可以指定一维的大小,也 可以省略第一维的大小说明。 练习:定义一个函数,功能是将含有N个元 素的整型数组中的数据前后顺序颠倒。
203
p186 调试题 3 # include "stdio.h" main() { int i,j,k; for(i=0;i<=4;i++) { for(j=0;j<=3-i;j++) printf(" "); for(k=0;k<=2*i;k++) printf("*"); printf("\n"); } for(i=0;i<=3;i++) { for(j=0;j<=i;j++) for(k=0;k<=6-2*i;k++) printf("*") ; printf("\n"); } }
204
P189 14 #include "stdio.h" #include "time.h" #include "stdlib.h" main() { int score=0,i,j,k,a[10],b[10],sum[10]; printf("add number: \n"); randomize(); for(i=0;i<10;i++) { a[i]= random(100); printf("%d ",a[i]); } printf(" \n"); printf("added number: \n"); /* randomize(); */
205
for(i=0;i<10;i++) { b[i]= random(100); printf("%d ",b[i]); } printf(" \n"); { printf(" (%d) %d+%d=",i+1,a[i],b[i]); scanf("%d", &sum[i]); if(sum[i]==a[i]+b[i]) { printf("very good!\n"); score=score+10; else { printf("try again!\n"); printf("%d+%d=",a[i],b[i]); scanf("%d",&sum[i]); { printf("This good!\n");
206
else { printf("still wrong\n"); } printf("Your score is %d\n",score);
score=score+5; } else { printf("still wrong\n"); } printf("Your score is %d\n",score); k=score/10; switch(k) { case 10 : printf("perfect!!"); break; case 9 : printf("Very very good!"); break; case 8: printf("Very good"); break; case 7: printf("good"); break; case 6: printf("Pass"); break; case 5: case 4: case 3: case 2: case 1: case 0: printf("flunk!"); break; } }
207
例 p211 sort(b,n) int b[][20],n; { int i,j,t; for(j=1;j<n;j++) for(i=0;i<n-j;i++) if(b[1][i]<b[1][i+1]) { t=b[0][i];b[0][i]=b[0][i+1];b[0][i+1]=t; t=b[1][i];b[1][i]=b[1][i+1];b[1][i+1]=t; }
208
insert(b,n) int b[][20],n; {int i,j,x,t,number; sort(b,n); printf("\nPlease input the number you want to insert: "); printf("\nscore="); scanf("%d",&x); printf("number="); scanf("%d",&number);
209
for(i=0;i<n;i++) if(x>=b[1][i]) { for(j=n-1;j>=i;j--), { b[0][j+1]=b[0][j]; b[1][j+1]=b[1][j]; } b[1][i]=x; b[0][i]=number; return (n+1); else if(x<b[1][n-1]) { b[1][n]=x; b[0][n]=number;
210
delete1(b,n) int b[ ][20],n; { int i,j;
printf("\nPlease input the number (from 0 to %d): ",n-1); scanf("%d",&i); for(j=i;j<n-1;j++) { b[0][j]=b[0][j+1]; b[1][j]=b[1][j+1];} return(n-1); }
211
print(b,n) int b[][20],n; { int i,j; printf("\n"); for(i=0;i<2;i++) { for(j=0;j<n;j++) printf("%6d",b[i][j]); }
212
main() { int n=6,m=5; int a[2][20]={{1001,1053,1023,1011,1009,1020}, {63,98,75,61,80,92}}; while(m!=0) { printf("\nPlease input your choice(0-4): "); scanf("%d",&m); switch(m) { case 1:n=insert(a,n);break; case 2:sort(a,n);break; case 3:n=delete1(a,n);break; case 4:print(a,n);break; default:printf("error!"); } } }
213
例: 用函数,判定某数是否为素数。 main() {int x; printf(“\nEnter a integer number: ”);
scanf(“%d”,&x); if(isprime(x)) printf(“\n%d is prime”,x); else printf(“\n%d is not prime”,x); } isprime(int n) { int i; for(i=2;i<=sqrt((double(n));i++) if ((n%i)= =0) return 0; return 1; }
214
return max(x,y,z)-min(x,y,z); }
例: 求三个数中最大数和最小数的差值。 int max(x,y,z) int x,y,z; {int r,t; r=x>y?x:y; t=r>z?r:z; return t; } int min(x,y,z) r=x<y?x:y; t=r<z?r:z; int div(x,y,z) int x,y,z; { return max(x,y,z)-min(x,y,z); } main() { int a,b,c,d; scanf(“%d,%d,%d”,&a,&b,&c); d=div(a,b,c); printf("\nThe result is: %d",d);
215
七、局部变量和全局变量 a,b,c有效 x,y,i,j有效 m,n有效 局部变量
在一个函数内部定义的变量是内部变量(即局部变量),它只在本函数范围内有效,也就是说只有在本函数内才能使用它们,在此函数以外是不能使用这些变量的。 float f1(a) int a; {int b,c; } char f2(x,y) int x,y; {int i,j; main() {int m,n; a,b,c有效 x,y,i,j有效 形式参数也是 局部变量。 m,n有效
216
main() {int a,b; {int c; c=a+b; } a,b有效 c有效
全局变量 在函数外部定义的变量称为全局变量(外部变量)。全局变量可以为本文件中其它函数所共有。其有效范围为:从定义变量的位置开始到本源文件结束。
217
例如: int p=1,q=5; float f1(a) int a; {int b,c; 外部变量p,q说明 }
char c1,c2; char f2(x,y) int x,y; {int I,j; main() {int m,n; } 外部变量p,q说明 外部变量 c1,c2说明 全局 变量 p,q 有效 全局 变量 c1,c2 有效
218
例: 有一个一维数组,内放10个学生成绩,写一个函数,求出平均分,最高分和最低分。
float max=0,min=0; float average(array,n) float array[ ];int n; {int i; float aver,sum=array[0]; max=min=array[0]; for (i=1;i<n;i++) {if (array[i]>max) max=array[i]; else if (array[i]<min) min=array[i]; sum=sum+array[i]; } aver=sum/n; return(aver);
219
printf("max=%6.2f\nmin=%6.2f\n average=%6.2f\n",max,min,ave); }
main() {float ave,score[10]; int i; 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); } main函数 score 10 ave max min average函数 array n aver max min
220
int max(x,y) 外部变量说明 int x,y; main() {int z; {extern int a,b;
全局变量增加了函数间数据联系的渠道。有时可以利用全局变量得到一个向上的返回值。但使用时应注意以下几点: 全局变量始终占用存储单元。 它使函数的通用性降低了。 如果全局变量定义之前的函数想引用该外部变量,可在该函数中用关键字extern作“外部变量说明”,就可使用该全局变量。如: int max(x,y) int x,y; {int z; z=x>y?x:y; return(z); } 外部变量说明 main() {extern int a,b; printf(%d,max(a,b)); } int a=13,b=-8; 外部变量定义
221
int a=3,b=5; 例: max(a,b) int a,b; { int c; c=a>b?a:b; return(c); }
如果在同一个源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量不起作用。 int a=3,b=5; max(a,b) int a,b; { int c; c=a>b?a:b; return(c); } 例: 外部变量a,b说明 形参变量a,b有效 全局变量a,b无效 main() {int a=8; printf("%d",max(a,b)); } 局部变量a有效 全局变量b有效
222
八、动态存储变量与静态存储变量 变量的存储类别 从变量值存在的时间来分,可以分为静态存储变量和动态变量。
从变量值存在的时间来分,可以分为静态存储变量和动态变量。 静态存储:指在程序运行期间分配固定的存储空间。 动态存储:指在程序运行期间根据需要进行动态的分配存储空间。 内存中供用户使用的存储空间情况: 形参变量、未 加static说明 的局部变量和 函数调用时的 现场保护和返 回地址。 全局变量和 static说明的 局部变量 程 序 区 静态存储区 动态存储区
223
int f(a) int a; {auto int b,c=3; C语言变量的两个属性: 数据类型:如int、float、char等。
数据的存储类别:静态存储和动态存储。具体包含四种: auto 自动的, static 静态的, register 寄存器的, extern 外部的。 局部变量的存储方式 函数中的局部变量,如不做专门的说明,都是在动态存储区分配存储空间。这类局部变量称为自动变量,可用关键字auto作存储类型说明。如: int f(a) int a; {auto int b,c=3; } auto可以省略,系 统按自动类型处理
224
用static说明局部变量,称为局部静态变量,在静态存储区分配存储空间。在调用函数结束后,其占据的存储单元不释放,下一次调用该函数,其值为上一次调用结束时的值。
例: f(a) int a; {auto int b=0; static int c=3; b=b+1; c=c+1; return(a+b+c); } main() {int a=2,i; for (i=0;i<3;i++) printf("%d,",f(a)); 第三次 调用开始 第一次 调用开始 第三次 调用结束 第二次 调用结束 第二次 调用开始 第一次 调用结束 在编译时 赋值只执 行一次。 b c 1 1 1 5 6 4 运行结果为:7 ,8 ,9
225
对局部静态变量的几点说明: 局部静态变量属于静态存储类别,在静态存储区内分配存储单元。 对局部静态变量是在编译时赋初值,以后调用函数时不再重新赋初值。而自动变量是在函数调用时才赋初值,每调用一次函数重新赋一次初值。 定义局部变量如不赋初值,则对静态局部变量,系统在编译时自动赋初值0或空字符(\0)。对自动局部变量,它的值是一个不确定的值。 局部静态变量在函数调用结束后依然存在,但其它函数不能引用它。 寄存器变量:将局部变量的值放在运算器中的寄存器中,需要时直接从寄存器取出参与运算。用关键字register作说明。例如:
226
printf("%d!=%d\n",i,fac(i));
例: 定义寄存器变量i,f。 int fac(n) int n; {register int i,f=1; for (i=1;i<=n;i++) f=f*i; return(f); } main() {int i; for (i=1;i<=5;i++) printf("%d!=%d\n",i,fac(i)); 注:只有局部自动变量和形式 参数可以作为寄存器变量。 寄存器数目是有限的。 Turbo C对寄存器变量当作 自动变量处理。 不能对寄存器变量进行取地 址运算。 结果:1!=1 2!=2 3!=6 4!=24 5!=120
227
全局变量的存储方式 file1.c extern int a; power(int n) {int i,y=1;
全局变量在编译时分配在静态存储区。 一个C语言程序可以由一个或多个源程序文件组成。如果程序由一个源文件组成,则全局变量可以为程序中各个函数所引用。如果由多个源程序文件组成,则分为两种情况: 允许其它文件中的函数引用,但应在需要引用它的文件中,对引用变量用extern作说明。例如: file1.c extern int a; power(int n) {int i,y=1; for (i=1;i<=n;i++) y*=a; return(y); }
228
file2.c int a; main() {int power(); int b=3,c,d,m; printf ("enter the number a and its power:\n"); scanf ("%d, %d", &a, &m); c=a*b; printf ("%d*%d=%d ", a, b, c); d=power(m); printf ("%d**%d=%d",a,m,d); }
229
file1.c file2.c extern int a; fun(n) int n; { a=a*n; }
只被本文件中的函数引用。在定义外部变量时前面加一个static说明。例如: file1.c file2.c extern int a; fun(n) int n; { a=a*n; } static int a; main() { } 不能使用 a变量。
230
九、内部函数和外部函数 内部函数 如果一个函数只能被本文件中其它函数所调用,称为内部函数。(也称静态函数) 定义格式为:
如果一个函数只能被本文件中其它函数所调用,称为内部函数。(也称静态函数) 定义格式为: static 类型标识符 函数名(形参表) 如:static int fun(a,b) 外部函数 在定义一个函数时,冠以关键字extern,则该函数是外部函数,可为本文件和其它文件中的函数调用。C语言规定,在定义函数时,如果省略extern关键字,隐含为外部函数。在调用此函数的文件中,一般要用extern说明所用的函数是外部函数。例如:
231
例: 有一个字符串,内有若干个字符,今输入一个字符,程序将字符串中该字符删除。
file1.c(文件1) main() {extern enter_string(),delete_string(),print_string(); char c; static char str[80]; enter_string(str); scanf("%c",&c); delete_string(str,c); print_string(str); } file3.c(文件3) extern delete_string(str,ch) char str[],ch; {int i,j; for (i=j=0;str[i]!='\0';i++) if (str[i]!=ch) str[j++]=str[i]; str[j]='\0'; } file2.c(文件2) #include stdio.h extern enter_string(str) char str[80]; {gets(str); } file4.c(文件4) extern print_string(str) char str[ ]; {printf("%s",str); }
232
第六章 指 针 一、指针的概念 内存 指针:一个存储对象在内存中存放的起始地址,数据对象的地址。
第六章 指 针 一、指针的概念 指针:一个存储对象在内存中存放的起始地址,数据对象的地址。 指针变量:保存其它对象内存地址(指针)的数据对象,存放另一个变量地址的变量。 内存 2000 2002 2004 3 6 9 变量i 变量j 变量k 2000 变量i_pointer
233
注意:一个指针变量只能指向同一个类型的变量。
二、变量的指针和指向变量的指针变量 指针变量的定义: 类型说明符 *标识符 指针变量的名字 “*”表示后面是一个指针变量 例如: int *p1,*p2; float *p3,*p4; char *c1,*c2; 注意:一个指针变量只能指向同一个类型的变量。 如:p3,p4只能指向float型数据。
234
a &a 100 指针变量的引用 两个运算符: & 取地址运算符 * 指针运算符
两个运算符: & 取地址运算符 * 指针运算符 指针变量中只能存放地址,不能将其它类型的数据值赋给一个指针变量。 例如: 若有 int *pointer_1, a; 则可 pointer_1=&a; *pointer_1=100; 不能有 pointer_1=100; pointer_1 a &a *pointer_1 100
235
int *pointer_1,*pointer_2; a=100;b=10; pointer_1=&a; pointer_2=&b;
main() { int a,b; int *pointer_1,*pointer_2; a=100;b=10; pointer_1=&a; pointer_2=&b; printf("%d,%d\n",a,b); printf("%d,%d\n", *pointer_1,*pointer_2); } 例 结果: 100, 10 下面的表达式含义是什么? &*pointer_1 *&a (*pointer_1)++ *pointer_1++ &a a a++ 先调用指针变量得到a, 再对指针变量自增。
236
例 main() {int *p1,*p2,*p,a,b; scanf("%d,%d",&a,&b); p1=&a;p2=&b;
if (a<b) {p=p1;p1=p2;p2=p;} printf("a=%d,b=%d\n",a,b); printf("max=%d,min=%d\n",*p1,*p2); } p1 a &b 5 &a p &a p2 b &a 9 &b 结果: a=5, b=9 max=9, min=5 指针变量作为函数参数 指针变量作为函数的参数,是将一个变量的地址传送到另一个函数中。
237
例: swap(p1,p2) int *p1,*p2; { int p; p=*p1; *p1=*p2; *p2=p;} 结果为: 9,5
pointer_1 &a &a p1 a 5 9 main() {int a,b; int *pointer_1,*pointer_2; scanf("%d,%d",&a,&b); pointer_1=&a;pointer_2=&b; if (a<b) swap(pointer_1,pointer_2); printf("\n%d,%d\n",a,b); } p 5 b 5 9 &b p2 pointer_2 &b
238
实验8 C语言指针的定义和使用(17周) 一、实验目的 1、掌握指针的概念、定义和指针变量的使用; 2、学会使用数组的指针和指向数组的指针变量; 3、学会使用字符串的指针和指向字符串的指针变量; 4、了解使用指向函数的指针变量和指向指针的指针的概念和使用方法。 二、实验内容 1、调试 例8.1(P211) 例8.2(P214) 例8.3(P215) 例8.4(P218) 例8.5(P219) 例8.6(P221) 例8.7(P223) 例8.8(P226) 例8.9(P228) 例8.10(P230) 例8.11(P231) 例8.14(P234) 2、编写并调试习题 习题 1-11(P247)、 三、程序清单 四、结果及分析
239
例 输入a,b,c三个整数,按大小顺序输出。 swap(pt1,pt2) int *pt1,*pt2; {int p; p=*pt1; *pt1=*pt2; *pt2=p; } exchange(q1,q2,q3) int *q1,*q2,*q3; {if (*q1<*q2) swap(q1,q2); if (*q1<*q3) swap(q1,q3); if (*q2<*q3) swap(q2,q3); } main() {int a,b,c,*p1,*p2,*p3; scanf("%d,%d,%d",&a,&b,&c); p1=&a;p2=&b;p3=&c; exchange(p1,p2,p3); printf("\n%d,%d,%d\n",a,b,c); }
240
三、数组的指针和指向数组的指针变量 p 数组的指针就是数组的起始地址。数组元素的指针是数组元素的地址。 指向数组元素的指针变量的定义与赋值
数组的指针就是数组的起始地址。数组元素的指针是数组元素的地址。 指向数组元素的指针变量的定义与赋值 方法同指向变量的指针变量一样。例如: int array[10]; int *p; p=&array[2]; array array[0]array[1]array[2]array[3]array[4]array[5]array[6]array[7]array[8]array[9] p &array[2]
241
int array[10]; int *p; p=&array[0]; C语言规定:数组名代表数组的首地址。也就是数组第一个元素的地址。 p
C语言规定:数组名代表数组的首地址。也就是数组第一个元素的地址。 p &array[0] array int array[10]; int *p; 因而可以有: p=array; 等价于 p=&array[0]; array[0]array[1]array[2]array[3]array[4]array[5]array[6]array[7]array[8]array[9]
242
array &array[0] &array[1] p+i? p+i*d
通过指针引用数组元素 C语言规定:如指针p指向数组的一个元素,则p+1指向数组的下一个元素。 array &array[0] p array[0]array[1]array[2]array[3]array[4]array[5]array[6]array[7]array[8]array[9] &array[1] p+i? p+1 p+i*d 如果有p=a;则p+i和a+i就是a[i]的地址。 *(p+i)或*(a+i)是p+i或a+i所指向的数组元素。
243
C语言允许指向数组的指针变量带下标,如p[i] 等价于*(p+i) 。
对于数组元素的引用:可以使用下标法,也可以用指针法。 例 输出数组全部元素。 指针变量 下标法 数组名 main() {int a[10]; int i; for (i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); printf("%d",a[i]); } main() {int a[10]; int i,*p; for (i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); for (p=a;p<(a+10);p++) printf("%d",*p); } main() {int a[10]; int i; for (i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); printf("%d",*(a+i)); } 注意:不能有 a++
244
p=a; 例 输出a数组的10个元素。 main() {int *p,i,a[10]; p=a; for (i=0;i<10;i++)
scanf("%d",p++); printf("\n"); for (i=0;i<10;i++,p++) printf("%d",*p); } p=a; 注意以下指针变量的运算:如有p=&a[i] 则: p *p++ 等价于*( p++) *(++p) (*p)++ *(p--) *(--p) 是如何运算?
245
数组名作函数参数 用数组名作函数的参数,是将实参数组的首地址传递给形参。 例 将数组a中n个整数按相反顺序存放。 main() {static int i,a[10]={3,7,9,11,0,6,7,5,4,2}; printf("The original array:\n"); for (i=0;i<10;i++) printf("%d",a[i]); printf("\n"); inv(a,10); printf("The array has been inverted:\n"); } void inv(x,n) int x[ ],n; {int t,i,j,m=(n-1)/2; for (i=0;i<=m;i++) {j=n-1-i; t=x[i];x[i]=x[j];x[j]=t; } return;
246
for (;i<=p;i++,j--) {t=*i;*i=*j;*j=t;} return; }
运行结果: The original array: 3, 7, 9, 11, 0, 6, 7, 5, 4, 2 The array has been inverted: 2, 4 , 5, 7, 6, 0, 11, 9, 7, 3, 将形参x定义为指针: a a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8]a[9] i,x 3 void inv(x,n) int *x,n; {int *p,*i,*j,t,m=(n-1)/2; i=x;j=x+n-1;p=x+m; for (;i<=p;i++,j--) {t=*i;*i=*j;*j=t;} return; } 7 9 p=x+m 11 6 7 5 4 j, x+n-1 2
247
如果有一个实参数组,则实参与形参的可以有以下4种情况:
形参和实参都用数组名。 实参用数组名,形参用指针变量。 实参和形参都用指针变量。 实参用指针变量,形参用数组名。 例 用选择法对10个整数排序(由大到小)。 sort(x,n) int x[ ],n; {int i,j,k,t; for (i=0;i<n-1;i++) {k=i; for (j=i+1;j<n;j++) if (x[j]>x[k]) k=j; if (k!=i) {t=x[i];x[i]=x[k];x[k]=t;} } } main() {int *p,i,a[10]; p=a; for (i=0;i<10;i++) scanf("%d",p++); p=a; sort(p,10); for (p=a,i=0;i<10;i++) {printf("%d",*p); p++;} }
248
例 用选择法对10个整数排序(由大到小)。 形参为指针变量 sort(x,n) int *x, n; { int i,j,k,t; for (i=0;i<n-1;i++) { k=i; for ( j=i+1; j<n; j++) if ( *(x+j) > *(x+k) ) k=j; if (k!=i) {t=*(x+i); *(x+i)=*(x+k); *(x+k)=t;} } main() {int *p,i,a[10]; p=a; for (i=0;i<10;i++) scanf("%d",p++); p=a; sort(p,10); for (p=a,i=0;i<10;i++) {printf("%d",*p); p++;} }
249
指向多维数组的指针和指针变量 二维数组的地址: 设有 static int a[3][4]={{1,2,3,4},{5,6,7,8},{10,11,12,13}}; 则: a a[0] a[1] a[2] a+1 a+2 a[0] 等价于 *(a+0) a[1] 等价于 *(a+1) a[i] 等价于 *(a+i) a[0]+1, *(a+0)+1是&a[0][1] a[1]+2, *(a+1)+2是&a[1][2] a[i]+3 , *(a+i)+3 是 &a[i][3]
250
以下表示形式的含义为什么: a a[0],*(a+0),*a a+1 a[1],*(a+1) a[1]+2,*(a+1)+2,&a[1][2] *(a[1]+2),*(*(a+1)+2),a[1][2] 辨析 a+i+j : 第(i+j)行首地址 *(a+i)+j: 第i行第j列地址, 地 址 注意一维数组和二维数组中a+i和*(a+i)的不同。 在一维数组a+i为地址,*(a+i)为值。 在二维数组a+i和*(a+i)都为地址。 在二维数组中: a+i=a[i]=*(a+i)=&a[i]=&[i][0] a+i: 第i行首地址 *(a+i): 第i行第0列地址,等价于*(a+i)+0:
251
例: 158, 158 166, 166 174, 174 9, 9 #define FORMAT "%d,%d\n" main()
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23}; printf(FORMAT,a,*a); printf(FORMAT,a[0],*(a+0)); printf(FORMAT,&a[0],&a[0][0]); printf(FORMAT,a[1],a+1); printf(FORMAT,&a[1][0],*(a+1)+0); printf(FORMAT,a[2],*(a+2)); printf(FORMAT,&a[2],a+2); printf(FORMAT,a[1][0],*(*(a+1)+0)); } 运行结果: 158, 158 166, 166 174, 174 9, 9
252
for(p=a[0]; p<a[0]+6; p++) {if((p-a[0])%3==0) printf("\n");
多维数组的指针 可以用指针变量指向多维数组及其元素。有两种方式: 指向数组元素的指针变量。 例 用指针变量输出二维数组的元素。 p main() { int a[2][3]={1,2,3,4,5,6}; int *p; for(p=a[0]; p<a[0]+6; p++) {if((p-a[0])%3==0) printf("\n"); printf("%3d", *p); } } a[0] 2006 2010 2012 2004 2008 2000 2002 2000 2002 2004 2006 2008 2010 1 2 3 4 5 6 a[1] 运行结果:
253
二维数组a[n][m]中,元素a[i][j]相对于a[0][0]的偏移量计算公式为: i*m+j
数组元素在数组中的相对位置(偏移量): 二维数组a[n][m]中,元素a[i][j]相对于a[0][0]的偏移量计算公式为: i*m+j m为二维数组的列数 因此,a[0][0]的地址加i*m+j就是元素a[i][j]的地址。 即: &a[0][0]+ i*m+j &a[i][j] 如果有如下定义: int a[3][4], *p; p = *a; 则元素 a[2][1]的地址为p+2*4+1 或者说 a[2][1]等价于 *(p+2*4+1)。
254
{static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,0,1,2}}; int i, j, *p=a;
例 定义一个指向数组元素的指针,将一个二维数组按行列的格式显示出。 main() {static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,0,1,2}}; int i, j, *p=a; for(i=0; i<3; i++) {for(j=0; j<4; j++) printf("%4d",*(p+i*4+j) ); printf("\n"); } 表达式p+i*4+j表示 第i行第j列元素的地址, 因此*(p+i*4+j)就是 元素a[i][j]。 运行结果:
255
指向由m个数组成的一维数组的指针变量。
指针定义形式如下: 类型标识符 (*标识符)[所指数组元素个数] 例如:int (*p)[4]; 表示变量p是指向有4个元素的一维整型数组的指针变量。 不能省略 例 输出二维数组任一个元素的值。 main() {static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11}; int (*p)[4],i,j; p=a; scanf("%d,%d",&i,&j); printf("\na[%d,%d]=%d\n",i,j,*(*(p+i)+j)); } p,a p+1 a[0] a[1] a[2] p+2 若输入:1,2↙ 则输出为:? 6
256
p = a; p是指向二维数组a中第i行的指针变量,则p+1是指向数组a中第i+1行的指针。即p的变化是以“行”为单位的。
如果p是指向二维数组a中第i行的指针变量,也就是说p所指向的内容是一维数组a[i],则*p a[i] &a[i][0]。p和*p的值相同(因为第i行的地址和元素a[i][0]的地址相等),但含义不同(即所指向的内容不同)。 如果有如下定义: int (*p)[4], a[3][4]; p = a; 则有以下表达式的等价关系: p+i a+i &a[i] 数组a中第i行的地址 *p a[0] &a[0][0] 数组a中第0行0列元素的地址 *(p+i) a[i] &a[i][0] ......数组a中第i行0列的地址 *(p+i)+j &a[i][j] 第i行第j列元素的地址 *p+i &a[0][i] ......第0行第i列元素的地址
257
{static int a[3][4]={{1,2,3,4},{2,3,4,5},{3,4,5,6}};
例 输出二维数组的元素。 main() {static int a[3][4]={{1,2,3,4},{2,3,4,5},{3,4,5,6}}; int i, j, (*p)[4]; for(i=0; i<3; i++) {p=a+i; for(j=0; j<4; j++) printf("a[%d][%d]=%-5d", i, j, *(*p+j)); printf("\n"); } p *p a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] a[0] a[1] a[2] 表示使p指向 a中的第i行 P *p *p表示第i行的首地址, *p+j表示第i行第j列元素的地址, *(*p+j)即为a[i][j]。 p *p 运行结果: a[0][0]=1 a[0][1]=2 a[0][2]=3 a[0][3]=4 a[1][0]=2 a[1][1]=3 a[1][2]=4 a[1][3]=5 a[2][0]=3 a[2][1]=4 a[2][2]=5 a[2][3]=6
258
如果有如下定义: int a[3][4] ,*p, (*pa)[4]; p = a[0]; pa = a; 则下列表达式的含义是什么? a,*a,**a,a[2],a+2,*a+2; p,p++,p+2,*(p+2),*p+2,p+1*4+2,*(p+2*4); pa,pa++,pa+2,*pa,*pa+2,*(pa+2), *(*pa+2),*(*(pa+1)+2)。 通过指针变量存取数组元素速度快,且程序简明。用指针变量作形参,可以允许数组的行数不同。因此数组与指针紧密联系。
259
四、字符串的指针和指向字符串的指针变量 字符串的表示形式 在C语言程序中,可以用字符数组或字符指针两种方法实现一个字符串。
在C语言程序中,可以用字符数组或字符指针两种方法实现一个字符串。 实际上在字符指针用法中,C语言在内存开辟了一个字符数组用来存放字符串常量,并把字符串首地址赋给字符指针变量p。即指针变量p中存放的是一个指向字符串“I love China!”的地址,而不是字符串“I love China!”。 字符指针法 字符数组法 main() {char *p="I love China!"; printf("%s\n",p); } main() {static char a[ ]="I love China!"; printf("%s\n",a); } a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] a[10] a[11]a[12]a[13] a I l o v e C h i n a ! \0 2000 I l o v e C h i n a ! \0 p
260
注意: char *p= " I love China ";
main() {static char a[ ]="I am a boy.",b[20]; int i; for (i=0;*(a+i)!='\0';i++) *(b+i)=*(a+i); *(b+i)='\0'; printf("string a is:%s\n",a); printf("string b is:"); for (i=0;b[i]!='\0';i++) printf("%c",b[i]); } 对于字符串中字符的存取,可以用下标法、地址法和指针法。 例 将字符串a复制到字符串b中。 地址法 结果: string a is: I am a boy. string b is: I am a boy. 下标法
261
b a main() {char a[ ]="I am a boy.",b[20],*p1,*p2; int i; p1=a;p2=b;
for (;*p1!='\0';p1++,p2++) *p2=*p1; *p2='\0'; printf("string a is:%s\n",a); printf("string b is:"); for (i=0;b[i]!='\0';i++) printf("%c",b[i]); } b a p1 p2 a[0]a[1]a[2]a[3]a[4]a[5]a[6]a[7]a[8]a[9]a[10a[11 p1 b[0]b[1]b[2]b[3]b[4]b[5]b[6]b[7]b[8]b[9]b[10b[11 I I a m b o y . \0 p2 指针法 p1 p2 a p1 p2 m p1 p2 p1 p2 a p1 p2 p1 p2 b p1 p2 o p1 p2 y p2 p1 . 结果: string a is: I am a boy. string b is: I am a boy. p2 p1 \0
262
字符数组和字符指针变量都能实现字符串的存储和运算,但应注意二者之间的区别。
字符指针变量与字符数组 字符数组和字符指针变量都能实现字符串的存储和运算,但应注意二者之间的区别。 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址。 赋初值方式: 数组:static char stir[]={" I love China! "}; 字符指针变量:char *p= " I love China! "; 赋值方式:字符指针变量可以,而数组不行。 如有:char str[14],*p; 则: p="I love China! "; 可以。 而: str="I love China! "; 不行。
263
数组在编译时分配若干内存单元,而指针变量只分配一个存放地址的单元。若未给指针变量赋一个地址,则它并没有具体指向那一个字符数据。
使用下面方法就可能产生严重的错误。 char *p; scanf("%s",p); P指向一个不定的地址 指针变量的值可以改变,而数组名代表的值不可改变。 如:p++可以 而: a++不行 (p为指针,a为数组名)。 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。例如: char *p; p= "a=%d,b=%f\n" ; printf(p,a,b); 等价于 printf("a=%d,b=%f\n",a,b);
264
字符串指针作函数参数 将一个字符串从一个函数传递到另一个函数,可以用字符数组名作参数,也可以用指向字符串的指针变量作参数。 归纳起来,有以下4种情况: 实参 形参 数组名 数组名 数组名 字符指针变量 字符指针变量 字符指针变量 字符指针变量 数组名 例12.24用函数调用实现字符串的复制。
265
方法一:(形参为数组) void str_copy(s1, s2) char s1[],char s2[] {int i=0; while(s2[i]!='\0') {s1[i]=s2[i];i++;} s1[i]= '\0'; } 方法二:(形参为指针) void str_copy(p1, p2) char *p1,char *p2 {while(*p2!= '\0' ) {*p1=*p2;p1++;p2++;} *p1='\0'; } main() /*实参为数组名*/ {static char a[]= " I am a teacher. "; static char b[]= " you are a student. "; printf(" string_a=%s\n string_b=%s\n ",a,b); str_copy(a,b); /*实参为数组名*/ printf(" \nstring_a=%s\nstring_b=%s\n ",a,b); }
266
char *pa= " I am a teacher. "; char *pb= " you are a student. ";
main() /*实参为字符指针变量*/ { char *pa= " I am a teacher. "; char *pb= " you are a student. "; printf(" string_a=%s\n string_b=%s\n ",pa,pb); str_copy(pa,pb); /*实参为字符指针变量*/ printf(" \nstring_a=%s\nstring_b=%s\n ",pa,pb); } 程序运行结果为: string_a=I am a teacher. string_b=you are a student.
267
五、函数的指针和指向函数的指针变量 一个函数在编译时被分配给一个入口地址,这个入口地址就称为函数的指针。可以定义一个指针变量指向函数,该指针称为指向函数的指针变量。其定义形式如下: 数据类型标识符 (*指针变量名)( ) main 函数返回值的类型 1000 例如:int (*p)( ); char (*p1)( ); p=max; 1900 max max p 定义p和p1为指向函数的指针变量。p指向一个带整型返回值的函数,p1指向一个带字符型返回值的函数。 min 2000
268
注意:对函数指针 变量p进行p++,p+n, p--等运算无意义.
用函数指针变量调用函数 例 求a和b中的大者。 用函数指针变量调用函数 main() {int max(); int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("a=%d,b=%d,max=%d",a,b,c); } max(x,y) int x,y; {int z; if (x>y) z=x; else z=y; return(z);} main() {int max(); int a,b,c,(*p)( ); p=max; scanf("%d,%d",&a,&b); c=(*p)(a,b); printf("a=%d,b=%d,max=%d",a,b,c); } 一般方法: 注意:对函数指针 变量p进行p++,p+n, p--等运算无意义.
269
把指向函数的指针变量作函数参数 例如: sub(x1,x2) int (*x1)(),(*x2)(); {int a,b,i,j; a=(*x1)(i); b=(*x2)(i,j); } sub(f1,f2); 则可有 (f1和f2为函数名) 例 设一个函数process, 在调用它时,每次实现不同的功能。输入两个数a和b,第一次调用process找出其中大者,第二次调用找出其中小者,第三次调用求两者之和。
270
max(x,y) int x,y; {int z; if(x>y) z=x; else z=y; return(z);}
main() {int max(), min(),add(); int a,b: printf(" enter a and b:"); scanf(" %d,%d",&a,&b); printf(" max="); process(a,b,max) printf(" min="); process(a,b,min) printf(" sum="); process(a,b,add) } max(x,y) int x,y; {int z; if(x>y) z=x; else z=y; return(z);} min(x,y) int x,y; {int z; if(x<y) z=x; else z=y; return(z);}
271
printf(" %d\n",result);} add(x,y) int x,y; {int z; z=x+y; return(z);}
process(x,y,fun) int x,y; int (*fun) ( ); {int result; result=(*fun)(x,y); printf(" %d\n",result);} add(x,y) int x,y; {int z; z=x+y; return(z);} 运行情况: enter a and b: 2,6 max=6 min=2 sum=8
272
六、 返回指针值的函数 一般定义形式为: 类型标识符 *函数名(参数表) 例如: int *a(x,y);
类型标识符 *函数名(参数表) 例如: int *a(x,y); 例12.26 有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。
273
pt pointer main() float *p; int i,m; scanf("%d",&m);
{static float score[ ][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}}; float *search(); float *p; int i,m; scanf("%d",&m); p=search(score,m); for (i=0;i<4;i++) printf("%5.2f\t",*(p+i)); } float *search(pointer,n) float (*pointer)[4]; int n; {float *pt; pt=*(pointer+n); return(pt);} pt pointer 运行情况: 1
274
七、 指针数组和指向指针的指针变量 char *name[5]; int a=12, b=20, *pa ; pa=&a; p[0]=pa;
指针数组的概念 一个数组,其元素均为指针类型数据,称为指针数组。 定义形式为: 类型标识 *数组名[数组长度说明] 例如: int *p[4]; char *name[5]; int a=12, b=20, *pa ; pa=&a; p[0]=pa; p[1]=&b; name[0] name[1] name[2] name[3] name[4] Follow me BASIC Great Wall FORTRAN Computer design
275
指向指针的指针(指向指针变量的指针变量) 定义形式为: 类型标识 **标识符 例如:char **p; *(*p)
定义形式为: 类型标识 **标识符 例如:char **p; *(*p) char *name[5]; p=name+2; 即 name name[0] name[1] name[2] name[3] name[4] Follow me BASIC Great Wall FORTRAN Computer design p name+2 printf("%o\n",*p); printf("%s\n",*p); 8进制表示的Great Wall的地址 Great Wall 结果为?
276
例 用指向指针类型的指针变量输出一个变量 main() { int a=15, **p; int *pp=&a; p=&pp;
例 用指向指针类型的指针变量输出一个变量 main() { int a=15, **p; int *pp=&a; p=&pp; printf("%d\n",**p); } pp p *pp 、**p、a &a 15
277
例 {static char *name[ ]={"Follow me", "BASIC", "Great Wall",
main() {static char *name[ ]={"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"} char **p; int i; for (i=0;i<5;i++) {p=name+i; printf(%s\n,*p);} } Follow me BASIC Great Wall FORTRAN Computer design name name[0] name[1] name[2] name[3] name[4] Follow me BASIC Great Wall FORTRAN Computer design p name+4 name+3 name+0 name+1 name+2
278
是定义的类型名。struct student num,name,sex,age,score,addr不同
第七章 结构体与共用体 一、结构体 结构体是将不同的数据类型组织在一起而形成的一个有机的整体,C语言提供的结构体类型就是这样一个整体。 num name sex age score addr 例如: Li Fun F Xi’an struct student {int num; char name[20]; char sex; int age; float score; char addr[30]; }; struct是关键字,student 是定义的类型名。struct student 是定义一个结构体类型,它包括了 num,name,sex,age,score,addr不同 类型的数据项。
279
定义结构体类型的一般形式: struct 结构体名 {成员1; 每个成员的定义格式: 类型标识符 成员名; 成员n; }; 例如
{成员1; 成员n; }; 每个成员的定义格式: 类型标识符 成员名; 例如 struct student { int num; char name[20]; char sex; int age; float score; char addr[30]; }; 定义一个结构体类型 struct student
280
二、定义结构体类型变量的方法 先定义结构体类型再定义变量。 有三种定义方法: 在定义类型的同时定义变量。 直接定义结构类型变量。
先定义结构体类型再定义变量。 有三种定义方法: 在定义类型的同时定义变量。 直接定义结构类型变量。 先定义结构体类型再定义变量 例如上面定了一个结构体类型struct student,可以用它来定义变量。如: struct student s1,s2; s1和s2为struct student类型变量,即它们具有如下的结构: num name sex age score addr s1 s2
281
在定义类型的同时定义变量 定义的一般形式: struct 结构体名 {成员1; 成员n; }变量名表列; 例如:
{成员1; 成员n; }变量名表列; 例如: struct student {int num; char name[20]; char sex; int age; float score; char addr[30]; }s1,s2; 定义了两个具有如下结构的变量s1和s2。 num name sex age score addr s1 s2
282
直接定义结构类型变量 struct {int num; char name[20]; char sex; int age; float score; char addr[30]; }s1,s2; 定义一般形式: struct {成员1; 成员n; }变量名表列; 例如 注意:先定义类型,后定义变量。只能对变量赋值、存取或 运算,而不能对一个类型赋值、存取或运算。 成员名可以与程序中的变量名相同,二者代表不同的 对象。
283
struct staff { unsigned int number; struct date char name[20];
成员也可以是一个结构体变量,即结构可以嵌套。 例如: struct staff { unsigned int number; char name[20]; char sex; struct date birthday; char address[30]; }s1,s2; struct date { int year; int mouth; int day; }; number name sex address birthday year mouth day
284
s1.number s1.name s1 .sex s1.birthday.year
三、结构体类型变量的引用 C语言规定:可以对最低一级的成员进行引用。当两个结构变量的类型相同时,可以互相赋值。 对成员引用的一般格式: 结构体变量名.成员名 互相赋值: s2=s1; s1.number s1.name s1 .sex s1.birthday.year s1.birthday.month s1.birthday.day s1.address 例如: 成员(分量)运算符 s2.number=s1.number+1 ; scanf("%d",&s1.number);
285
四、结构体类型变量的初始化 在定义结构变量的同时给其赋初值。和数组一样,只有结构体变量为全局变量和静态变量时,才能进行。 如:
struct student {int num; char name[20]; char sex; int age; float score; char addr[31]; }; struct student s1={10010,"Li ming", 'F',18,87.5, "Xi’an"};
286
注意:①对外部存储类型的结构体变量进行初始化。
结果: No.: 89031 Name: Li Lin Sex: M Address: Xi’ an 例如: struct student { long int num; char name[20]; char sex; char addr[20]; }a={89031,"Li Lin", 'M', "Xi’ an"}; main() {printf ("No.: %ld\n Name: %s\n Sex: %c\n Address:%s\n " a.num, a.name, a.sex, a.addr); }
287
②对静态存储类型的结构体变量进行初始化。
例如: 结果: No.: 89031 Name: Li Lin Sex: M Address: Xi’ an main() { static struct student { long int num; char name[20]; char sex; char addr[20]; }a={89031,"Li Lin", 'M', "Xi’ an"}; printf ("No.: %ld\n Name: %s\n Sex: %c\n Address:%s\n " a.num, a.name, a.sex, a.addr); } ③对自动存储类型的结构体变量不能进行初始化。只能在函数执行时用赋值语句对各个成员分别赋值。
288
例 struct teacher { long int number; char name[20]; char sex;
char profession[16]; char addr[31]; }; 例 main() {struct teacher t1,t2,t3; t1.number=2001; strcpy(t1.name, "Wang wei"); t1.sex='M'; strcpy(t1.profession,"docent"); strcpy(t1.addr,"Xi’an"); printf("No:%ld, name:%s, sex:%c,profession:%s,addr:%s\n", t1.number, t1.name, t1.sex, t1.profession, t1.addr); t2=t1; t3=t2; } 运行结果: No: 2001, name: Wang wei, sex: M,profession: docent,addr: Xi’an
289
五、结构体数组 一个数组的各个元素都是一个结构类型数据,这样的数组就是结构数组。 struct student 结构体数组的定义:
一个数组的各个元素都是一个结构类型数据,这样的数组就是结构数组。 struct student { int num; char name[20]; char sex; int age; float score; char addr[31]; }; struct student s1[5]; 结构体数组的定义: 结构体数组的初始化: 在定义结构数组的同时,给其赋初值。
290
在定义student时,元素个数可以 不指定即可以写成student[ ]。
如: struct pupil {int num; char name[20]; char sex; int age; }student[3]= { {10101,"Li Ling", 'M',18}, {10102, "Zhang Fun", 'M',19} , {10104, "Wang Min", 'F',20} }; 在定义student时,元素个数可以 不指定即可以写成student[ ]。 例13.3 通过结构体数组元素引用结构体成员
291
struct teacher {int number; char name[20]; char sex; char profession[16]; char addr[31]; }; main() {int i; struct teacher t[3]; for(i=0;i<3;i++) scanf("%d,%s,%c,%s,%s",&t[i].number, t[i].name,&t[i].sex, t[i].profession,t[i].addr); printf("No:%ld, name:%s, sex:%c,profession:%s,addr:%s\n", t[i].number, t[i].name, t[i].sex, t[i].profession, t[i].addr); }
292
struct student stu_1,*p; p=&stu_1;
六、 指向结构体类型数据的指针变量 一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量。 指向结构体变量的指针变量 #include "string.h" main() { struct student {long int num; char name[20]; char sex; float score; }; struct student stu_1,*p; p=&stu_1; p stu_1
293
strcpy(stu_1.name,"Li Lin"); stu_1.sex='M'; stu_1.score=89.5;
stu_1.num=89101; strcpy(stu_1.name,"Li Lin"); stu_1.sex='M'; stu_1.score=89.5; printf("No.:%ld\nname:%s\nsex:%c\nscore:%f\n" stu_1.num,stu_1.name,stu_1.sex,stu_1.score); printf("\nNo.:%ld\nname:%s\nsex:%c\nscore:%f\n" (*p).num, (*p).name, (*p).sex, (*p).score); 运行结果: No.: No.: 89101 name: Li Lin name: Li Lin sex: M sex: M score: score: 89.5 89101 Li Lin M 89.5 p stu_1
294
①结构体变量.成员名; ② (*P).成员名; ③ p-> 成员名;
C语言中还可以通过 “->”引用成员。 (指向结构体成员运算符) 如: p->name (*p).name p->sex (*p).sex p->score (*p).score 等价于 ①结构体变量.成员名; ② (*P).成员名; ③ p-> 成员名; 注意:“->”的优先级与“( )”、“[ ]”和句点“.”相同。 因而 ++p->num 等价于++(p->num) (++p)->num 先对p加1,再引用num p++->num 等价于(p++) ->num
295
则:*q->y *(q->y)表示y所指的内容; *q->y++ *((( q->y ) ++) )
再看一种结构成员为指针的情况: struct lpoint { int x; int *y; } stupoint, *q; q=&stupoint; 则:*q->y *(q->y)表示y所指的内容; *q->y++ *((( q->y ) ++) ) 访问y所指的内容后y自增; (*q->y)++ (*(q->y) ) ++ 自增y所指的内容; *q++->y *((q++)->y) 访问y所指的内容后q自增。
296
stu 指向结构体数组的指针 p struct student { int num; char name[20]; char sex;
int age; }; struct student stu[3]={{10101,"Li Lin", 'M',18}, {10102, "Zhang Fun", 'M',19}, {10104, "Wang Min", 'F',20}}; main() {struct student *p; printf(" No Name sex age\n"); for(p=stu; p<stu+3;p++) printf("%5d%-20s%2c%4d\n",p->num, p->name, p->sex, p->age); } 10101 Li Lin M 18 10102 Zhang Fun 19 10104 Wang Min F 20 p p
297
运行结果: No. Name sex age Li Lin M 18 Zhang Fun M 19 10104 Wang Min F 20
注:不能将一个结构体成员的地址赋给一个指向结构体的指针变量。如:p=&stu.name; 如果p的初值为stu,即指向第一个元素,则p+1后指向下一个元素的起始地址。如: (++p)->num (p++)->num
298
struct student { int num; char name[20]; float score[3]; };
用指向结构体的指针作函数参数 例 有一个结构体变量stu,内含学生学号、姓名和三门课的成绩。要求在main函数中赋以值,在另一函数print中将它们打印输出。 #include "string.h" #define format "%d\n%s\n%f\n%f\n%f\n" struct student { int num; char name[20]; float score[3]; }; num name score[0] score[1] score[2]
299
main() {void print(); struct student stu; stu.num=12345; strcpy(stu.name, "Li Li"); stu.score[0]=67.5; stu.score[1]=89; stu.score[2]=78.6; print(&stu); } p stu num name score[0] score[1] score[2] void print(p) struct student *p; {printf(format, p->num,p->name,p->score[0], p->score[1],p->score[2]); printf("\n"); }
300
七、 共用体 将几个不同的变量共占同一段内存的结构,称为“共用 体”。其定义形式如下: union 共用体名 {成员1; 成员n;
将几个不同的变量共占同一段内存的结构,称为“共用 体”。其定义形式如下: union 共用体名 {成员1; 成员n; }变量名表列; ch,i,f i ch f a 例如: union data {int i; char ch; float f; }a,b,c;
301
使用共用体应注意以下几点: 共用体成员的引用格式与结构体成员的引用一样。 同一个内存段可以用来存放几种不同类型的成员,但在每一瞬间时只能存放其中一种,而不是同时存放几种。 共用体变量中起作用的成员是最后一次存入的成员,在存放一个新的成员后原来的成员就失去作用。 不能对共用体变量名赋值,也不能企图引用变量名来得到成员的值。 不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针。 共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。
302
举例: main() {union {int x; int y; }z; z.x=10; z.y=20; printf("z.x=%d\n",z.x);} 运行结果为:z.x=20 将一个四位数(十六进制数)分解成两部分输出。 main() {union {int x; struct {char low; char high; } b; }a={0x3366}; printf("a.x=%x\n",a.x); printf("a.b.low=%xa.b.high=%x\n",a.b.low,a.b.high); } 运行结果为:a.x=3366 a.b.low=66 a.b.high=33
303
第八章 文件概述 一、 文件概述: 二进制文件和文本文件:
第八章 文件概述 一、 文件概述: 二进制文件和文本文件: 文件在磁盘上总是以二进制形式存储的。但是数据在文件中的表现形式不同。以整型数127为例: 二进制文件是将内存中的数据按其在内存中的存储形式原样保存在文件中。因此整型数127在二进制文件中用2个字节保存为: 文本文件是将数据按字符形式存放在文件中。因此整型数127在文本文件中用3个字节存放: (整型数127) (字符‘1’) (字符‘2’) (字符‘7’)
304
二、 文件类型指针: 在过去使用的C版本(UNIX下的C)有两种对文件的处理方法:缓冲文件系统;非缓冲文件系统。
ANSI C标准规定只采用缓冲文件系统。 每一个被使用的文件都在内存中开辟一个区,用来存放文件的有关信息(文件名、文件状态以及文件的当前位置等 ) 。这些信息保存在一个结构体类型的变量中。
305
int -cleft; /*缓冲区中剩下的字符*/ int -mode; /*文件操作模式*/
FILE由系统定义。形式: typedef struct { int -fd; /*文件号*/ int -cleft; /*缓冲区中剩下的字符*/ int -mode; /*文件操作模式*/ char *-nextc; /*下一个字符位置*/ char *-buff; /*文件缓冲区位置*/ }FILE; 文件指针的定义形式为: FILE *标识符,*标识符, ,*标识符; 例如:FILE *fp; fp是一个指向FILE类型结构体的指针变量。可以指向某一个文件的结构体变量,从而通过该结构变量中的文件信息能够访问该文件。
306
三、文件的打开与关闭: 对文件作任何读写操作之前必须“打开”该文件。在程序读写操作结束后必须“关闭”该文件。 文件的打开(fopen函数)
对文件作任何读写操作之前必须“打开”该文件。在程序读写操作结束后必须“关闭”该文件。 文件的打开(fopen函数) C语言用fopen()函数来实现打开文件。用fopen函数打开一个指定文件后,返回一个指向该文件的指针,在程序中通过这个指针来实现对文件的读写操作
307
fopen函数的调用形式为: FILE *fp; fp=fopen(文件名,使用文件方式); 如果函数调用成功,fopen函数的返回值是指向该文件的指针,程序可以使用这个指针对所打开的文件进行读写操作。否则返回一个空指针—NULL; 使用文件方式是一个字符串,系统规定了打开文件的几种模式,并用指定字符串表示。 例如: fp=fopen("A1", "r") 只读方式 fp指向A1文件
308
文件使用方式: 文件使用方式 含义 “r” 只读 为输入打开一个文本文件进行读操作 “w” 只写 为输出打开一个文本文件进行写操作
文件使用方式 含义 “r” 只读 为输入打开一个文本文件进行读操作 “w” 只写 为输出打开一个文本文件进行写操作 “a” 追加 向文本文件尾追加数据 “rb” 只读 为输入打开一个二进制文件进行读操作 “wb” 只写 为输出打开一个二进制文件进行写操作 “ab” 追加 向二进制文件尾追加数据 “r+” 读写 为读/写打开一个文本文件 “w+” 读写 为读/写建立一个新的文本文件 “a+” 读写 同”r+” “rb+” 读写 为读/写打开打开一个二进制文件 “wb+” 读写 为读/写建立一个新的二进制文件,若文件不存在则创建 “ab+” 读写 同”rb+”
309
fclose函数用来关闭fp所指向的文件。该文件必须是用fopen函数打开的。如果关闭成功则返回1,否则返回0。
例如: main() {FILE * fp; fp = fopen("d:\tc\lx5_6.c", "r"); if (fp == NULL) {printf("cannot open this file!\n"); exit(1); } fclose(fp); 如果打开文件失败,则退出程序 进行读写操作 关闭文件lx5_6.c
310
四、文件的读写 fputc和fgetc函数: fputc函数将一个字符写入文件的当前位置。 其一般调用形式为:
fputc(ch, fp); 其中ch是要输出的字符,它可以是一个字符常量,也可以是一个字符变量。fp是文件指针变量,它从fopen函数得到返回值。如果函数调用成功则返回ch的值,否则返回EOF。 fgetc函数从文件中读取当前位置的一个字符返回。 其一般形式为: ch=fgetc(fp); 字符变量 文件型指针变量
311
C语言中使用feof函数来判断文件是否结束。如果是文件结束,函数feof(fp)的值为1(真),否则为0(假)。
#include “stdio.h” main() { FILE *fp; int ch; /*也可以 char ch;*/ if ((fp=fopen(“d:\\my.dat”,”r”))= =NULL) { printf(“\n ths file does not open \n”); exit(1);} ch=fgetc(fp); while(!feof(fp)) { putchar(ch); ch=fgetc(fp);} fclose(fp); }
312
例 打开一个ASCII文件,将文件内容显示到显示器上。然后输入一行字符串,将其保存到该文件中。
#include "stdio.h" main() {FILE *fp; char c,str[100], filename[30],i=0; scanf("%s",filename); if( (fp=fopen(fliename, "r+")) == NULL) {printf("file can’t open!\n"); exit(1); }; while((c=fgetc(fp))!=EOF) putchar(c); gets(str); while(str[i]!='\0') {fputc(str[i], fp); i++;} fclose(fp); }
313
fread(buffer, size, count, fp);
fread函数和fwrite函数: fread函数 其一般调用形式为: fread(buffer, size, count, fp); 表示从fp所指向的文件的当前位置读入count个大小为size字节的数据块放入buffer所指向的内存区域。 fwrite函数 其一般调用形式为: fwrite(buffer, size, count, fp); 表示将从buffer开始的count个大小为size字节的数据块写入fp所指向的文件的当前位置。 buffer:是一个指针。(指向内存缓冲区) size: 要读写的字节数。(int) count:要进行读写多少个size字节的数据项。(int) fp: 文件型指针。
314
fscanf和fprintf函数: 其一般调用形式为: fscanf(文件指针, 格式字符串, 变量地址列表); 表示从文件的当前位置格式化输入数据。对于格式字符串和变量地址列表的说明与scanf函数相同。 例如:fscanf(fp, "%d,%f",&i,&t); 从磁盘文件上读入两个字符分别给变量i和t。 fprintf(文件指针, 格式字符串, 输出表达式列表); 将数据格式化输出到文件中。对于格式字符串和输出表达式列表的说明与printf函数相同。 例如:fprintf(fp, "%d,%6.2f",i,t); 将变量i和t的值按%d和%6.2f的格式输出到fp指向的文件上。
315
#include "stdio.h" main() {FILE *fp; int k; char str[80]; scanf("%s%d",str,&k); if( (fp=fopen("testfile", "w")) == NULL) {printf("The file can’t be opened!\n"); exit(1); }; fprintf(fp,"%s %d",str,k); fclose(fp); if( (fp=fopen("testfile", ”r")) == NULL) {printf("The input file can’t be opened!\n"); fscanf(fp,"%s %d",str,&k); printf("%s,%d",str,k); fclose(fp);} 例
316
#define N 20 #include "stdio.h" main() { FILE *fp; float y,ystar,yend,ystep; char name[N]; printf("please input file name:\n"); scanf("%s",name); printf("*********************************\n"); printf("##########ystar,yend,ystep#######\n"); scanf("%f,%f,%f",&ystar,¥d,&ystep); if( (fp=fopen(name,"a+")) == NULL) {printf("This file can't be opened!\n"); exit(0); }; for (y=ystar;y<=yend;y+=ystep) fprintf(fp,"%4.2f\n",y); fclose(fp); } X-ray X坐标
317
printf("enter the infile name:\n");
scanf("%s",infile); if ((in=fopen(infile,"r"))==NULL) {printf("can not open infile \n"); exit(1); } printf("enter the outfile name:\n"); scanf("%s",outfile); if ((out=fopen(outfile,"a+"))==NULL) {printf("can not open outfile \n"); exit(2); } while(!feof(in)) { ch=fgetc(in); if (ch==' ') { fscanf(in,"%f",&y); printf("%f\n",y); fprintf(out,"%f\n",y);} } fclose(in); fclose(out); XPS Y轴数据 #include "stdio.h" #include "stdlib.h" #define N 20 main() { FILE *out; FILE *in ; char infile[N]; Char outfile[N], ch; float y;
318
# include "stdio.h" # include "stdlib.h" main() { FILE *out; FILE *in ; char infile[10],outfile[10],ch,ch2; printf("enter the infile name:\n"); scanf("%s",infile); printf("enter the outfile name:\n"); scanf("%s",outfile); if ((in=fopen(infile,"r"))==NULL) { printf("can not open infile \n"); exit(1); }
319
if ((out=fopen(outfile,"w"))==NULL)
{ printf("can not open outfile \n"); exit(2); } while(!feof(in)) { ch=fgetc(in); if (ch==' ') { while(1) { ch2=fgetc(in); putchar(ch2); fputc(ch2,out); if (ch2=='\n') break; } } fclose(in); fclose(out);
320
#include "stdio.h" #include "math.h" float f2(x) float x; { float y; y=x*x-2*x+1; return (y); } main() { float x,y1,y2,y3; printf("Please input variable x:"); scanf("%f",&x); y1=f2(x) ; printf("x=%f,y1=%f\n",x,y1); y2=f2((x+1)) ; printf("x=%f,x+1=%f,y2=%f\n",x,(x+1),y2); y3=f2((cos(x))) ; printf("x=%f,cosx=%f,y3=%f\n",x,cos(x),y3);
321
#include "stdio.h" main() { int a; int fun(); printf("\nEnter a integer number:"); scanf("%d",&a); if(fun(a)) printf("\n%d is prime",a); else printf("\n%d is not prime",a); } fun(int m) { int x,y; for(x=2;x<=m/2;x++) if(m%x==0) {y=0; break;} else y=1; return (y) ; }
322
第九章 编译预处理 一、编译预处理 C语言提供三种预处理功能: 宏定义; 文件包含; 条件编译。
第九章 编译预处理 一、编译预处理 C语言提供三种预处理功能: 宏定义; 文件包含; 条件编译。 分别用宏定义命令、文件包含命令和条件编译命令来实现。这些命令以符号“#”开头,结尾没有分号(;)。 该命令的作用域是从定义的地方开始到源文件结束。 在C编译系统对程序进行通常的编译之前,先对程序中这些命令进行“预处理”。然后将结果和源程序一起再进行编译处理,最后得到目标代码。
323
1、宏定义 不带参数的宏定义 功能:用一个指定的标识符(宏名)来代表一个字符串。 一般形式: #define 标识符 任意字符串序列
功能:用一个指定的标识符(宏名)来代表一个字符串。 一般形式: #define 标识符 任意字符串序列 例如: #define PI main() {float l,s,r,v; printf("input radius:"); scanf(%f,&r); l=2.0*PI*r; s=PI*r*r; v=4.0/3*PI*r*r*r; printf("l=%f\ns=%f\nv=%f\n",l,s,v); } main() {float l,s,r,v; printf("input radius:"); scanf(%f,&r); l=2.0* *r; s= *r*r; v=4.0/3* *r*r*r; printf("l=%f\ns=%f\nv=%f\n",l,s,v); } 预处理
324
宏定义是用宏名代替一个字符串,也就是作简单的置换,不作语法检查。如:
说明: 宏定义是用宏名代替一个字符串,也就是作简单的置换,不作语法检查。如: #define PI p; area=PI*r; 预处理后为 area= p;*r 宏名的有效范围为定义命令之后到本源文件结束。可以用#undef命令终止宏定义的作用。如: #define G 9.8 main() { } #undef G f1() G有效范围
325
在进行宏定义时,被定义过的宏名可被重新定义,也可以引用已定义的宏名。如:
在进行宏定义时,被定义过的宏名可被重新定义,也可以引用已定义的宏名。如: #define PI 5.6 #define R 3.0 #define PI #define L 2*PI*R #define S PI*R*R main() {printf("L=%f\nS=%f\n",L,S); } 用双引号括起来的字符串内 的字符,即使与宏名相同, 也不进行置换。如L,S 预处理 main() { printf("L=%f\nS=%f\n",2* *3.0, *3.0*3.0); }
326
定义形式为:#define 宏名(参数表) 字符串
带参数的宏定义 进行字符串替换,还要进行参数替换。 定义形式为:#define 宏名(参数表) 字符串 包含括弧中指定的参数 如: #define S(a,b) a*b area=S(3,2); area=3*2 注意:不能写成 #define S (a,b) a*b 不能有空格
327
#define S(r) PI*(r)*(r)
main() {float a,area; a=3.6; area= *a*a; printf("r=%f\narea=%\n",a,area); } 例: #define PI #define S(r) PI*r*r main() {float a,area; a=3.6; area=S(a); printf("r=%f\narea=%\n",a,area); } 预处理 预处理后 结果为? 如果有以下语句, area=S(x+y); area= *x+y*x+y; #define S(r) PI*(r)*(r) area= *(x+y)*(x+y);
328
B B A A 2、“文件包含”处理 文件包含处理是指一个源文件可以将另一个源文件的全部内容包含进来。
文件包含处理是指一个源文件可以将另一个源文件的全部内容包含进来。 一般形式为:#include "文件名" file1.c file1.c file2.c B B #include "file2.c " 预处理 A A
329
文件包含是可以嵌套的: file1.c file2.c file3.h #include "file2.c "
#include "file3.h " 也可按如下方式: file1.c file2.c file3.h #include "file3.h " #include "file2.c " file3中的全局变量,file2、 file1可直接使用,不必用 extern说明。 另:一个#include只能包含 一个文件。 file3.h file2.c file1.c 编译后 文件1为
330
3、条件编译 条件编译是指让编译程序根据条件有选择地对源程序的一部分进行编译。 C语言提供三组条件编译命令,其形式如下:
条件编译是指让编译程序根据条件有选择地对源程序的一部分进行编译。 C语言提供三组条件编译命令,其形式如下: #ifdef 标识符 程序段1 #else 程序段2 #endif 若标识符存在编译 程序段1,否则编译 程序段2 #ifndef 标识符 程序段1 #else 程序段2 #endif 若标识符不存在编译 程序段1,否则编译 程序段2
331
{char str[20]="C Language",c; int i=0; while ((c=str[i])!='\0') {i++;
#if 表达式 程序段1 #else 程序段2 #endif 若表达式为真编译 程序段1,否则编译 程序段2 #define LETTER 1 main() {char str[20]="C Language", c; int i=0; while ((c=str[i])!='\0') {i++; #if LETTER if (c>='a'&&c<='z') c=c-32; #else if (c>='A'&&c<='Z') c=c+32 #endif printf("%c",c); }} 例 main() {char str[20]="C Language",c; int i=0; while ((c=str[i])!='\0') {i++; if (c>='a'&&c<='z') c=c-32; printf("%c",c); } 编译后
332
if (n<0) printf("n<0,error!"); else if(n==0) f=1;
#include "stdio.h" # define M 15 float fac(n) int n; { float f=0; if (n<0) printf("n<0,error!"); else if(n==0) f=1; else f=fac(n-1)*n; return(f); }
333
printf("Output Yanghui triangle:\n"); for(n=0;n<=M;n++)
main() { int n,i; float sum; printf("Output Yanghui triangle:\n"); for(n=0;n<=M;n++) { for(i=0;i<=n;i++) { sum=fac(n)/(fac(i)*fac(n-i)); printf("%5.0f",sum); } printf("\n");
334
考试题型 一、单选题 (每小题2分,共20分 ) 下列字符中,ASCⅡ码值最大的是( )。 A)A B)b C)M D) n
的八进制数为( )。 A) 24.54 B) 22.68 C) 23.83 D) 21.22 二、判断题(每小题2分,共20分) +=运算符的结合性是从右向左的 。 int x,y; x=y;后y中的值将放入x中,y中的值将丢失。 三、程序填空题(每小题5分,共20分) 以下程序将1--12间能被4整除的数输出 void main() { int ( ); for (;n<=12;n++); { if ( ) continue; printf("%5d",n); } } n=1 n%4!=0
335
四、问答题 (每小题5分,共10分)(无) 1、计算机系统的组成? 五、阅读程序写答案 (每小题5分,共20分) main() { int n=1; for (;n<=12;n++); { if (n%4!=0) continue; printf("%5d",n); } } 六、程序设计(20分,两个小题) 1 有一个字符串,包含n 个字符,编写一个程序,将此字符串中从第m个字符开始的全部字符复制成另一个字符串。(用函数完成)
336
考试题型 一、单选题 (每小题2分,共20分 ) 下列字符中,ASCⅡ码值最大的是( )。 A)A B)b C)M D) n
的八进制数为( )。 A) 24.54 B) 22.68 C) 23.83 D) 21.22 二、判断题(每小题3分,共15分) +=运算符的结合性是从右向左的 。 int x,y; x=y;后y中的值将放入x中,y中的值将丢失。 三、程序填空题(每小题5分,共20分) +=运算符的结合性是从右向左的 。
337
四、阅读程序写答案 (每小题5分,共15分) main() { int n=1; for (;n<=12;n++); { if (n%4!=0) continue; printf("%5d",n); } } 五 、程序填空 (每小题5分,共15分) main() { int n=1; for (;n<=12;n++); { if (n%4!=0) continue; printf("%5d",n); } }
338
六、程序设计(20分,两个小题) 1 有一个字符串,包含n 个字符,编写一个程序,将此字符串中从第m个字符开始的全部字符复制成另一个字符串。(用函数完成)
339
一 、填空 1、在计算机内部,数据是以 形式加工、处理和传送的。 2、计算机硬件由 、 、 、 、 五大部分构成。 3、计算机软件可分为 和 两大部分。 4、在微机系统中,普遍使用的字符编码是__________码。 二 、数制 转换 1、(31.68)=()2=()8=()16 2、 ( )2 =()10 三、简答 1、按优先级别列出六种不同级别的运算符,并指出其结合率。 2、请写出C语言中的运算: 等于、不等于、大于等于、小于等于、逻辑与、逻辑或、按位与、按位或。 四、程序设计 由键盘输入一个字符先判断是否为大写字母,如果是转换为小写字母输出,如果不是提示为非法字符。
Similar presentations