1
C程序设计
2
第一章 C语言概述第二章 数据类型、运算符和表达式第三章 最简单的C程序设计第四章 逻辑运算和判断选取控制第五章 循环控制第六章 数组第七章 函数第八章 编译预处理第九章 指针第十章 结构体与共用体第十一章 文件
C 程序设计
3
第一章 C 语言概述
1,C语言的发展与计算机对话是从低级语言开始逐步发展起来的 。
机器语言低级语言汇编语言
FORTRAN语言
ALGOL 60语言
COBOL语言
BASIC语言
PASCAL语言
C/C++语言
ADA语言
Java语言高级语言
4
ALGOL 60 (1960年 )
CPL语言 (1963年 )
C语言的发展 BCPL 语言 (1967年 )
B语言 (1970年 )
C语言 (1972- 1973年 )
2,C语言的特点
①提供了一整套控制语句 (9种 ),实现了对程序逻辑流的有效控制,这有利于结构化程序设计。
②数据类型丰富,提供了整型、实型、字符型、数组类型、指针类型、
结构体类型、共用体类型等数据类型。
③可以直接访问物理地址,进行位操作。
④提供了 34种丰富的运算符。
⑤具有预处理功能。
⑥具有很好的可移植性。
⑦生成目标程序质量高,程序执行效率高。
5
3,简单的 C程序介绍例 1.1
main()
{
printf("This is a c program.\n");
}
例 1.2
main()
{ int a,b,sum;
a=123; b=456;
sum=a+b;
printf("sum is %d\n",sum);
}
6
例 1.3
main()
{int a,b,c;
scanf("%d,%d",&a,&b);
c=max(a,b);
printf(‖max=%d\n",c);
}
int max(x,y)
int x,y;
{int z;
if (x>y) z=x;
else z=y;
return(z) ;
}
C函数从 main()开始执行。
变量定义和语句之后必须有一个分号(;)。
一行内可写几个语句,
一个语句可分写在多行。
可以用 /*…… */作注释。
C语言没有输入输出语句。
7
C 程序是由函数组成的,每一个源程序至少包含一个 main()函数。
一个函数是由函数的说明部分和函数体两部分组成。
①函数的说明部分函数类型 函数名 函数参数形参类型 形参
②函数体 (括在一对大括弧 {------}中的部分)
变量定义 (int a,b,c;)
函数体执行部分 (由若干语句组成)
※ 特例:空函数 dump()
{ }
int max(x,y)
int x,y;
8
第二章 数据类型、运算符和表达式
1.4 C程序的上机部骤三个步骤,?编辑,?编译 (F9),?运行 (F10)。
1,C的数据类型整型字符型实型(浮点型)
枚举类型数组类型结构体类型共用体类型单精度型双精度型指针类型空类型构造类型基本类型数据类型
9
2,常量常量:程序中其值不发生变化的量。
C语言中有数值常量字符常量字符串常量符号常量整型实型十进制八进制 (由数字0开头 )
十六进制 ( 由 0x 或 0X 开头 )
常用形式 (3.14159,-0.555,.666)
指数形式 (0.55e5,3.33E-3,4e+2
(用单引号括起来的一个字符。如,'a','D')
(用双引号括起来的一串字符。如,"abcd")
(用一个标识符代表一个常量 )
转义字符:常用,\‖开头后跟一个字符,但含义改变。见下表:
注意,'a' 和 "a"的区别
10
字符形式 功能
\n 换行
\t 横向跳格(即跳到下一个输出区)
\v 横向跳格
\b 退格
\r 回车
\f 走纸换页
\\ 反斜杠字符,\‖
\‘ 和 \‖ 单引号字符 (?);双 引号字符 (‖)
\ddd 1到 3位 8进制数所代表的字符
\xhh 1到 2位 16进制数所代表的字符例 ls2_1,main()
{printf(" ab c\t de\rf\tg\n");
printf("h\ti\b\bj k"); }
11
例 ls2_2
#define PRICE 30
main()
{int num,total;
num=10;
total=num*PRICE;
printf("total=%d",total);
}
符号常量例 2.3:
整型数,125,0125,0x125,+35,-235,-035
实型数,3.14159,-555.6,888.0,0.88,8.88e+18
注意,用指数形式表示的浮点数必须有尾数,指数部分必须是整数。如,e4,.e3,0.25e4.5,e 等是错误的 。
12
3,变量变量:程序中其值可发生变化的量 。
每一个变量都应有一个名字,称为变量名。而且在内存中占据一定的存储空间,用来存放变量的值。
标识符:由程序员定义的单词,用它来命名程序中的一些实体(如函数名、变量名、类型名、数组名等)。
C语言规定标识符由字母、数字和下划线三种字符组成,且第一个字符必须为字母或下划线。(注意,?大小写字母不一样。长度任意。不要采用系统保留字。)
C语言规定对使用的变量必须 先定义,后使用 。
目的,?保证程序中变量名的正确使用。
可分配相应的存储空间。
便于检查变量所进行的运算是否合法。
13
① 整型变量,(四种类型 )
类型 所占位数 数的范围基本型 (int) 16 -32768~32767
短整型 (short int) 16 -32768~32767
长整型 (long int) 32 -2147483648~2147483647
无符号整型 (unsigned int) 16 0~65535
无符号短整型 (unsigned short) 16 0~65535
无符号长整型 (unsigned long) 32 0~4294967295
整型变量的定义格式,类型 变量名表列 ;
例 ls2-4 main()
{int a,b,c,d; c=a+u; d=b+u;
unsigned u; printf("a+u=%d,b+u=%d\n",c,d);
a=12; b=-24; u=10; }
无符号型
14
② 实型变量,(两类)
类型 所占位数 数的范围单精度实型 (float) 32 10 ~10
双精度实型 (double) 64 10 ~10
单精度实型提供 7位有效数字,双精度实型提供 15~16位有效数字。
如,float a;
a=111111.111 (最后两位小数不起作用)
而,double a;
a=111111.111 (全部接收)
-38 38
-308 308
③ 字符变量,char
用来存放一个字符常量。占一个字节 (8位 ),存放该字符的 ASCII码值。
如,char c1,c2;
c1='a'; c2='b';
15
例 2.5,main()
{char c1,c2;
c1=97; c2=98;
printf("%c %c",c1,c2);
}
例 2.6,main()
{char c1,c2;
c1='a'; c2='b';
c1=c1-32; c2=c2-32;
printf("%c %c",c1,c2);
}
97 98
01100001 01100010
c1
c1
c2
c2
c1='a'; c2='b';
'a'='A'+32
ASCII码表中大小写字母之间具有:
C语言中允许字符型数据与整型数据互相赋值。如:
int i; i='a';
char c; c=97;
16
例 2.7,main()
{int i;
char c;
i='a'; c=97;
printf("%c,%d\n",c,c);
printf("%c,%d\n",i,i);
}
输出结果,a,97
a,97
④ 变量赋初值:
在定义变量的同时给变量初始化。
如,int a=10;
float f=5.55;
char c='a';
int a=15,b=15,d=15; 不能写成,int a=b=d=15;
格式为,类型 变量名=常量值请分析下面程序:
相当于,int a;
a=10;
17
main()
{ int a;
printf("\n%d",a);
}
输出结果是什么?
没有赋初值的变量,其值为一个不定的值。引用该变量,
就回出现上面程序中的问题,产生莫名奇妙的结果。
⑤数据类型转换:
C语言允许不同类型的数值型数据可以混合运算,运算时系统自动将数据转换成同一类型数据。
double
long
unsigned
int
float
char,short低级高级必定的转换必定的转换
18
例如,若有 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运算。
用?的结果减?的结果。
强制类型转换:格式如下例如,(double) a,(int)(x+y),(float)(5%3),(int)x+y
⑥ 算术运算符和算术表达式
基本算术运算符,+,-,*,/,%
(注:两个整数相除,结果为整数。)
(类型名 )(表达式 )
19
算术表达式和运算符的优先级与结合性:
用算术运算符和括号,( )‖将运算对象连接起来,符合C
语法规则的式子称为算术表达式。
运算对象,常量、变量、函数等。
优先级,先乘除后加减。
结合性,自左至右。
例如,a*b/c-1.5+‘a‘ ; a-(b*c)
自增、自减运算符,++,-- (使变量的值增1或减1)
结合性,自右至左。
例如,++i,--i (先自增或自减,后引用)
i++,i-- (先引用,后自增或自减)
i=3; printf(―%d‖,++i);
i=3; printf(―%d‖,i++);
20
⑦ 赋值运算符和赋值表达式赋值符号,= (将赋值符右边表达式的值赋给赋值符左边的一个变量。 )
赋值运算符的数据类型若不一致,则要进行类型转换。
转换方式为:
将实型数据赋给整型变量时,舍弃实数的小数部分。
如,i=5.65,i 的值为 5。
将整型数据赋给实型变量时,数值不变。
将字符数据赋给整型变量时,将字符数据放到整型变量低 8位中。
两种情况,若字符最高位为 1,整型变量的高 8位置 1,否则高 8位置 0。若把字符处理为无符号的量,整型变量的高 8位置 0。
1 1 11 11 10
11 11 11 101 1 11 11 11
C='\376' 11 11 11 10
11 11 11 100 0 00 00 00
C='\376'
i的值为 254i的值为 -2
21
将整型数据赋给长整型变量时,将整型数据放入长整型变量的低 16位,
若整型数为负数,长整型变量的高 16位置 1,否则长整型变量的高 16位置 0。
将长整型数据赋给整型变量时,取长整型数据低 16位。
将 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。
int 赋给 unsigned int
将 long 赋给 unsigned long 时,原样赋值。
short 赋给 unsigned short
例如,main()
{unsigned a;
int b=-1;
a=b; printf(―%u‖,a);
}
应注意数值的范围,
超出则数据出错在负数时,数值发生变化。
11 11 11 11 11 11 11 11 1 1 11 11 11 11 11 11 11ba
11 11 11 11 11 11 11 11
1 1 11 11 11 11 11 11 11b
a
22
复合的赋值运算符,(共十个)
+=,- =,*=,/=,%=,<<=,>>=,&=,^=,|=
例如,a+=3 等价于 a=a+3
x*=y+8 等价于 x=x*(y+8)
x%=3 等价于 x=x%3
赋值表达式:
由赋值运算符将一个变量和一个表达式连接起来的式子。
格式为,<变量 ><赋值运算符 ><表达式 >
位运算注:若右边为表达式应加圆括号,( )‖!
23
赋值运算符结合性为:,自右而左,。
例如,a=b=c=5
a=5+(c=6)
a=(b=4)+(c=6)
a=(b=10)/(c=2)
a+=a– =a*a 若 a=5,则 赋值 表达式的值 为 –40。
⑧逗号运算符和逗号表达式逗号运算符:,
格式,表达式 1,表达式 2,表达式 3,,表达式 n
优先级,最低 。
逗号表达式的值为 表达式 n的值。
例如,a=3*5,a*4
(a=3*5,a*4),a+5
x=(a=3,6*3)
x=a=3,6*a
若 x的值是 8,则表达式 x*=x-=x+=x的值?
24
第三章 最简单的C程序设计
1、C语句概述
C语言的语句是用来向计算机系统发出操作指令。每一个为实现特定目的的程序都包含若干个C语句。
C语句的五种分类:
控制语句:完成一定的控制功能(9条)。
函数调用语句:由一次函数调用加一个分号构成。
如,scanf("%d\n",&a);
表达式语句:由表达式加一个分号构成。
如,i=i+1;
空语句:由一个分号构成。
复合语句;由一对大括号“{}”组成。
如,{z=x+y; b=15; printf("%d",b); }
25
2、程序的三种基本结构
顺序结构:从前向后顺序执行程序。
选择结构:根据判断条件的结果选择执行程序。
A
B
A
B N-S流程图流程图
P
A B
P真 假
A B
流程图
N-S流程图
26
另:由 选择结构派生出的多分支选择结构:
A1 A2 Ai An
k=k1 k=k2 k=ki k=kn
k
B
27
循环结构:根据条件反复的执行某一段程序若干次。
当型循环结构:
P
A
真假当 P为真
A
N-S流程图流程图直到型 循环结构:
P
A
真假 直到 P为真
A
流程图
N-S流程图
28
3、赋值语句由赋值表达式加一个分号构成。
如,h=49;
注意:赋值语句与赋值表达式的区别。
如,if ((a=b)>0) t=a;
赋值表达式 赋值语句
4、数据输出
C语言数据输出,是由调用输出函数来完成。
putchar函数功能:向终端输出一个字符。
格式,putchar (ch)
函数名 参数
29
例 4.1,#include "stdio.h"
main()
{char a,b,c;
a='B';b='O';c='Y';
putchar(a);putchar(b);putchar(c);putchar('\n');
}
格式输出函数 printf()
功能:输出若干个任意类型的数据。
格式,printf("格式控制 ",参数 1,参数 2,参数 3,… )
由格式说明和普通字符构成 输出数据。由表达式构成。
格式说明,由 %后跟一个 格式字符 组成。中间可插入 l,m、
n,0,+ 和 - 几个附加符号。
普通字符,照原样输出。
30
例如,printf("a=%d b=%d",a,b); (设 a=12;b=15;)
输出结果为,a=12 b=15
格式字符:
格式字符 作 用
d 以带符号的十进制形式输出整数(正数不输出符号)。
o 以 8进制无符号形式输出整数(不输出前导符 0)。
x 以 16进制无符号形式输出整数(不输出前导符 0x)。
u 以无符号十进制形式输出整数。
c 以无符号形式输出,只输出一个字符。
s 输出字符串。
f 以小数形式输出单、双精度数,隐含输出 6位小数。
e 以标准指数形式输出单、双精度数,数字部分小数位数为 6位。
g 选用 %f或 %e格式中输出宽度较短的一种格式,不输出无意义的 0。
31
例?,long a=135790;
printf("%ld",a );
例?,a=-1;
printf("%d,%o,%x",a,a,a);
例?,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);
}
例?,main()
{int i=234;
char c=?a‘;
printf("%d,%5d,%c,%3c",i,i,c,c);
}
1 1 11 11 11 11 11 11 11
a=-1
输出为,-1,177777,ffff
a=-1,177777,ffff,65535
b=-2,177776,fffe,65534
结果为
234,234,a,a
结果为指定输出宽度。数据宽度不够,
前面补空格,超过原样输出。
32
附加格式说明字符:
字 符 作 用字母? 用于长整型整型,可加在格式符 d,o,x,u前面。
m(代表一个正整数 ) 数据最小宽度。
.n(代表一个正整数 ) 对实数,表示输出 n位小数 ;对字符串,表示截取的字符个数。
一 输出的数字或字符在域内向左靠。
例?,main()
{printf("%3s,%7.2s,%.4s,%-5.3s\n","CHINA","CHINA","CHINA","CHINA");
}
例?,main()
{float f=123.456;
printf("%f %10f %10.2f %.2f %-10.2f\n",f,f,f,f,f);
}
输出为,CHINA,CH,CHIN,CHI
输出为,123.455994 123.455994 123.46 123.46 123.46
33
例?,main()
{float f=123.456;
printf("%e %10e %10.2e %.2e %-10.2e\n",f,f,f,f,f);
} 输出结果为:
1.234560e+002 1.234560e+002 1.23e+002 1.23e+002 1.23e+002
13列 13列 10列 9列 10列例?,main()
{float f=123.456;
printf("%f %10e %g\n",f,f,f);
}
输出为,123.456000 1.234560e+002 123.456
13列10列 10列格式控制字符串中用连续两个 %,表示输出一个 %。
如,printf(―%f%%‖,1.0/3); 输出为,0.333333%
34
5、数据输入
C语言数据输入,是由调用输入函数来完成。
getchar函数功能,从输入设备输入一个字符。
格式,getchar ()
例 5.1 #include "stdio.h"
main()
{char c;
c=getchar();
putchat(c);
}
格式输入函数 scanf()
功能:输入若干个任意类型的数据。
格式,scanf("格式控制 ",参数 1,参数 2,参数 3,… )
只能接收一个字符 !
由格式说明和普通字符构成变量的地址或字符串的首地址。
35
格式说明,由 %后跟一个 格式字符 组成。中间可插入?,h、
m,* 几个附加字符。
普通字符,照原样输入。
例如,main()
{int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d,%d,%d\n",a,b,c);
}
输入格式为,5 6 7
若用,scanf("%d,%d,%d",&a,&b,&c);
则输入格式为,5,6,7
若用,scanf("%d:%d,%d",&a,&b,&c);
则输入格式为,5:6,7
36
格式字符:
格式字符 作 用
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格式输入
37
附加格式说明字符:
字 符 作 用字母 l 用于输入长整型数据,可加在格式符 d,o,x,f,e前面。
字母 h 对实数,表示输出 n位小数 ;对字符串,表示截取的字符个数。
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。
例?,scanf("%d%c%f",&a,&b,&c);
若输入数据格式为,1234a123o.26
例?,scanf("%2d %*3d %2d",&a,&b);
若输入数据格式为,12 345 67 则将 12赋给 a,67赋给 b。
a b c
38
6、程序举例
ls3_10输入三角形的三边长,求三角形的面积。
(设输入的三边长 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);
}
ls3_11从键盘输入一个大写字母,要求改用小写字母输出。
ls3_12 求 ax2+bx+c=0方程的根。 a,b,c由键盘输入,设 b2-4ac>0
39
main()
{int a=0123,b=0x123;
float c=111111.1111111111,d=1.23e3;
double x=111111.1111111111,y=1.23e3;
printf("%o,%d\n",a,a);
printf("%x,%d\n",b,b);
printf("%f,%f\n",c,x);
printf("%f,%e\n",d,y);
}
例 ls3_1
40
main()
{int i=0,j=0,x,y; i=i+1; j++;
printf("%d,%d\n",i,j); /* 1,1 */
x=++i; y=j++;
printf("%d,%d\n",x,y); /* 2,1 */
printf("%d,%d\n",++y,y); /*from right to leght 2,1*/
printf("%d,%d\n",++x,y++); /*3,2 */
printf("%d,%d,%d\n",i,(i++)+(i++)+(i++),i); /*5,9,2*/
printf("%d,%d,%d\n",i,(++i)+(++i)+(++i),i); } /*8,21,5*/
例 ls3_2
41
第四章 逻辑运算和判断选取控制
1,关系运算符和关系表达式关系运算符用于两个数值之间的比较运算。C语言提供 6
种关系运算符,它们是:
<,<=,>,>=,==,!=
优先级相同 优先级相同高到低关系运算符、算术运算符和赋值运算符的优先级为:
算术运算符 关系运算符 赋值运算符低高
42
例如,c>a+b 等效于 c>(a+b)
a>b!=c 等效于 (a>b)!=c
a==b<c 等效于 a==(b<c)
a=b>c 等效于 a=(b>c)
关系表达式:用关系运算符将两个表达式连接起来的式子。
关系表达式运算结果为:“真”或“假”值。C语言用 1代表
“真”值,用 0代表“假”值。同时系统在运算时以非 0为
“真”值,以 0为“假”值。
例如,若 a=3,b=2,c=1
f=a>b>c 则 f的值为 0。
2,逻辑运算符和逻辑表达式
C语言提供 3种逻辑运算符:
逻辑运算符 结合性 优先级
&& 与 自左至右 中
|| 或 自左至右 低
! 非 自右至左 高
43
逻辑表达式:用逻辑运算符将关系表达式或逻辑量连接起来的式子。运算结果为:“真”或“假”值。系统在运算时以非 0为“真”值,以 0为“假”值。
逻辑运算:
a b&&
0
0
0
1
0
0
1
1
a b||
0
1
1
1
0
0
1
1
01
a !a 例如,4&&0||2 的值为 1
5&&!0 的值为 1
与运算 或运算非运算
44
运算符 优先级逻辑非! 高算术运算符关系运算符
&&和 ||
赋值运算符 低逻辑、关系、算术和赋值运算符的优先级为:
例如,(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--!0 值为 1
'c'&&'d' 值为 1
在逻辑表达式求解时,有可能出现某些逻辑运算符不被执行,但整个表达式的结果已经得到。
45
例如,? a&&b&&c
若 a为 0,则 b和 c不再判断。表达式结果为 0,即“假”值。
a||b||c
若 a为 1,则 b和 c不再判断。表达式结果为 1,即“真”值。
m=(a>b)&&n=c>4) 设 a=1,b=2,c=3,m=1,n=1。
则 m为 0,n 为 1。
请问表达式 x>0&&x<10 的含义是什么?
a
b
c
0
0
0
非 0
非 0
1 0
a
b
c
非 0
0
0
0 1
非 0
非 0
&&运算 ||运算
46
3,条件运算符和条件表达式条件运算符,?,它是一个三目运算符。
条件表达式的一般格式为:
表达式 1?表达式 2:表达式 3
运算过程:表达式1的结果为真(非0)时,表达式2
的计算结果作为条件表达式的值;否则,取表达式3的计算结果为条件表达式的值。
如,a>b?a:b
条件运算符的 优先级 低于逻辑、关系、算术运算符高于赋值运算符。
如,a>b?a:b+1 相当于 (a>b)?a:(b+1)
47
条件运算符的结合性为:“自右至左”。
如,a>b?a:c>d?c:d 相当于 a>b?a:(c>d?c:d)
注意:条件表达式中的表达式 1、表达式 2、表达式 3可以是不同的类型。
如 ls4_1,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=1 p=1.5
输出 p值
48
4,if 语句作用:判定给定的条件,决定执行不同段 的 程序。
⑴ if语句的格式:
(a) if (表达式 ) 语句 1 [ else 语句 2 ]
表达式语句 1 语句 2
真 (非 0) 假 (0)
只能用一个语句,
若有多个语句可用复合语句{}。执行过程:
括号,()‖不能缺省
49
例?,if (x>y) z=x;
else z=y;
例?,if (x>y) printf("%d,%d",x,y);
else printf("%d,%d",y,x);
(b) if 语句可以省略 else部分,变为如下格式:
if (表达式 ) 语句执行过程,表达式结果为真,执行表达式后面的语句,否则不执行该语句,而执行 if语句下面的语句。
[例 ls4_5] 输入两个实数,按代数值由小到大次序输出这两个数。
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);
}
a b
t
例,if (x%2==0)
printf(―It is an even number\n‖);
else
printf(―It is an odd number\n‖);
50
[例 ls4_6] 输入三个实数,按代数值由小到大次序输出这三个数。
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",a,b,c);
}
a b c
a b c
小 大
( c) if语句实现多分支选择结构的格式:
if (表达式 1) 语句 1
else if (表达式 2) 语句 2

else if (表达式 m) 语句 m
else 语句 n
51
表达式 1
语句 1 语句 2
真 (非 0)
假 (0)
执行过程:
表达式 2
真 (非 0)
表达式 3
语句 3
真 (非 0) 表达式 4
语句 4 语句 5
假 (0)
假 (0)
假 (0)
真 (非 0)
if (score>=90) l=?A‘;
else if (score>=80) l=?B‘;
else if (score>=70) l=?C‘;
else if (score>=60) l=?D‘;
else l=?E‘;
Example
52
⑵ if语句的嵌套:
在 if语句的语句 1或语句 2部分,若使用 if语句,称为 if语句的嵌套。如,if (表达式 1)
if (表达式 2) 语句 1
else 语句 2
else
if (表达式 3) 语句 3
else 语句 4
内嵌 if语句
[例 ls4_7] 有一函数如下,编一程序,输入一个 x值,输出 y值。
-1 (x<0)
y= 0 (x=0)
1 (x>0)
main()
{int x,y;
scanf("%d",&x);
if (x<0) y=-1;
if (x==0) y=0;
if (x>0) y=1; printf("x=%d,y=%d\n",x,y);}
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;
53
注意,If语句嵌套使用时,if与 else是以最近匹配规则来匹配。
例?,y=-1;
if (x!=0)
if(x>0) y=1;
else y=0;
例?,y=0;
if (x>=0)
if(x>0) y=1;
else y=-1;
请分析它们的运行结果?
例?,y=-1;
if (x>0) y=1;
if (x==0) y=0;
例?,y=0;
if (x!=0)
if(x>0) y=1;
else y=-1;
54
5,多分支选择结构亦可用 switch语句实现,
A1 A2 Ai An
K
k=k1 k=k2 k=ki k=kn
B
55
switch 语句格式,switch(表达式 )
{case 常量 表达式 1:语句序列 1
case 常量 表达式 2:语句序列 2
case 常量 表达式 3:语句序列 3

case 常量 表达式 n:语句序列 n
default,语句 序列 n+1
}
执行过程为:当表达式的值等于常量表达式 i的值,则从语句序列 i开始执行到语句序列 n+1为止 。若表达式的值不等于任何一个常量表达式的值,则只执行 default后 面 的语句。
一般在每个语句序列之后加一个 break语句,这样在执行语句序列 i之后,使流程跳出 switch结构,实现 多分支选择 结构 。
一般为整型或字符型表达式可以是一个语句,也可以是几个 语句 。
Ls4_2.c
56
例?:编写一个能进行两个操作数加减乘除四则运算的计数器模拟程序。 (ls4_3.c)
main()
{char op; float x,y;
printf(―Please input an 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");
}
}
输入:两个操作数和运算符计算:根据运算符确定运算输出:运算结果
+‘进行加运算。
‘ -‘进行减运算 。
‘ *’进行乘运算 。
‘ /‘进行除运算 。
57
例?:分析下列程序的输出结果。 (ls4_4,c)
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);
}
执行结果为,m=4,n=5
switch语句的嵌套
58
6.程序举例
写程序,判某一年是否闰年。
输入:年 (year)
计算:判是否闰年输出:闰年或非闰年 (leap)
能被 4整除,但不能被 100整除。
能被 4整除,又能被 400整除。
59
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‖);
}
Lx4_1.c
60
main()
{int x,x1;
scanf(―%d‖,&x1);
if (x1<0) x=-x1;
if (x<10) printf(―x=%d y=1\n‖,x1);
else if (x<100) printf(―x=%d y=2\n‖,x1);
else if (x<1000) printf(―x=%d y=3\n‖,x1);
else if (x<10000) printf(―x=%d y=4\n‖,x1);
else printf(―x=%d y=5\n‖,x1);
}
¤ 输入一个带符号的短整型数,输出该数的位数。
Any
bug?
61
求 ax2+bx+c=0方程的解。
输入,a,b,c
计算:求解方程输出:两个实根或两个复根
a=0,不是二次方程。
b2-4ac=0,有两个相等实根。
b2-4ac>0,有两个不等实根。
b2-4ac<0,有两个共轭复根。
62
#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));
else if (disc>1e-6)
{x1=(-b+sqrt(disc))/(2*a);
x2=(-b-sqrt(disc))/(2*a);
printf(" has distinct real roots:%8.4fand%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); }
}
Lx4_2.c
请问该程序有无问题?
63
给出一百分制成绩,要求输出成绩等级‘ A‘、’ B‘、‘C’、‘D’、
‘E’。 90分以上为‘ A‘,80~89分为’ B‘,70~79分为‘C’,
60~69分为‘D’,60分以下为‘E’ 。
64
main()
{int x;
scanf("%d",&x);
if (x>100||x<0)
printf(―input error\n");
else
{ x=x/10;
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");
}
}
Lx4_3.c
65
运输公司对用户计算运费,距离越远,每公里的费用越低。标准如下:
main()
{int s;float p,w,d,f;
scanf(―%f,%f,%d‖,&p,&w,&s);
if (s<250 ) d=0;
else if (s<500) d=2;
else if (s<1000) d=5;
else if (s<2000) d=8;
else if (s<3000) d=10;
else d=15;
f=p*w*s*(1-d/100.0);
printf(―freight=%f‖,f);}
S<250 没有折扣
250?s<500 2%折扣
500?s<1000 5%折扣
1000?s<2000 8%折扣
2000?s<3000 10%折扣
3000?s 15 %折扣运费 f=每吨每公里运价 p*
重量 w*距离 s*( 1-折扣
d/100)
66
S<250 S/250=0 没有折扣
250?s<500 S/250=1 2%折扣
500?s<1000 S/250=2,3 5%折扣
1000?s<2000 S/250=4,5,6,7 8%折扣
2000?s<3000 S/250=8,9,10,11 10%折扣
3000?s S/250=12,… 5 %折扣
main()
{int c,s;float;p,w,d,f;
scanf(―%f,%f,%d‖,&p,&w,&s);
if (s>=3000) c=12;
else c=s/250;
switch(c)
{case 0:d=0;break;
case 1:d=2;break;
case 2:
case 3:d=5;break;
case 4:
case 5:
case 6:
case 7,d=8;break;
case 8:
case 9:
case 10:
case 11:d=10;break;
case 12:d=15;break;}
f=p*w*s*(1-d/100.0);
printf(―%f‖,f);}
将第一栏的条件转换为第二栏的常量,
才可用 switch 语句注意
67
¤ 输入一个日期(含年、月、日),输出该日是该年的第几天。
main()
{ int year,month,day,d=0,flag=1;
printf(―\nplease input year,month,day:‖);
scanf(―%d%d%d‖,&year,&month,&day);
switch(month)
{ case 1:d=0;break;
case 2:d=31;break;
case 3:d=31+28;break;
case 4:d=31+28+31;break;
case 5:d=31+28+31+30;break;
case 6:d= 31+28+31+30+31;break;
case 7:d= 31+28+31+30+31+30;break;
68
case 8:d= 31+28+31+30+31+30+31;break;
case 9:d= 31+28+31+30+31+30+31+31;break;
case 10:d= 31+28+31+30+31+30+31+31+30;break;
case 11:d= 31+28+31+30+31+30+31+31+30+31;break;
case 12:d= 31+28+31+30+31+30+31+31+30+31+31;break;
default,printf(?input error of month!\n);flag=0;
}
if ((m>2)&&((year%4==0)&&year%100!=0||(year%400==0)))
d++;
d=d+day;
if (flag)
printf(year=%d month=%d day=%d-------no=%d\n‖,year,month,day,d);
}
小结本章
69
第五章 循 环 控 制循环控制结构在程序中是指对某段程序或某条语句根据条件重复执行。C语言提供了 while,do-while和 for三种支持循环控制结构的语句。
1,while语句
while语句是 支持“当型”循环控制结构的语句。
一般格式为,while (表达式 ) 语句执行过程:
表达式语句真 (非 0)
假 (0)
当表达式为真语句
N-S流程图若有多个语句可用复合语句{}
70
100
例 5.1 求 ∑n。
n=1
输入:无计算,1+2+3+…+100
输出:计算的和
sum=0;i=0
当 i<100
i=i+1
sum=sum+i
main()
{int sum=0;i=0;
while (i<100)
{i=i+1;
sum=sum+i;}
printf("sum=%d\n",sum);
}
练习:计算
12+22+32+ … +102
71
2,do_while语句
do_while语句是 支持“直到型”循环控制结构的语句。
一般格式为,do 语句
while (表达式 );
执行过程:
表达式语句真 (非 0)
假 (0)
直到表达式为真语句
N-S流程图若有多个语句可用复合语句{}
注意:
72
100
例 5.2 求 ∑n。
n=1
输入:无计算,1+2+3+?+100
输出:计算的和
sum=0;i=0
i=i+1
sum=sum+i
直到 i>=100
main()
{int sum=0;i=0;
do
{i++;
sum=sum+i;
}
while (i<100);
printf("sum=%d\n",sum);
}
注意:
两者的不同
73
输入 100个字符,统计其中空格、星号及小写字母的个数。
# include ―stdio.h‖
main()
{int i=1,blank=0,star=0,letter=0;
char c;
while (i<=100))
{c=getchar();
if (c==) blank++;
if (c==?*‘) star++;
if (c>=?a‘ &&c<=?z‘) letter++;
i++;}
printf(―blank=%d\nstar=%d\nlletter=%d\n‖,blank,star,letter);
}
能否将 if 语句改为,
if (c==) blank++;
else if (c==?*‘) star++;
else letter++;
74
while和 do_while语句的比较:
main()
{int sum=0;i=0;
scanf("%d",&d);
while (i<10)
{i++;
sum=sum+i;
}
printf("sum=%d\n",sum);
}
main()
{int sum=0;i=0;
scanf("%d",&d);
do {i++;
sum=sum+i;
}
while (i<10)
printf("sum=%d\n",sum);
}
我们看到:当 i<10时,两程序的结果相同。
而当 i>=10时,两程序的结果就不同了。
75
3,for 语句
for语句是一种使用比 while语句更加灵活的 循环控制语句。
一般格式为,for (表达式 1;表达式 2;表达式 3) 语句执行过程:
表达式 2
语句真 (非 0)
表达式 1
表达式 3
假 (0)
先求解表达式1; (只求一次 )
求解表达式 2,若为真 (非 0)值,则执行语句,然后求解表达式 3,再转到求解表达式 2。若为假 (0)值,
则结束 for语句的执行。
76
例如,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);
}
练习:计算
12+22+32+ … +102
77
For语句三个表达式的使用说明:
三个 表达式都可缺省,但分号 (;)不能省略。若表达式 1缺省,
则从表达式 2开始执行。 若表达式 2缺省,则认为表达式 2始终为真,循环无终止地进行下去。
例如,?for ( ;i<=100;i++) sum=sum+i;
for (i=1; ;i++) sum=sum+i;
for (i=1;i<=100; ) {sum=sum+i;i++;}
for ( ;i<=100; ) {sum=sum+i;i++;}
for ( ; ; ) 语句省略表达式 1 省略表达式 2
省略表达式 3省略表达式 1,3
循环无终止地进行下去
78
三个 表达式可以是C语言的任意型表达式。
例如,?for ( sum=0,i=1;i<=100;i++) sum=sum+i;
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;
逗号表达式逗号表达式
i=0;j=100
当 i<=j
k=i+j;
i++;
j--;
空语句逗号表达式
79
例,输入 10个整数,输出其中最大的一个。
main()
{int x,max,i;
scanf(―%d‖,&max);
for (i=1;i<=9;i++)
{scanf(―%d‖,&x);
if (max<x) max=x;}
printf(―max=%d\n‖,max);
}
请将此程序改为求 10个数中的最大数和最小数?
main()
{int x,max,min,i;
scanf(―%d‖,&max);min=max;
for (i=1;i<=9;i++)
{scanf(―%d‖,&x);
if (max<x) max=x;
if (min>x) min=x;}
printf(―max=%d\n‖,max);
printf(―min=%d\n‖,min);
}
80
例 5.3:用 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=sum+2*i-1;
printf("sum=%d\n",sum);
}
练习:任给正整数 n,
计算 1!+2!+3!+ …+n!
81
例 5.4:任给正整数 n,计算 1!+2!+3!+ …+n! 。
输入:无计算,1!+2!+3!+ … +n!
输出:计算的和
sum=0;i=1;t=1;
当 i<=n
t=t*i
sum=sum+t
i++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);
}
对数列的任一项都是在其前一项的基础上计算出来的,设 t为第 i项的前一项,则第 i项的值为 t*i。
4,程序举例
82
例,求 Sn=a+aa+aaa+……+aa……a 之值,其中 a是一个数字,n由键盘输入。 n个 a
main()
{int a,n,i;
long int sum=0,term=0;
scanf(―%d,%d‖,&a,&n);
for (i=1;i<=n;i++)
{term=term*10+a;
sum=sum+term;}
printf(―%ld\n‖,sum);
}
要加的项的通用表达式
83
例 5.10 用 —≈1- — + — - — + ┄ 公式求的近似值,直到最后一项的绝对值小于 10-6为止。
1 1 1
4 3 5 7
pi=0,term=1,sign=1
当 |term|>=10-6
pi=pi+term
n=n+2
sign=-sign
term=sign*1/n
pi=pi*4
输出 pi
#include ―math.h‖
main()
{int sign=1 ;float n=1.0,term=1,pi=0;
while (fabs(term)>=1e-6)
{pi=pi+term;
n=n+2;
sign=-sign;
term=sign*1/n;}
pi=pi*4;
printf(―pi=%f\n―,pi);}
84
例 5.11 求 Fibonacci 数列,1,1,2,3,5,8,…… 前 40个数。
main()
{
long int f1=1,f2=1;
int i;
for (i=1;i<=20;i++)
{printf(―%12ld %12ld ‖,f1,f2);
if (i%2==0) printf(―\n‖);
f1=f1+f2;
f2=f2+f1;}
}
1 1 2 3 5 8……
f1 f2 f1 f2
f1+f2
f2+f1
85
累加求和问题的编程步骤,
1,确定要加的项数即确定循环的次数;
2,循环内做两项工作:
求要加的项 term的通用表达式,term一般和循环变量有关或和上一项有关 ;
求累加和即,sum=sum+term
3,确定 sum,term所需的初值,并把它们放在循环之前。若是从第一项开始加,sum的初值为 0,term的初值视情况而定,一般使其符合要加的第一项。
4,检查边界是否正确:第一次和最后一次加的项是否正确?循环的次数是否多一次或少一次?
累加求和问题小结
86
# include ―stdio.h‖
main()
{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);}
}
例 5.14 译密码。输入一行字符,要求输出其相应的密码。编码规律是这样,将每个字母变成其后的第四个字母,A变成 E,W变成 A,X
变成 B,Y变成 C,Z变成 D,小写字母也是这样编码,非字母字符不变。
能否将此条件改为,
(c>‘Z‘|| c>‘z‘ )?
87
5,循环 语句的嵌套一个循环 语句内又包含另一个完整的 循环语句,称为循环语句的嵌套。内嵌的循环语句一般称为内循环,包含内循环的循环语句称为外循环。内循环再嵌套内层循环,就够成了多重循环。
main()
{int i,j;
for (i=1;i<10;i++)
{for (j=1;j<=i;j++)
printf(―%3d*%d=%2d‖,i,j,i*j);
printf(―\n‖); }
}
该语句执行多少次?
该程序的功能是什么?
88
例,打印如下图案:
*
* * *
* * * * *
* * * * * * *
* * * * *
* * *
*
main()
{int i,j,k;
for (i=1;i<=4;i++)
{for (j=1;j<=4-i;j++) printf(― ‖);
for (k=1;k<=2*i-1;k++) printf(―*‖);
printf(―\n‖);}
for (i=1;i<=3;i++)
{for (j=1;j<=i;j++) printf(― ‖);
for (k=1;k<=7-2*i;k++) printf(―*‖);
printf(―\n‖);}
}
89
多重循环程序设计时,应注意以下几点:
三种循环不仅可以自身嵌套,而切可以互相嵌套。
嵌套时,要在一个循环体内包含另一个完整的循环结构。即无论哪种嵌套关系,都必须将一个完整的循环语句全部放在某个循环体内。而不能使两个循环语句相互交叉。
运行时,应注意内嵌的语句执行过程。如
for (i=1;i<=n;i++ )
{ j=1;
while (j<=m )
{ printf("a");j++; }
}
该语句执行多少次?
内嵌循环外层循环 外层循环内嵌循环正确格式 不正确格式
90
6,Break语句 和 continue语句
⑴ break语句
break语句的功能是:
在 switch语句中使流程跳出 switch结构。
在 循环 语句中使流程跳出当前循环。
例 5.7:编程将从键盘上输入的若干个正整数求和,遇到负数则终止程序,并且输入的数不超过 10个。
输入:正整数计算:求累加和输出:和
i=1;
当 i<=M
输入 x
x>0
sum+=x
i=i+1
真 假
break
91
例 5.7:
#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\n",sum);
}
i=1;
当 i<=M
输入 x
x>0
sum+=x
i=i+1
真 假
break
main()
{int i;
for (i=1; ; i++)
if (i%3==0&&i%5==0&&i%7==3)
{printf(―%d ‖,i);
break;}
}
程序的功能?
92
⑵ continue语句
continue语句的功能是 在 循环 语句中使本次循环结束,即跳过循环体中下面尚未执行的语句,接着进行下次是否执行循环的判断。
例 5.8:编程把 100~200之间的不能被 3整除的数输出。
main()
{int i;
for (i=100;i<=200;i++)
{if (i%3==0)
continue;
printf("%d,",i);
}
}
93
main()
{int sum=0;i=1;
loop,if (i<=100)
{sum+=i;
i++;
goto loop;
}
printf("sum=%d\n",sum);
}
100
例 5.9 求 ∑n。
n=1
7,goto语句
goto语句的功能是,无条件的转向语句标号所指的语句去执行。
格式,goto 语句标号;
遵循标识符定名规则
94
例 5.12 判断 m是否素数。
main()
{int m,i,k;
scanf(―%d‖,&m); k=m/2;
for (i=2;i<=k;i++)
if (m%i==0) break;
if (i>k)
printf(―%d is a prime \n‖,m);
else
printf(―%d is not a prime\n‖,m);}
}
输入 m
输出是素数 输出不是素数
i>k
k=m/2
假假真用 break 结束循环 i=i+1
当 i<=k
m被 i整除
i=2
真素数就是只能被1和它自身整除的整数。
95
输入:无计算,求素数输出:输出素数
m=101
m<=200
i=2 k=sqrt(m)
i<=k
m%i==0
break i=i+1
i>k
输出 m
m=m+2
真真 假假例 5.5:求 100和 200之间的素数。
# include ―math.h‖
main()
{int m,i,k,n=0;
for (m=101;m<=200;m=m+2)
{ if (n%10==0) printf(―\n‖);
k=sqrt(m);
for (i=2;i<=k;i++)
if (m%i==0) break;
if (i>=k+1)
{ printf(―%d ‖,m);n=n+1;}}
}
96
分析下列程序的输出结果:
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);
}
结果,a=6,i=17
main()
{int i,k;
for ( k=2;k<=32766;k++)
{for (i=2;i<k;i++)
if (k%i==0)
break;
if (i==k)
printf("%d,",k);
}
}
97
补充作业题:
1.编程打印以下图案:
*
* *
* *
* *
* *
* *
*
0 1 2 3 4 5
1 2 3 4 5 6
2 3 4 5 6 7
3 4 5 6 7 8
4 5 6 7 8 9
5 6 7 8 9 10
2.求 sin-1x=x+ 1*x32*3 + 1*3x52*4*5 1*3*5x72*4*6*7+ +……
1
1 2 1
1 2 3 2 1
1 2 3 4 3 2 1
(1) (2) (3)
98
3.1991年我国人口为 11.3亿,问当人口增长率分别为 2%,1.5%,1%,
0.5%时,到哪一年我国人口超过 13亿?
4.小红今年 12岁,她的父亲比她大 20岁,编程计算出她的父亲几年后比她的年龄大一倍,那时他们俩的年龄各为多少?
3.百马百瓦问题:有一百匹马 (包括大马,小马,马驹 )驮 100块瓦,大马驮 3块,小马驮 2块,两个马驹驮 1块。编程求大马,小马,马驹各多少?
4.编程求 1*3*5*7*…….*49 的值。
5.已知 n个不等的数,求所有大于 m的数的和及平均值。
99
第六章 数 组前面我们使用了基本数据类型 (如整型、实型、字符型 ),
数组是 C语言提供的一种构造类型数据,即由基本数据类型按一定规则组成。
数组 是有序数据的集合。数组中的每一个元素都属于同一个数据类型。用一个统一的 数组名 和 下标来 唯一地确定数组中的元素。
如,int char
float等。
遵循标识符定名规则只能包含常量和符号常量。
表示数组包含的元素个数。
既数组的长度,从 0开始。
1,一维数组的定义和引用
一维数组的定义方式:
类型说明符 数组名 [常量表达式 ];
100
如,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语言规定只能逐个引用数组元素,不能一次引用整个数组。而且必须先定义数组,然后才能使用数组元素。
数组元素的表示形式:
数组名 [下标 ] 下标可以是表达式例 6.1 main()
{int i,a[10];
for (i=0;i<=9;i++) a[i]=i;
for (i=9;i>=0;i--) printf("%d ",a[i]);
}
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
0
1
2
3
4
5
6
7
8
9
101
一维数组的初始化:
所谓初始化就是在定义数组的同时,给数组元素赋初始值。
C语言规定,只有静态存储和外部存储数组才能初始化。方法见下例:
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]
0 1 2 3 4 0 0 0 0 0
b[0] b[1] b[2] b[3] b[4]b[5] b[6] b[7] b[8] b[9]
0 0 0 0 0 0 0 0 0 0
c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]
静态存储
static int a[10]={0,1,2,3,4,5,6,7,8,9};
static int b[10]={0,1,2,3,4}; (后 5个元素赋值 0。 )
static int c[10]={0,0,0,0,0,0,0,0,0,0}; static int c[10]等价
102
1 2 3 4 5
d[0] d[1] d[2] d[3] d[4]
static int d[]={1,2,3,4,5};
可以不指定数组长度,系统根据后面花括号里的数据决定数组的长度。
一维数组程序举例:
例 6.2 给数组 a中存入 10个整数,在保证数据不丢失的情况下,
将数组中的最大数存入 a[0]位置。
输入,10个整数计算:求最大数输出,a数组真 假
i=0;
当 i<=9a[0]<a[i]
t=a[0]
a[0]=a[i]
a[i]=t
103
例 6.3,请将数组 a[0……9] 中的元素循环右移 k位,要求只用一个元素大小的附加空间。
main()
{int i,j,k,x,a[10];
for (i=0;i<=9;i++)
scanf(―%d‖,&a[i]);printf(―\n‖);
scanf(― %d‖,&k); printf(―\n‖);
for (i=1;i<=k;i++)
{ x=a[9];
for (j=8;j>=0;j--)
a[j+1]=a[j];
a[0]=x;}
for (i=0;i<=9;i++)
printf(―%d ‖,a[i]);printf(―\n‖);}
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]
9 0 1 2 3 4 5 6 7 8
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
8 9 0 1 2 3 4 5 6 7
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
7 8 9 0 1 2 3 4 5 6
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
104
例 6.4 用选择法对若干个数排序(由小到大)。
选择法,通过比较首先选出最小的数放在第一个位置上,
然后在其余的数中选出次小数放在第二个位置上,依此类推,直到所有的数成为有序序列。
第二趟排序后,11 25 [61 82 72 47 33 47]
第一趟排序后,11 [33 61 82 72 47 25 47]
第三趟排序后,11 25 33 [82 72 47 61 47]
第四趟排序后,11 25 33 47 [72 82 61 47]
第五趟排序后,11 25 33 47 47 [82 61 72]
第六趟排序后,11 25 33 47 47 61 [82 72]
第七趟排序后,11 25 33 47 47 61 72 [82]
最后排序结果,11 25 33 47 47 61 72 82
如:已知原始数据,[47 33 61 82 72 11 25 47]
105
main()
{int a[11],i,j,min,t;
printf("input 10 numbers:\n");
for (i=1;i<11;i++)
scanf("%d",&a[i]);
printf("\n");
for (i=1;i<=9;i++)
{min=i;
for (j=i+1;j<=10;j++)
if (a[min]>a[j])
min=j;
if (min!=i)
{t=a[i];a[i]=a[min];a[min]=t;}}
printf("the sorted numbers:\n");
for (i=1;i<11;i++)
printf("%d ",a[i]);printf(―\n‖);
}
a[i]>a[j]
t=a[i]
a[i]=a[j]
a[j]=t
真 假
for j=i+1 to n
for i=1 to n-1
输出 a[1]到 a[n]
输入 n个数给 a数组选择的次数
select
106
例 6.5 用起泡法对若干个数排序(由小到大)。
起泡法,对相邻两个数比较和交换,使全部数据排列有序。
如,9
8
5
4
2
0
8
9
5
4
2
0
8
5
9
4
2
0
8
5
4
9
2
0
8
5
4
2
9
0
8
5
4
2
0
9
第 1次 第 2次 第 3次 第 4次 第 5次 结果第一趟最大数已得到。
8
5
4
2
0
5
8
4
2
0
5
4
8
2
0
5
4
2
8
0
5
4
2
0
8
第 1次 第 2次 第 3次 第 4次 结果第二趟
5
4
2
0
8
9
107
5
4
2
0
4
5
2
0
4
2
5
0
4
2
0
5
第 1次 第 2次 第 3次 结果第三趟
4
2
0
2
4
0
2
0
4
第 1次 第 2次 结果第四趟
2
0
0
2
第 1次 结果第五趟
0
2
4
5
8
9
最后结果即:如果有 n个数,则要进行 n-1趟比较。在第 j趟比较中要进行 n-j次两两比较。
2
0
4
5
8
9
108
a[j]>a[j+1]
t=a[j]
a[j]=a[j+1]
a[j+1]=t
真 假
for j=1 to n-i
for i=1 to n-1
输出 a[1]到 a[n]
输入 n个数给 a数组
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 (i=1;i<=9;i++)
for (j=1;j<=10-i;j++)
if (a[j]>a[j+1])
{t=a[j];a[j]=a[j+1];a[j+1]=t;}
printf("the sorted numbers:\n");
for (i=1;i<11;i++)
printf("%d ",a[i]);printf(―\n‖);
}
例 6.6
请比较冒泡法和选择法的不同?
bubble
109
如:初始数据,[47] 33 61 82 72 11 25 47
i=2(33) [33 47] 61 82 72 11 25 47
i=3(61) [33 47 61] 82 72 11 25 47
i=4(82) [33 47 61 82] 72 11 25 47
i=6(11) [11 33 47 61 72 82] 25 47
i=7(25) [11 25 33 47 61 72 82] 47
i=8(47) [11 25 33 47 47 61 72 82]
i=5(72) [33 47 61 72 82] 11 25 47
例 6.7,插入排序,每次对一个待排的数据,按其值的大小插入到前面已排好序的数据中。
110
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 (i=2;i<=10;i++)
{a[0]=a[i];
j=i-1;
while (a[0]<a[j])
{a[j+1]=a[j];j=j-1;}
a[j+1]=a[0];}
printf("the sorted numbers:\n");
for (i=1;i<11;i++)
printf("%d ",a[i]);printf(―\n‖);
}
为什么把 a[i]送给 a[0]? a[0]的作用有几个?
insert
111
例,用折半查找法在一组排好序 (递增有序或递减有序 )
的值中查找某个数据 。
折半查找的基本思想,
首先将待查数据 k与排好序 ( 递增有序 ) 的一组数据的 中间位置 上的数据进行比较,若相等,则查找成功;
若 k>a[mid],则待查数据 k只可能出现在右半部
a[mid+1… n]中,则应在这个右半部中再进行折半查找;
若 k<a[mid],则待查数据 k只可能出现在左半部
a[1… mid-1]中,则应在这个左半部中再进行折半查找;
这样通过逐步缩小查找范围,直到找到或找不到该数据 k为止 。
112
例,05,13,19,21,37,56,64,75,80,88,92
查找 k=21的过程:
1,05,13,19,21,37,56,64,75,80,88,92

low mid hig
2,05,13,19,21,37,56,64,75,80,88,92

low mid hig
3,05,13,19,21,37,56,64,75,80,88,92

low hig
mid
1 116
1 53
4 5
查找成功
113
1,05,13,19,21,37,56,64,75,80,88,92

low mid hig
2,05,13,19,21,37,56,64,75,80,88,92

low mid hig
3,05,13,19,21,37,56,64,75,80,88,92

low hig
mid
111 6
7 119
4,05,13,19,21,37,56,64,75,80,88,92
hig low
10 11

low>hig,查找不成功查找 85
9 10
114
main()
{int i,j,number,low,hig,mid,a[11],find;
printf(―please input 10 sorted numbers:\n‖);
for (i=1;i<=10;i++) scanf(―%d‖,&a[i]); printf(―\n‖);
printf(―please input the number to be searched:‖);
scanf(―%d‖,&number);printf(―\n‖);
low=1; hig=10; find=0;
while ((find==0)&&(low<=hig))
{mid=(low+hig)/2;
If (number==a[mid]) find=mid;
else if (number<a[mid]) hig=mid-1;
else low =mid+1;}
if (find==0) printf(―%d is not found\n‖,number);
else printf(―%d is the %dth number,\n ‖,number,find);} binsearc
115
2,二维数组的定义和引用
二维数组的定义方式:
类型说明符 数组名 [常量表达式 ][常量表达式 ];
例如,
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]
内存
116
二维数组的引用:
与一维数组一样,只能逐个引用数组元素,不能一次引用整个数组。而且必须先定义二维数组,然后才能使用数组元素。
二维数组元素的表示形式:
数组名 [下标 ][下标 ]
如,a[2][1]=a[0][0]+a[0][1];
在引用数组元素时,应注意下标值不要超出已定义数组的范围 。
下标可以是表达式内存
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]
1 2 3 4 5 6 7 8 9 10 11 12
二维数组的初始化:
如,?static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
117
分行赋初值:
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]
1 2 3 4 5 6 7 8 9 10 11 12
部分元素赋初值:
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]
1 0 0 0 5 0 0 0 9 0 0 0
static int a[3][4]={{1},{0,5},{0,0,11}};
1 0 0 0 0 5 0 0 0 0 11 0
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]
118
static int a[3][4]={{1},{9}};
1 0 0 0 9 0 0 0 0 0 0 0
static int a[3][4]={{1},{},{9}};
1 0 0 0 0 0 0 0 9 0 0 0
C 语言规定 一维长度可省略,但二维长度不能省略。系统会根据顺序赋初值或分行赋初值自动分配存储空间。如:
static int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12};
系统根据顺序赋初值自动分配 3行 4列存储空间。
static int a[][3]={{0,3},{},{0,12}};
系统根据分行赋初值自动分配 3行 2列存储空间。
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][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]
119
几点注意,
在数组定义时,一般要加“静态存储” 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]
120
二维数组程序举例:
例 6.4 将一个二维数组行和列元素互换,存到另一个二维数组中。
1 2 3
4 5 6
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");
for (i=0;i<=2;i++)
{for (j=0;j<=1;j++)
printf("%5d",b[i][j]);
printf("\n");}
}
输出 b数组交换元素输出 a数组
121
例 6.5 有一个 3*4的矩阵,要求编程序以求出其中值最大的那个元素的值,以及其所在的行号和列号。
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]
main()
{int i,j,row,column,max;
static int a[3][4]={{1,2,3,4},{9,8,7,6},{-10,10,-5,2}};
max=a[0][0]; row=0,column=0;
for (i=0;i<=2;i++)
for (j=0;j<=3;j++)
if (a[i][j]>max)
{max=a[i][j];
row=i;
column=j; }
printf("max=%d,row=%d,column=%d\n",max,row,column);
}
122
main()
{int a[3][4],i,j,k,c,t,r;
for(i=0;i<3;i++) for(j=0;j<4;j++) scanf("%d",&a[i][j]);
for(i=0;i<12;i++)
{ r=i/4;c=i%4;
for(j=r;j<3;j++)
if (j==r)
for(k=c+1;k<4;k++)
if(a[r][c]>a[j][k]) t=a[r][c],a[r][c]=a[j][k],a[j][k]=t;
else
{for(k=0;k<4;k++)
if(a[r][c]>a[j][k]) t=a[r][c],a[r][c]=a[j][k],a[j][k]=t;}
for(i=0;i<3;i++) {for(j=0;j<4;j++) printf("%7d",a[i][j]); printf("\n");}
}
将二维数组表示的数据排序。
sort2
123
例:编程求两个矩阵的乘积。( A=M*N,B=N*P)
1 2 3
4 1 2
a=
1 4 1
2 1 2
3 4 3
b= 14 18 1412 25 12a*b=
main()
{static int a[2][3]={{1,2,3},{4,1,2}},b[3][3]= {{1,4,1},{2,1,2},{3,4,3}};
int c[2][3],i,j,k;
for (i=0;i<=1;i++)
for (j=0;j<=2;j++)
{c[i][j]=0;
for (k=0;k<=2;k++)
c[i][j]=c[i][j]+a[i][k]*b[k][j];}
printf("array c:\n");
for (i=0;i<=1;i++)
{for (j=0;j<=2;j++)
printf("%5d",c[i][j]);
printf("\n");}
}
juzhengtimes
124
3,字符数组使用
字符 数组的定义、初始化和引用方式:
定义、初始化和引用方法与前面介绍一样。如:
char c[10],b[3][4];
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]
c p r o g r o m
c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] c[8] c[9]
static char c[10]={'c',' ','p','r','o','g','r','a','m'}
\0 空字符
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]
125
字符数组程序举例:
例 6.6 输出一个字符。
main()
{static char c[10]={'I',' ','a','m',' ','a',' ','b','o','y'};
int i;
for (i=0;i<10;i++)
printf("%c",c[i]);
}
例 6.7 输出一个钻石图形。
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");}
}
126
字符串和字符串结束标志:
字符串,用双引号括起来的一串字符。
字符串结束标志,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
用字符串常量初始化字符数组
127
注意:下面两例的不同
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]
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 \0 \0 \0 \0
花括号也可省略系统不加字符‘ \0‘。
系统自动在最后附加
128
字符数组的输入输出:
逐个字符输入输出:用格式,%c‖。
如,main()
{static char c[10];
int i;
for (i=0;i<10;i++)
scanf("%c",&c[i]);
for (i=0;i<10;i++)
printf("%c",c[i]);
}
将整个字符串一次输入输出。用格式,%s‖。
如,main()
{static char c[10];
scanf("%s",c);
printf("%s",c);
}
数组名数组元素输出时遇到第一个
,\0‖字符结束输出。
输入字符串时,不要超过数组长度。
129
C语言在用 scanf输入多个字符串时,以 空格 分隔。如:
static char c[5],d[5],e[5];
scanf("%s%s%s",c,d,e);
输入数据,How are you
则结果为
H o w
c[0] c[1] c[2] c[3] c[4]
a r e
d[0] d[1] d[2] d[3] d[4]
y o u?
e[0] e[1] e[2] e[3] e[4]
\0
\0
\0
下面程序输入数据为,How are you
main()
{static char c[20];
scanf("%s",c);
printf("%s",c); 输出结果:

How
\0
\0
130
字符串处理函数:
puts(字符数组 )
将一个字符串输出到终端。在输出时将‘ \0‘转换成‘ \n‘
如:
static char str[]="China\nXi‘an";
puts(str); 输出结果为:
strcat(字符数组 1,字符数组 2)
连接两个字符数组中的字符串,把字符串 2接到字符串 1的后面,结果放在字符数组 1中,函数调用后得到一个函数值 ——字符数组 1的地址。例如:
gets(字符数组 )
从终端输入一个字符串到字符数组,并且得到一个函数值,该函数值是字符数组的起始地址。
如,gets(str);
China
Xi‘an
131
strcpy(字符数组 1,字符串 2,N)
将字符串 2拷贝到字符数组 1中。例如:
static char str[30]="People‘s Republic of ";
strcpy(str,"China‖);
strcpy(str,"happy‖,2);
str字符数组变为:
ha\0na\0‘s Republic of \0\0……
static char str1[30]="People‘s Republic of ";
static char str2[]="China";
printf("%s",strcat(str1,str2));
输出,People‘s Republic of China
132
strcmp(字符串 1,字符串 2)
字符串 1与字符串 2比较,比较规则是两个字符串自左至右逐个字符相比(按 ASCII吗值大小比较),直到出现不同的字符或遇到 \0为止。如全部字符相同,则认为相等;若出现不同的字符,则以第一个不同的字符的比较结果为准。
比较结果由函数带回。
如果:字符串 1=字符串 2,函数值为 0。
字符串 1>字符串 2,函数值为一正整数。
字符串 1<字符串 2,函数值为一负整数。
strlen(字符数组 )
求字符串长度。如:
static char str[20]="China";
printf("%d",strlen(str));
133
strlwr(字符串 )
将字符串中的大写字母转换成小写字母。
strupr(字符串 )
将字符串中的小写字母转换成大写字母。
字符数组程序举例:
例 6.8 输入一行字符,统计其中有多少个单词,单词之间用空格分隔开。
当前字符 =空格未出现新单词,使 word=0,num不累加前一字符为空格( word=0),出现新单词,使 word=1,num累加。
前一字符为非空格( word=1),未出现新单词,,num不累加。
是否
134
#include ―stdio.h‖
main()
{char string[81]; int i,num=0,word=0;char c;
gets(string);
for(i=0;(c=string[i]!=?\0‘;i++)
if (c==? ‘) word=0;
else if (word==0) {word=1; num++; }
printf(―there are %d words in the line\n‖,num);}
当前字符 =空格未出现新单词,使 word=0,num不累加前一字符为空格( word=0),出现新单词,使 word=1,num累加。
前一字符为非空格( word=1),未出现新单词,num不累加。
是否
135
例 6.9 有三个字符串,要求找出其中最大者。
#include ―stdio.h‖
#include?string.h‖
main()
{char string[20]; char str[3][20]; 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 string is,\n%s\n,string);}
136
#include"stdio.h"
#include"string.h"
main()
{char a[5][10],max[10];int i;
printf("\nplease input 5 strings.\n");
for(i=0;i<5;i++)
gets(a[i]);
strcpy(max,a[0]);
for(i=1;i<5;i++)
if (strcmp(max,a[i])>0)
strcpy(max,a[i]);
printf("\nthe smallest string is,%s\n",max);}
例,有若干个字符串,要求找出其中最大者。
137
a[0][0]
a[0][1]

a[0][19]
a[1][0]
a[1][1]
a[1][19]
a[2][0]
a[2][1]

a[2][19]
a[0]
a[1]
a[2]


a[0]
a[1]
a[2]
a[0]
a[1]
a[2]
a[3]
a[4] A m e r i c a \0 …
D e n m a r k \0 …
C h i n a \0 …
B r i t a i n \0 …
G r e e c e \0 …
a 0 1 2 3 4 5 6 7 8 9 … 19
如何把选择法、冒泡法等排序算法应用到字符串的排序上?
char a[5][20];
138
#include"stdio.h"
#include"string.h"
main()
{char a[5][10],temp[10];int i,j;
printf("\nplease input 5 strings\n");
for(i=0;i<5;i++) gets(a[i]);
for(i=0;i<5;i++)
for(j=i+1;j<5;j++)
if (strcmp(a[i],a[j])>0)
strcpy(temp,a[i]),strcpy(a[i],a[j]),strcpy(a[j],temp);
printf("\n\nthe sorted strings are:\n");
for(i=0;i<5;i++)
printf("%s ",a[i]);
printf("\n“) ; }
139
#include "string.h"
#include "stdio.h"
main()
{char s[10],t[10] ; int i=0,j=0;
gets(s);gets(t);
while(s[i++]==t[j])
if (t[j++]=='\0')
{ printf("\n%d\n",0); exit(0);}
if(s[--i]-t[j]>0)
printf("\n%d\n",1);
else
printf("\n%d\n",-1);
}
例:分析程序的运行结果
140
main()
{static char s[]="121";
int k=0,a=0,b=0;
do
{k++;
if (k%2==0)
{a=a+s[k]-'0';continue;}
b=b+s[k]-'0'; a=a+s[k]-'0';}
while(s[k+1]);
printf("k=%d a=%d b=%d\n",k,a,b);}
zifuzhuan
例:分析程序的运行结果
k=2 a=3 b=2
141
#include "stdio.h"
main()
{int k;
char w[][10]={"china","japan","english","hreortg"};
for(k=1;k<3;k++)
printf("%s\n",&w[k][k]);}
zifushuzu
142
第七章 函数一个C程序可由一个主函数和若干个函数构成。由主函数调用其它函数,其它函数也可以互相调用。同一个函数可以被一个或多个函数调用任意多次。C程序的执行从 main()函数开始,调用其它函数后回到 main()函数,在 main()函数中结束整个程序的运行。
函数使我们设计程序的复杂程度得以简化。我们可以将一个任务划分为若干个功能模块,每一个功能模块用一个函数来完成。功能相同的模块只编一个函数。常用的功能模块编写成函数,放在函数库中供公共选用,如,printf(),gets()
等函数 。
main()
a b c
d e f g h h i
e g
函数调用示意图
143
例 7.1
main()
{printstar();
prit_message();
printstar();
}
printstar()
{
printf("*****************************\n");
}
print_message()
{
printf(" How do you do!\n");
}
运行结果为,******************************
How do you do!
******************************
144
所有函数都是平行的,可以互相调用。但不能调用主函数 (main())。
从用户的角度看:
标准函数 (库函数 )。 由系统提供用户自定义函数。 用户自己编制函数分为
从函数的形式看:
函数分为 无参函数有参函数 #include "stdio.h"
main()
{char c;
c=grtchar();
printstr();
printf("%c",c);
}
例如无参函数有参函数自定义函数标准函数
145
1,函数定义的一般形式
无参函数的定义形式类型标识符 函数名 ()
{说明部分语句}
有参函数的定义形式类型标识符 函数名 (形式参数表列 )
形式参数说明
{说明部分语句}
C语言规定:
省略类型标识符的函数,一律按整型处理。
类型标识符 规定了一个函数返回给调用函数的数据的类型。
146
例如:
int max(x,y)
int x,y;
{int z;
z=x>y?x:y;
return(z);
}
形式参数说明形式参数表列返回给调用函数的数据
C语言可以有空函数,形式为:
类型标识符 函数名 ()
{}
例如,dummy() {}
147
return(z);
}
z=x>y?x:y;
{int z;
2,函数参数和函数的返回值
形式参数和实际参数形式参数用于接收从调用函数传递来的数据。在未出现调用时,它们并不占内存中的存储单元,只有调用时才被分配内存单元。
例如,main()
{int a,b,c;
max(x,y)
int x,y;
a b c
x y
scanf("%d,%d",&a,&b);
c=max(a,b);); 6 5
6 5
z
6
6
printf("max is %d",c);
}
148
说明,?实参可以是常量、变量或表达式。
必须指定形参的类型,且实参与形参的类型应一致。
C语言规定,实参变量对形参变量的数据传递是“值传递”,由实参传递给形参,而不能由形参传回来给实参。
在定义形参类型时,还可以采用以下形式:
int max(int x,int y);
{… }
6
56
5
a b
x y
149
函数的返回值通过函数调用使主函数能得到一个确定的值,这个值就是函数的返回值。
函数的返回值是通过函数中的 return语句获得的。
return语句将被调函数中的一个确定的值带回主调函数中去。如果主调函数不要返回值,则可以不要 return语句。
函数值的类型应该和 return语句中表达式的类型一致。
如果不一致,以函数类型为准。对数值型数据,可以自动进行类型转换。例如,
main()
{float a,b;
int c;
scanf("%f,%f",&a,&b);
c=max(a,b);
printf("max is %d\n",c);
}
max(float x,float y)
{float z;
z=x>y?z:y;
return(z);
}
整型 实型运行时若输入 1.5,2.5
则输出结果为,max is 2
150
如果被调函数中没有 return语句,此时函数带回一个不确定的值。例如,7.1
main(){int a,b,c;
a=printstar();
b=print_message();
c=printstar();
printf("%d,%d,%d\n",a,b,c);
}
C语言规定,可以用,void‖定义“无类型”(或称“空类型”)。这样,系统就使函数不带回任何值。
如有 void printstar()
{…}
则,a=printstar(); 在编译时系统会给出出错信息。
151
一个函数中可以有一个以上的 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);
}
152
3,函数的调用
一般形式为,函数名 (实参表列 )
(实参与形参按顺序对应,一一传递数据。)
例 7.4
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实参求值按:自右至左顺序。
如果要传递自左至右顺序可采用如下格式:
j=i;
k=++i;
p=f(j,k);
153
函数的调用方式
函数语句。如:
printstar();
函数表达式。(即函数出现在表达式中。)
c=2*max(a,b);
函数参数。函数调用作为一个函数的实参。如:
m=max(a,max(b,c));
printf("%d",max(a,b));
几点说明:
被调用函数必须存在。
如果使用库函数,在文件开始用 include命令将调用有关库函数时所用到信息包含到本文件来。
154
如果使用自定义函数,一般应该在主调函数中对被调函数进行说明。一般格式为:
类型标识符 被调用函数的函数名 ( )
例 7.5
main()
{float add();
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);
}
对被调函数进行说明定义 add函数
155
如果在主调函数中没有对被调函数进行说明,系统自动按整型说明。
如果被调函数定义出现在主调函数之前,可以不进行说明。
如果以在所有函数之前,在文件的开头,在函数的外部已主调函数了函数类型,则在各个主调函数中不必对所调用的函数再进行类型说明。
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);}
主调函数不必说明 add函数。
例如:
156
4,函数的嵌套调用
C语言允许在调用一个函数的过程中,又调用另一个函数。这称为函数的 嵌套调用 。
main函数 a函数 b函数调用 a函数 调用 b函数结 束
157
5,函数的递归调用在调用一个函数的过程中又出现直接或间接地调用该函数本身。这称为函数的 递归调用 。
例如:
直接调用 间接调用
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);
}
158
递归函数的定义,不应出现无终止的递归调用。而应定义为在有限次数、有终止的递归调用函数。
n
i=1
n-1
i=1?i 可以定义为 n+?i
1 (n=0)
n*(n-1) (n>0)
1 (n=1)
n+?i (n>1)?i=
n
i=1
n-1
i=1
xn 可以定义为 x*xn-1 xn= 1 (n=0)x*x n-1 (n>0)
n! 可以定义为 n*(n-1)! n!=
递归定义式 边界条件对于一个问题,只要能够知道递归定义式,及边界条件 (即递归终止的条件 ),就可以编写一个递归函数。例如:
159
用递归函数的方法计算 s=83
main()
{ int pow();
printf("s=%d",pow(3));
}
int pow(int n)
{if (n>0) return(8*pow(n-1));
else return(1);
}
main()
printf("s=%d",pow(3))
调用 pow(3)
return(8*pow(2))
return(8*pow(1))
return(8*pow(0))
return(1)
返回 1
返回 8
返回 64
返回 512
512
显示,s=512
160
例 7.9 Hanoi(汉诺 )塔问题。有三根针 A,B,C。 A针上有 64盘子,盘子大小不等,大的在下,小的在上。 (要求把这 64个盘子从 A针移到 C针,在移动过程中可以借助 B针,每次只允许移动一个盘,且在移动过程中在三根针上都保持大盘在下,
小盘在上。 )
A B C
A C,A B,C B,A C,B A,B C,A C
161
将上面三个操作可分为以下两个操作:
将 n-1个盘子从一个针移动到另一个针上 (n>1)。
将 1个盘子从一个针上移动到另一个针上。
这是一个递归的过程,分别用两个函数实现以上两个操作:
用 hanoi(n,one,two,three)函数实现上面第一类操作。
用 move(getone,putone)函数实现上面第二类操作。
程序为:例 lx7_9.c
第一步,one A,two B,three C。
第三步,one B,two C,three A。
162
6,数组作为函数的参数
C语言允许用 数组元素作为实参,其用法与变量相同。同时也允许用 数组名做实参和形参 。
数组元素做函数实参用法与变量相同,也是“值传递”。例如 7.10
数组名做实参和形参数组名做实参和形参,是将 实参数组的起始地址传递给形参数组,这样两个数组就共占用同一段内存单元 。
例如:若实参数组 a的起始地址为 2000,则 传递给形参数组 b的值是地址 2000。
…… ……
2000a
b
163
例 7.11 有一个一维数组 score,内放 10个学生成绩,求平均成绩。
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);
}
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);
}
数组名做实参数组名做形参
164
用数组名做实参和形参,应注意以下几点:
在主调函数和被调用函数中分别定义数组。
实参数组与形参数组类型应一致。
实参数组与形参数组大小可以一致也可以不一致,C
编译对形参数组大小不作检查,只是将 实参数组的首地址传递给形参数组 。
实参和形参是地址传递,因而形参数组中各元素的值发生变化会使实参数组元素的值同时发生变化。
例 7.13 用选择法对数组中 10个整数由小到大排序。
选择法:先将 10个数中最小的数与 a[0]对换;再将 a[1]到 a[9]
中最小的数与 a[1]对换; ……,每比较一轮,找出一个未经排序数中最小的一个。共应比较 9轮。
165
void sort(array,n)
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;}
}
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
166
例 7.14 有一个 3*4的矩阵,求其中的最大元素。
max_value(array)
int array[ ][4];
{int i,j,k,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个元素的整型数组中的数据前后顺序颠倒。
167
7,局部变量和全局变量
局部变量在一个函数内部定义的变量是内部变量 (即局部变量 ),它只在本函数范围内有效,也就是在本函数才能使用它们,在次函数以外它们不存在,也就无法使用它们。
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有效形式参数也是局部变量。
168
C语言允许在一个复合语句的开始定义变量,这些变量只在本复合语句有效。
main()
{int a,b;
{int c;
c=a+b;
}
}
c有效 a,b有效
全局变量在函数外部定义的变量称为全局变量 (外部变量 )。全局变量可以为本文件中其它函数所共有。其有效范围为:从定义变量的位臵开始到本源文件结束。
169
int p=1,q=5;
float f1(a)
int a;
{int b,c;
}
char c1,c2;
char f2(x,y)
int x,y;
{int I,j;
}
main()
{int m,n;

例如:
全局变量
c1,c2
有效全局变量
p,q
有效外部变量 p,q说明外部变量
c1,c2说明
170
例 7.15 有一个一维数组,内放 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/10;
return(aver);
}
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);
}
score 10
array n aver
ave max min main函数
average函数
max min
171
全局变量增加了函数间数据联系的渠道。有时可以利用全局变量得到一个以上的返回值。但使用时应注意以下几点:
全局变量始终占用存储单元。
它使函数的通用性降低了。
如果全局变量定义之前的函数想引用该外部变量,可在该函数中用关键字 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;
外部变量说明外部变量定义
172
如果在同一个源文件中,外部变量与局部变量同名,
则在局部变量的作用范围内,外部变量不起作用。
例 7.17
int a=3,b=5;
max(a,b)
int a,b;
{int c;
c=a>b?a:b;
return(c);
}
main()
{int a=8;
printf("%d",max(a,b));
}
局部变量 a有效全局变量 b有效形参变量 a,b有效全局变量 a,b无效外部变量 a,b说明
173
8,动态存储变量与静态存储变量
变量的存储类别从变量值存在的时间来分,可以分为静态存储变量和动态变量。
静态存储:指在程序运行期间分配固定的存储空间。
动态存储:指在程序运行期间根据需要进行动态的分配存储空间。
内存中供用户使用的存储空间情况:
程 序 区静态存储区动态存储区全局变量和 static
说明的局部变量形参变量、未加 static说明的局部变量和函数调用时的现场保护和返回地址。
174
C语言变量的两个属性:
数据类型,如 int,float,char等。
数据的存储类别,静态存储和动态存储。具体包含四种:
auto 自动的,static 静态的,
register 寄存器的,extern 外部的。
局部变量的存储方式
函数中的局部变量,如不做专门的说明,都是在动态存储区分配存储空间。这类局部变量称为 自动变量,可用关键字
auto作存储类型说明。如:
int f(a)
int a;
{auto int b,c=3;
}
auto可以省略,系统按自动类型处理
175
用 static说明局部变量,称为 局部静态变量,在静态存储区分配存储空间。在调用函数结束后,其占据的存储单元不释放,下一次调用该函数,其值为上一次调用结束时的值。
例 7.18 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));
}
0 3
b c
1 40 5
第一次调用开始第一次调用结束第二次调用开始第二次调用结束第三次调用开始
1 6
第三次调用结束运行结果为,7,8,9
在编译时赋值只执行一次。
176
对局部静态变量的几点说明:
局部静态变量属于静态存储类别,在静态存储区内分配存储单元。
对 局部静态变量是在编译是赋初值,以后调用函数时不再重新赋初值。 而自动变量是在函数调用时才赋初值,每调用一次函数重新赋一次初值。
定义 局部变量如不赋初值,则对静态变量,系统在编译时自动赋初值 0或空字符 (/0)。对自动变量,它的值是一个不确定的值。
局部静态变量在函数调用结束后依然存在,但其它函数不能引用它。
寄存器变量:将局部变量的值放在运算器中的寄存器中,需要时直接从寄存器取出参与运算。用关键字 register作说明。例如:
177
例 7.20
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));
}
定义寄存器变量 i,j。
注,?只有局部自动变量和形式参数可以作为寄存器变量。
寄存器数目是有限的。
Turbo C对寄存器变量当作自动变量处理。
不能对寄存器变量进行取地址运算。
178
全局变量的存储方式全局变量在编译时分配在静态存储区。
一个C语言程序可以由一个或多个源程序文件组成。如果程序由一个源文件组成,则全局变量可以为程序中各个函数所引用。如果由多个源程序文件组成,则分为两种情况:
允许其它文件中的函数引用,但应在需要引用它的文件中,对引用变量用 extern作说明。例如:
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,m,c);
d=power(m);
printf("%d**%d=%d",a,m,d);
}
extern int a;
power(int n)
{int i,y=1;
for (i=1;i<=n;i++)
y*=a;
return(y);
}
file1.c file2.c
179
只被本文件中的函数引用。在定义外部变量时前面加一个 static说明。例如:
file1.c file2,c
static int a;
main()
{
}
extern int a;
fun(n)
int n;
{?
a=a*n;
}
不能使用 a
变量。
180
9,内部函数和外部函数
内部函数如果一个函数只能被本文件中其它函数所调用,称为内部函数。 (也称静态函数)
定义格式为:
static 类型标识符 函数名 (形参表 )
如,static int fun(a,b)
外部函数在定义一个函数时,冠以关键字 extern,则该函数是外部函数,可为本文件和其它文件中的函数调用。C语言规定,
在定义函数时,如果省略 extern关键字,隐含为外部函数。在调用此函数的文件中,一般要用 extern说明所用的函数是外部函数。例如:
181
例 7.22 有一个字符串,内有若干个字符,今输入一个字符,
程序将字符串中该字符删除。
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);
}
file2.c(文件 2)
#include stdio.h
extern exter_string(str)
char str[80];
{gets(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';
}
file4.c(文件 4)
extern print_string(str)
char str[ ];
{printf("%s",str);
}
182
函数应用举例
#include ―stdio.h‖
my_cmp(char s[],char t[])
{int i=0;
while(s[i]==t[i])
{if (s[i]==?\0‘) return(0);
i++;}
return(s[i]-t[i]);}
main()
{char a[20],b[20];
gets(a);gets(b);
printf("\n%d\n",my_cmp(a,b));
}
#include ―stdio.h‖
void fun(char a1[],char a2[],int n)
{int k;
for(k=0;k<n;k++)
a2[k]=(a1[k]-‘A‘-3+26)%26+‘A‘;
a2[n]=?\0‘;}
main()
{char s1[5]=―ABCD‖,s2[5];
fun(s1,s2,4);
puts(s2);}
运行结果,XYZAf
183
void trans(int num,int x,char a[])
{int r,i=0;
while(num!=0)
{ r=num%x; num=num/x;
switch(r)
{ case 10,a[i]='A';break;
case 11,a[i]='B';break;
case 12,a[i]='C';break;
case 13,a[i]='D';break;
case 14,a[i]='E';break;
case 15,a[i]='F';break;
default:a[i]=r+48;}
i++;}
}
main()
{int num,i,x;static char a[16];
scanf("%d%d",&num,&x);
trans(num,x,a);
printf("\n");
for(i=15;i>=0;i--)
printf("%c",a[i]);
printf("\n");}
用函数实现 10进制数向 2,8、
16进制数的转换
translat
184
#include ―stdio.h‖
mycat(char a[],b[])
{int i=0,j;
while(a[i]!=?\0‘) i++;
for (j=0;b[j]!=?\0‘;j++)
a[i+j]=b[j];
a[i+j]=?\0‘;}
C h i n a \0
t[0] t[1] t[2] t[3] t[4] t[5] t[6] t[7] t[8] t[9]
main()
{char s[18],t[10];
gets(s);gets(t);
mycat(s,t);
puts(s);}
s[0]s[1]s[2]s[3]s[4]s[5]s[6]s[7]s[8]s[9]s[10]s[11]s[12]s[13]s[14]s[15]s[16]s[17]
I l o v e \0
t
s b
a
mycat
185
如:初始数据,[47] 33 61 82 72 11 25 47
i=2(33) [33 47] 61 82 72 11 25 47
i=3(61) [33 47 61] 82 72 11 25 47
i=4(82) [33 47 61 82] 72 11 25 47
i=6(11) [11 33 47 61 72 82] 25 47
i=7(25) [11 25 33 47 61 72 82] 47
i=8(47) [11 25 33 47 47 61 72 82]
i=5(72) [33 47 61 72 82] 11 25 47
插入排序,每次对一个待排的数据,按其值的大小插入到前面已排好序的数据中。
186
main()
{int a[11],i,j,t;
printf("input 10 numbers:\n");
for (i=1;i<11;i++)
scanf("%d",&a[i]);
insert(a,11);
printf("\n");
printf("the sorted numbers:\n");
for (i=1;i<11;i++)
printf("%d ",a[i]);
printf(―\n‖);}
…… ……
2000b
a
void insert(int b[],int n)
{int i,j;
for (i=2;i<n;i++)
{b[0]=b[i];
j=i-1;
while (b[0]<b[j])
{b[j+1]=b[j];
j=j-1;}
b[j+1]=b[0];}
}
187
用函数实现冒泡法排序。
起泡法,对相邻两个数比较和交换,使全部数据排列有序。
如,9
8
5
4
2
0
8
9
5
4
2
0
8
5
9
4
2
0
8
5
4
9
2
0
8
5
4
2
9
0
8
5
4
2
0
9
第 1次 第 2次 第 3次 第 4次 第 5次 结果第一趟最大数沉到底部。
8
5
4
2
0
5
8
4
2
0
5
4
8
2
0
5
4
2
8
0
5
4
2
0
8
第 1次 第 2次 第 3次 第 4次 结果第二趟
5
4
2
0
8
9
188
5
4
2
0
4
5
2
0
4
2
5
0
4
2
0
5
第 1次 第 2次 第 3次 结果第三趟
4
2
0
2
4
0
2
0
4
第 1次 第 2次 结果第四趟
2
0
0
2
第 1次 结果第五趟
0
2
4
5
8
9
最后结果即:如果有 n个数,则要进行 n-1趟比较。在第 j趟比较中要进行 n-j次两两比较。
2
0
4
5
8
9
189
b[j]>b[j+1]
t=b[j]
b[j]=b[j+1]
b[j+1]=t
真 假
for j=0 to n-1-i
for i=0 to n-1
返回到主调函数传 n个数给 b数组
main()
{int a[10],i,j,t;void bubble(int,int);
for (i=0;i<10;i++)
scanf("%d",&a[i]);
printf("the sorted numbers:\n");
bubble(a,10);
for (i=0;i<10;i++)
printf("%d ",a[i]);
printf(―\n‖);
}
void bubble(int b[],int n)
{int i,j,t;
for (i=0;i<n-1;i++)
for (j=0;j<n-1-i;j++)
if (b[j]>b[j+1])
{t=b[j];b[j]=b[j+1];b[j+1]=t;}
}
190
选择法,通过比较首先选出最小的数放在第一个位置上,然后在其余的数中选出次小数放在第二个位置上,依此类推,直到所有的数成为有序序列。
第二趟排序后,0 2 [5 4 8 9]
第一趟排序后,0 [8 5 4 2 9]
第三趟排序后,0 2 4 [5 8 9]
第四趟排序后,0 2 4 5 [8 9]
第五趟排序后,0 2 4 5 8 [9]
最后排序结果,0 2 4 5 8 9
如:已知原始数据,[9 8 5 4 2 0]
函数应用举例
用函数实现选择法排序 (由小到大 )。
191
9
8
5
4
2
0
8
9
5
4
2
0
5
9
8
4
2
0
4
9
8
5
2
0
2
9
8
5
4
0
0
9
8
5
4
2
第 1次 第 2次 第 3次 第 4次 第 5次 结果第一趟最小数已得到。
9
8
5
4
2
8
9
5
4
2
5
9
8
4
2
4
9
8
5
2
2
9
8
5
4
第 1次 第 2次 第 3次 第 4次 结果第二趟
0
2
9
8
5
4
选择法排序的详细过程已知原始数据,9 8 5 4 2 0
前两趟的结果
192
9
8
5
4
8
9
5
4
5
9
8
4
4
9
8
5
第 1次 第 2次 第 3次 结果第三趟
9
8
5
8
9
5
5
9
8
第 1次 第 2次 结果第四趟
9
8
8
9
第 1次 结果第五趟
0
2
4
5
8
9
最后结果
0
2
4
5
9
8
如果有 n个数,则要进行 n-1趟选择。在第 i趟选择中要从第 i个位置到最后一个位置选择出一个最小数放到第 i个位置上;第
i次选择进行 n-i次比较。
193
void sort(int b[],int n)
{int i,j,min,t;
for (i=0;i<n-1;i++)
{min=i;
for (j=i+1;j<n;j++)
if (b[min]>b[j]) min=j;
if (min!=i)
{t=b[min];b[min]=b[j];b[j]=t;} } }
b[i]>b[j]
t=b[i]
b[i]=b[j]
b[j]=t
真 假
for j=i+1 to n
for i=0 to n-1
返回到主调函数传 n个数给 b数组
2000
b
a a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
b[0] b[1] b[2] b[3] b[4] b[5] b[6] b[7]b[8] a[9]
main()
{int a[10],i;
for (i=0;i<10;i++)
scanf("%d",&a[i]);
sort(a,10);
for (i=0;i<10;i++)
printf("%d ",a[i]);
printf(―\n‖);}
194
这样通过逐步缩小查找范围,直到找到或找不到数据
k为止 。
用函数实现在一组排好序 (递增有序或递减有序 )的数值中查找某个数据。
首先将待查找数据 k与排好序(递增有序)的一组数据的 中间位置 上的数据进行比较,若相等,则查找成功;
若 k>a[mid],则 k只可能出现在后半部 a[mid+1,…,n]
中,则应在这个后半部中再进行折半查找;
若 k<a[mid],则 k只可能出现在前半部 a[1,…,mid-1]
中,则应在这半部中再进行折半查找;
折半查找的基本思想
195
1,05,13,19,21,37,56,64,75,80,88,92

beg mid end
2,05,13,19,21,37,56,64,75,80,88,92

beg mid end
3,05,13,19,21,37,56,64,75,80,88,92

beg end
mid
1 116
1 53
4 5
查找成功 !
原始数据,05,13,19,21,37,56,64,75,80,88,92
查找 k=21的过程
196
1,05,13,19,21,37,56,64,75,80,88,92

beg mid end
2,05,13,19,21,37,56,64,75,80,88,92

beg mid end
3,05,13,19,21,37,56,64,75,80,88,92

beg end
mid
111 6
7 119
4,05,13,19,21,37,56,64,75,80,88,92
end beg
10 11

beg>end,查找不成功查找 85
9 10
197
main()
{int i,a[10],num;
printf(―Please input 10 sorted numbers:\n‖);
for (i=0;i<10;i++)
scanf(―%d‖,&a[i]);
printf(―\n‖);
printf(―Input the number to be searched:‖);
scanf(― %d‖,&number);
printf(―\n‖);
if (bisearch(a,0,9,number)==0 )
printf(―%d is not found\n‖,number);
else
printf(―%d is the %dth number ‖,number,find);
}
bisearch(int a[],int beg,int end,int num)
{int mid,find=0;
while((find==0)&&(beg<=end))
{mid=(beg+end)/2;
if (num==a[mid])
find=mid+1;
else if (num<a[mid])
end=mid-1;
else
beg=mid+1;}
return(find);
}
198
a[0][0]
a[0][1]

a[0][19]
a[1][0]
a[1][1]
a[1][19]
a[2][0]
a[2][1]

a[2][19]
a[0]
a[1]
a[2]


a[0]
a[1]
a[2]
a[0]
a[1]
a[2]
a[3]
a[4] a m e r i c a \0 …
d e n m a r k \0 …
c h i n a \0 …
b r i t a i n \0 …
g r e e c e \0 …
a 0 1 2 3 4 5 6 7 8 9 … 19
如何把选择法和折半查找应用到字符串的排序和查找上?
199
void sort(char na[][20],int n)
{int i,min; char t[20];
for(i=0;i<n;i++)
{min=i;
for(j=i+1;j<n;j++)
if (strcmp(na[min],na[j])>0)
min=j;
if (min!=i)
{strcpy(t,na[i]);strcpy(na[i],na[min]);strcpy(na[min],t); }
}
void sort(int b[],int n)
{int i,min,t;
for (i=0;i<n-1;i++)
{min=i;
for (j=i+1;j<n;j++)
if (b[min]>b[j]) min=j;
if (min!=i)
{t=b[i];b[i]=b[min];b[min]=t;}
}}
200
int search(char str[][20],int from,int to,char name[])
{int i,mid,find=0;
while((find==0)&&(from<=to))
{mid=(from+to)/2;
if (strcmp(str[mid],name)==0) find=mid+1;
else if(strcmp(str[mid],name)>0) to=mid-1;
else from=mid+1; }
return(find);
}
sortsea
a 0
str from to
5 country
main函数
search函数
name find
position
201
a[0][0]
a[0][1]

a[0][19]
a[1][0]
a[1][1]
a[1][19]
a[2][0]
a[2][1]

a[2][19]
a[0]
a[1]
a[2]


na[0]
na[1]
na[2]
a[0]
a[1]
a[2]
a[3]
a[4] a m e r i c a \0 …
d e n m a r k \0 …
c h i n a \0 …
b r i t a i n \0 …
g r e e c e \0 …
na
main()
{char a[5][20],country[20];int position,i;
for(i=0;i<5;i++) gets(a[i]);
sort(a,5);
for(i=0;i<5;i++) printf("%s ",a[i]);
printf("\ninput:"); gets(country);
if (position=search(a,0,4,country)) printf(―\n%d\n",position);
else printf(―\nnot found‖);
}
202
对上述问题的进一步讨论如果用户把国家名称输入错了,程序能自动纠正用户的拼写错误,找到一个最接近用户输入的国家名,提示给用户,而不是仅仅告诉用户“未找到”。
可以纠正的拼写错误包括,1、漏掉或多写了一个字母; 2、某一个字母被拼错了; 3、一对字母交换了位置。
方法,增加一个 spname函数,它能够确定两个字符串相差多少?
very rough spelling metric:
0 if the strings are identical
1 if two chars are transposed
2 if one char wrong,added or deleted
3 otherwise
example
203
1,如何对一批数值性数据进行排序?
2.如何在一 批 排好序的数值 性数据 中查找某个数据?
3.如何把 对一批数值性数据进行排序的算法应用到字符串的排序上?
4,如何在一组排好序的 字符串 中查找某个 字符串?
5,怎样使得查找过程具有一定的智能?
今日要点
204
#include"stdio.h"
#include"string.h―
#define N 6
#define EQ(s,t) (strcmp(s,t)==0)
void sort(char name[][20],int n)
{ char t[20],i,j,min;
for(i=0;i<n;i++)
{ min=i;
for(j=i+1;j<n;j++)
if(strcmp(name[min],name[j])>0) min=j;
if(min!=i) {strcpy(t,name[i]);strcpy(name[i],name[min]);strcpy(name[min],t);}
}
}
205
int search(char str[][20],int from,int to,char name[],char best[],int *d,int *num)
{int i,j,mid,find=0,nd;
while(from<=to)
{mid=(from+to)/2;
if ((nd=spname(str[mid],name))==0 {find=mid+1;*d=nd;*num=mid+1;break;}
else if (strcmp(str[mid],name)>0)
{to=mid-1;
if(nd<=*d&&nd!=3)
{strcpy(best,str[mid]);*num=mid+1;*d=nd; }}
else {from=mid+1;
if(nd<=*d&&nd!=3)
{strcpy(best,str[mid]);*num=mid+1;*d=nd;}}
}
return(find); }
206
int spname(char *s,char *t)
{while(*s++==*t) if(*t++=='\0') return(0);
if(*--s)
{if(*t)
{if (s[1]&&t[1]&&*s==t[1]&&*t==s[1]&&EQ(s+2,t+2))
return(1);
if (EQ(s+1,t+1))
return(2);}
if(EQ(s+1,t))
return(2); }
if (*t&&EQ(s,t+1))
return(2);
return(3);}
207
main()
{char country[20],guess[20],a[N][20],co='y'; int position,*nd,b,*num,c,i;nd=&b;
printf("\n\nPlease input %d countries to be sorted:\n",N);
for(i=0;i<N;i++) gets(a[i]); sort(a,N); printf("\nThe sorted countries are:");
for(i=0;i<N;i++) printf(" %s",a[i]);
while(co=='y')
{printf("\nPlease input the country you are going to search:");
gets(country);*nd=3;num=&c;
if (position=search(a,0,N-1,country,guess,nd,num))
printf("\n%s is the %dth\n",country,position);
else {if (*nd<3) printf("You may wanted to search %s.It is the %dth.",guess,*num);
else printf("Your input is wrong");}
printf("\nContinue(y/n?):"); co=getchar();getchar(); } gaisearch
}
208
第八章 编译预处理
C语言提供三种预处理功能:
宏定义;
文件包含;
条件编译。
分别用 宏定义命令,文件包含命令 和 条件编译命 令来实现。这些命令以符号,#‖开头,结尾没有分号 (;)。 该命令的 作用域 是从定义的地方开始到源文件结束。
在C编译系统对程序进行通常的编译之前,先对程序中这些命令进行“预处理”。然后将结果和源程序一起再进行编译处理,最后得到目标代码。
209
1,宏定义
不带参数的宏定义功能:用一个指定的标识符 (宏名 )来代表一个字符串。
一般形式,#define 标识符 任意字符串序列例如:
#define PI 3.1415926
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*3.1415926*r;
s=3.1415926*r*r;
v=4.0/3*3.1415926*r*r*r;
printf("l=%f\ns=%f\nv=%f\n",l,s,v);
}
预处理
210
说明:
宏定义是用宏名代替一个字符串,也就是作简单的置换,不作语法检查。如:
#define PI 3.1415926p;
area=PI*r; 预处理后为 area=3.1415926p;*r
宏名的有效范围为定义命令之后到本源文件结束。可以用 #undef命令终止宏定义的作用。如:
# define G 9.8
main()
{?
}
#undef G
f1()
{?
}
G有效范围
211
在进行宏定义时,被定义过的宏名可被重新定义,也可以引用已定义的宏名。如:
#define PI 5.6
#define R 3.0
#define PI 3.1415926
#define L 2*PI*R
#define S PI*R*R
main()
{printf("L=%f\nS=%f\n",L,S);
}
main()
{
printf("L=%f\nS=%f\n",2*3.1415926*3.0,3.1415926*3.0*3.0);
}
用双引号括起来的字符串内的字符,即使与宏名相同,
也不进行置换。如 L,S
212
area=3*2
带参数的宏定义进行字符串替换,还要进行参数替换。
定义形式为,#define 宏名 (参数表 ) 字符串包含括弧中指定的参数如,#define S(a,b) a*b
area=S(3,2);
注意:不能写成 #define S (a,b) a*b
不能有空格
213
例 8.3
#define PI 3.1415926
#define S(r) PI*r*r
main()
{float a,area;
a=3.6;
area=S(a);
printf("r=%f\narea=%\n",a,area);
}
预处理
main()
{float a,area;
a=3.6;
area=3.1415926*a*a;
printf("r=%f\narea=%\n",a,area);
}
如果有以下语句,
area=S(x+y);
预处理后结果为? area=3.1415926*x+y*x+y;
#define S(r) PI*(r)*(r) area=3.1415926*(x+y)*(x+y);
214
2.―文件包含”处理文件包含处理是指一个源文件可以将另一个源文件的全部内容包含进来。
一般形式为,#include "文件名 "
#include "file2.c "
A
file1.c
B
file2.c
file1.c
B
A
215
文件包含是可以嵌套的:
#include "file2.c " #include "file3,h "
file1.c file2.c file3.h
#include "file2.c "
#include "file3,h "
file1.c file2.c file3.h
也可按如下方式:
file3.h
file2.c
file1.c
编译后文件 1为
file3中的全局变量,file2、
file1可直接使用,不必用
extern说明。
另:一个 #include只能包含一个文件。
216
3.条件编译条件编译是指让编译程序根据条件有选择地对源程序的一部分进行编译。
C语言提供三组条件编译命令,其形式如下:
#if 标识符程序段 1
#else
程序段 2
#endif
#ifndef 标识符程序段 1
#else
程序段 2
#endif
若标识符 存在 编译程序段 1,否则编译程序段 2
若标识符 不存在 编译程序段 1,否则编译程序段 2
217
#if 表达式程序段 1
#else
程序段 2
#endif
若 表达式为真 编译程序段 1,否则编译程序段 2
例 8.8 #define LETTER 1main()
{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);
}
}
218
第九章 指 针
1.指针的概念指针:变量的地址。
指针变量:存放另一个变量地址的变量。
内存变量 i
变量 j
变量 k
变量 i_pointer
3
6
9
2000
2002
2004
20003010
219
2.变量的指针和指向变量的指针变量
指针变量的定义:
类型说明符 *标识符指针变量的名字―*‖表示后面是一个指针变量例如,int *p1,*p2;
float *p3,*p4;
char *c1,*c2;
注意,一个指针变量只能指向同一个类型的变量 。
如,p3,p4只能指向 float型数据。
220
指针变量的引用两个有关的运算符,& 取地址运算符
* 指针运算符(间接访问)
指针变量中只能存放地址,不能将其它类型的数据值赋给一个指针变量。
例如,若有 int *pointer_1,a;
则可 pointer_1=&a; *pointer_1=100;
不能有 pointer_1=100;
pointer_1 a
*pointer_1&a 100
221
例 9.1 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);
}
下面的表达式含义是什么?
&*pointer_1
*&a
(*pointer_1)++
*pointer_1++
&a
a
a++
pointer_1 a
*pointer_1&a 100
222
例 9.2 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);
}
p
p1
p2
a
b
&a
&b
&a
&b 5
&a 9
223
指针变量作为函数参数指针变量作为函数的参数,是将一个变量的地址传送到另一个函数中。
例 9.3 swap(p1,p2)int *p1,*p2;
{int p;
p=*p1;
*p1=*p2;
*p2=p;
}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);
}
a
b
pointer_1
pointer_2
5
9
&a
&b
p1
p2
p
&a
&b
5
9
5
结果为,9,5
224
是否可将交换 *p1和 *p2的值写为:
swap(p1,p2)
int *p1,*p2;
{int *p;
*p=*p1;
*p1=*p2;
*p2=*p;
}
main()
{int a,b;
scanf("%d,%d",&a,&b);
if (a<b) swap(&a,&b);
printf("\n%d,%d\n",a,b);
}
swap(p1,p2)
int p1,p2;
{int p;
p=p1;
p1=p2;
p2=p;
}
main()
{int a,b;
scanf("%d,%d",&a,&b);
if (a<b) swap(a,b);
printf("\n%d,%d\n",a,b);
}
225
swap(p1,p2)
int *p1,*p2;
{int *p;
p=p1;
p1=p2;
p2=p;
}
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);
}
pointer-1
pointer-2 b
&a
&b
&a 5
9
ap1
p2
&a
b
&a&b 5
9
ap1
p2b
&a
&b
5
9
a
(1) (2) (3) (4)
pointer-2 b
&a
&b
&b 5
&a 9
pointer-1 a
226
例 9.4 输入 a,b,c三个整数,按大小顺序输出。
swap(pt1,pt2)
int *pt1,*pt2;
{int p;
p=*pt1;
*pt1=*pt2;
*pt2=p;
}
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);
}
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);
}
227
总 结如果想通过函数调用得到 n个要改变的值,可以用下列方法:
( 1)在主调函数中定义 n个变量,用 n个指针变量指向它们;
( 2)然后将指针变量做实参,将 n个变量的地址传给所调用的函数的形参;
( 3)通过形参指针变量,改变形参指针变量指向的 n个变量;
( 4)主调函数中就可以用这些改变了值的变量。
注意:
不能通过改变形参指针变量的值而使实参指针变量的值也发生改变。
228
3.数组的指针和指向数组的指针变量数组的指针就是数组的起始地址。数组元素的指针是数组元素的地址。
指向数组元素的指针变量的定义与赋值方法同指向变量的指针变量一样。例如:
int array[10];
int *p;
p=&array[2];
array
&array[2]
p
array[0]
array[1]
array[2]
array[3]
array[4]
array[5]
array[6]
array[7]
array[8]
array[9]
229
C语言规定:数组名代表数组的首地址。也就是数组第一个元素的地址。
因而可以有:
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]
array&array[0]
p
230
通过指针引用数组元素
C语言规定:如指针 p指向数组的一个元素,则 p+1指向数组的下一个元素。
array[0]
array[1]
array[2]
array[3]
array[4]
array[5]
array[6]
array[7]
array[8]
array[9]
array&array[0]
p
&array[1]
p+1
p+i?
p+i*d
如果有 p=a;则 p+i和 a+i就是 a[i]的地址 。
*(p+i)或 *(a+i)是 p+i或 a+i所指向的 数组元素 。
231
C语言允许指针变量带下标,如 p[i] 等价于 *(p+i)。
对于数组元素的引用:可以使用 下标法,也可以用 指针法例 9.5 输出数组全部元素。
main()
{int a[10];
int i;
for (i=0;i<10;i++)
scanf("%d",&a[i]);
printf("\n");
for (i=0;i<10;i++)
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");
for (i=0;i<10;i++)
printf("%d",a[i]);
}
下标法 数组名 指针变量注意:不能有
a++
232
例 9.6 输出 a数组的 10个元素。
mian()
{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)
是如何运算?
233
数组名作函数参数用函数名作函数的参数,是将实参数组的首地址传递给形参。
例 9.7 将数组 a中 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;
}
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");
for (i=0;i<10;i++)
printf("%d",a[i]);
printf("\n");
}
234
void inv(x,n)
int *x,n;
{int *p,t,*i,*j,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;
}
array[0]
array[1]
array[2]
array[3]
array[4]
array[5]
array[6]
array[7]
array[8]
array[9]
a
i,x
p=x+m
j
将形参 x定义为指针:
例 9.8 从 10个数中找出其中最大值和最小值。程序如下:
请分析两个程序的运行结果是否一样?
235
max_min(a,n,max,min)
int a[],n,*max,*min;
{int i;max=&a[0];min=&a[0];
for(i=1;i<n;i++)
{if (*max<a[i]) *max=a[i];
if (*min>a[i]) *min=a[i];}}
main()
{int a[10],i,*p,*q,ma,mi; p=&ma;q=&mi;
for (i=0;i<10;i++)
scanf("%d",&a[i]);
max_min(a,10,p,q);
printf("%d,%d ",*p,*q);}
max_min(a,n,max,min)
int a[],n,*max,*min;
{int i; *max=a[0];*min=a[0];
for(i=1;i<n;i++)
{if (*max<a[i]) *max=a[i];
if (*min>a[i]) *min=a[i];}}
main()
{int a[10],i,*p,*q,ma,mi; p=&ma;q=&mi;
for (i=0;i<10,i++)
scanf("%d",&a[i]);
max_min(a,10,p,q);
printf("%d,%d ",*p,*q);}
236
如果有一个实参数组,则实参与形参的可以有以下 4种情况:
形参和实参都用数组名。
实参用数组名,形参用指针变量。
实参和形参都用指针变量。
实参用指针变量,形参用数组名。
例 9.10 用选择法对 10个整数排序。
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("%5d",*p);p++;
}
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;}
}}
237
sort1(int a,int b,int c)
{ int t;
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;} }
sort2(int *pa,int*pb,int*pc)
{int *p;
if (*pa>*pb){p=pa;pa=pb;pb=p;}
if (*pa>*pc){p=pa;pa=pc;pc=p;}
if *pb>*pc){p=pb;pb=pc;pc=p;} }
sort3(int *pa,int *pb,int *pc)
{int p;
if (*pa>*pb){p=*pa;*pa=*pb;*pb=p;}
if (*pa>*pc){p=*pa;*pa=*pc;*pc=p;}
if (*pb>*pc){p=*pb;*pb=*pc;*pc=p;} }
main()
{int x,y,z,*p1,*p2,*p3; x=17;y=24;z=8; p1=&x;p2=&y; p3=&z;
sort1(*p1,*p2,*p3);
printf("%d,%d,%d %d,%d,%d\n",x,y,z,*p1,*p2,*p3);
sort2(p1,p2,p3);
printf("%d,%d,%d %d,%d,%d\n",x,y,z,*p1,*p2,*p3);
sort3(p1,p2,p3);
printf("%d,%d,%d %d,%d,%d\n",x,y,z,*p1,*p2,*p3);}
238
#include ―stdio.h‖
swap1(char a,char b)
{char c;
c=a;a=b;b=c;}
swap2(char *a,char b)
{char c;
c=*a;*a=b;b=c;}
swap3(char * a,char *b)
{char c ;
c=*a;*a=*b;*b=c;}
main()
{ char a,b;
a=?A‘;b=?B‘;
swap1(a,b);
putchar(a);putchar(b);
swap2(&a,b);
putchar(a);putchar(b);
swap3(&a,&b);
putchar(a);putchar(b);
}
239
指向多维数组的指针和指针变量
二维数组的地址:
设 static int a[3][4]={{1,2,3,4},{5,6,7,8},{1,1,2,3}}
则有,1 2 3 4
5 6 7 8
1 1 2 3
a
a+2
a+1 a[0]
a[1]
a[2]
a[0]+1 a[0]+2 a[0]+3
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]
240
1,a是二维数组,代表整个二维数组的首地址,即第 0行的首地址,a+1代表第 1行的首地址,a+i代表第 i行的首地址。
2,a[0],a[1],a[2]是一维数组名,它代表一维数组的首地址,a[0]
代表第 0行第 0列元素的地址,即 &a[0][0]。
3,由 2知第 0行第 0列元素的地址可用 a[0]+0来表示( &a[0][0])。
a[i]+j表示第 i行第 j列元素的地址。
4,从逻辑上讲,a[0]与 *(a+0)等价,*(a+i)就是 a[i],因此 a[i]+j和
*( a+i)+j的值都是 &a[i][j]。
5,由 4知,a[0][1]的地址,可用 a[0]+1或 *(a+0)+1来表示,这样
a[0][1]= *( a[0]+1)或 *( *(a+0)+1)。
6,从逻辑上讲,&a[i]代表 a[i]的物理地址,而 a[i]不是实际的元素,
&a[i]是第 i行的首地址,因此 &a[i]和 a[i]的值是一样的。但含义却不一样,&a[i]或 a+i指向第 i行,而 a[i]或 *(a+i)指向列。
241
以下表示形式的含义为什么:
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和 *(a+i)的不同。
在一维数组 a+i为地址,*(a+i)为值。
在二维数组 a+i和 *(a+i)都为地址。
242
例 9.11
#define FORMAT "%d,%d\n"
main()
{static int a[3][4]={1,3,5,7,9,11,13,15,17,19,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
158,158
158,158
166,166
166,166
174,174
174,174
9,9
运行结果
243
多维数组的指针可以用指针变量指向多维数组及其元素。有两种方式:
指向数组元素的指针变量。
例 9.12 用指针变量输出二维数组的元素。
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);
}
}
2000
2002
2004
2006
2008
2010
1
2
3
4
5
6
a[0]
a[1]
2000
p
200220042006200820102012
244
数组元素在数组中的相对位臵 (偏移量 ):
二维数组 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)。
245
例 9_1.c 定义一个指向数组元素的指针,将一个二维 数组按行列的格式显示出。
main()
{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");
}
}
运行结果:
1 2 3 4
5 6 7 8
9 0 1 2
表达式 p+i*4+j表示第 i行第 j列元素的地址,
因此 *(p+i*4+j)就是元素 a[i][j]。
246
指向由 m个整数组成的一维数组的指针变量。
指针定义形式如下:
类型标识符 (*标识符 )[所指数组元素个数 ]
例如,int (*p)[4];
表示变量 p是指向有 4个元素的一维整型数组的指针变量。
不能省略例 9.13 输出二维数组任一列元素的值。
main()
{static int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4],i,j;
p=a;
scanf("%d,%d",&i,&j);
printf("\na[%d,%d]=%d\n",i,j,*(*(p+i)+j));
}
a[0]
a[1]
a[2]
p,a
p+1
p+2
若输入,1,2↙
则输出为:? 6
247
如果 p是指向二维数组 a中第 i行的指针变量,也就是说 p
所指向的内容是一维数组 a[i],则 *p? a[i]? &a[i][0]。 p
和 *p的值相同 (因为第 i行的地址和元素 a[i][0]的地址相等 ),
但含义不同 (即所指向的内容不同 )。
p是指向二维数组 a中第 i行的指针变量,则 p+1是指向数组 a中第 i+1行的指针。即 p的变化是以,行,为单位的。
如果有如下定义:
int (*p)[4],a[3][4];
p = a;
则有以下表达式的等价关系:
p+i? a+i? &a[i],.....数组 a中第 i行的地址
*p? a[0]? &a[0][0],.....数组 a中第 0行的首地址
*(p+i)? a[i]? &a[i][0],.....数组 a中第 i行的首地址
*(p+i)+j? &a[i][j],.....第 i行第 j列元素的地址
*p+i? &a[0][i],.....第 0行第 i列元素的地址
248
例 9_2.c 输出二维数组的元素 。
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
*p
p
*p
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
main()
{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指向 a中的第 i行
*p表示第 i行的首地址,
*p+j表示第 i行第 j列元素的地址,
*(*p+j)即为 a[i][j]。
249
如果有如下定义:
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)。
通过指针变量存取数组元素速度快,且程序简明。用指针变量作形参,可以允许数组的行数不同。因此数组与指针紧密联系。
250
例,一个班 3名同学,各学 4门课,计算总平均分数,以及第 n个学生的成绩。。
main()
{void average(); void search();
static float score[3][4]={{},{},{},{}};
average(*score,12);
search(score,2);
}
void search(p,n)
float (*p)[4];int n;
{int i;
printf(―the score of no.%d are:‖,n);
for (i=0;i<4;i++)
printf(―%5.2f \n‖,*(*(p+n)+i));}
void average(p,n)
float *p;int n;
{float *p_end;
float sum=0,aver;
p_end=p+n-1;
for (;p<=p_end;p++)
sum=sum+(*p);
aver=sum/n;
printf(―average=%5.2f\n‖,aver);}
251
例:在上一题的基础上,查找有一门以上课程不及格的学生,打印出他们的全部课程的成绩。
main()
{void search(); float score[3][4];
for (i=0;i<3;i++)
for (j=0;j<4;j++)
scanf(―%d‖,&a[i][j]);
search(score,3);
}
void search(p,n)
float (*p)[4];int n;
{int i,j,flag;
for (j=0;j<n;j++)
{flag=0;
for (i=0;i<4;i++)
if *(*(p+j)+i))<60) flag=1;
if (flag==1)
{printf(―no.%d is fail,his score are,\n‖,j+1);
for (i=0;i<4;i++)
printf(―%5.2f \n‖,*(*(p+j)+i));
printf(―\n‖);}
}}
252
4.字符串的指针和指向字符串的指针变量
字符串的表示形式在C语言程序中,可以用 字符数组 或 字符指针 两种方法实现一个字符串。
main()
{static char a[ ]="I love China!";
printf("%s\n",a);
}
main()
{char *p="I love China!";
printf("%s\n",p);
}
字符数组法 字符指针法
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 ! \02000
I l o v e C h i n a ! \0
p
实际上在字符指针用法中,C语言在内存开辟了一个字符数组用来存放字符串常量,并把字符串首地址赋给字符指针变量 p。即指针变量 p中存放的是一个指向字符串,I love China!‖
的地址,而不是字符串,I love China!‖。
253
注意,char *p=―I love China‖;
等价于 char *p;
p=―I love China‖;
不等价于 char *p;
*p=―I love China‖;
对于字符串中字符的存取,可以用下标法、地址法和指针法。例 9.18 将字符串 a复制到字符串 b中。
main()
{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]);
}
地址法下标法
254
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]);
}
指针法
a[0]
a[1]
a[2]
1[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
a[10
a[11
a
b[0]
b[1]
b[2]
b[3]
b[4]
b[5]
b[6]
b[7]
b[8]
b[9]
b[10
b[11
b
I
am
a
b o
y,
\0
p1 p2p1
p1
p1p1
p1p1
p1
p1p1
p1
p2
p2
p2p2
p2p2
p2
p2p2
p2
I
am
a
b
oy
.p1 p2
\0
255
字符指针变量与字符数组字符数组和字符指针变量都能实现字符串的存储和运算,
但应注意二者之间的区别。
字符数组由若干个元素组成,每个元素中放一个字符,
而字符指针变量中存放的是地址。
赋初值方式:
数组,static char str[]={―I love China!‖};
字符指针变量,char *p=―I love China!‖;
赋值方式:字符指针变量可以,而数组不行。
如有,char str[14],*p;
则,p=―I love China!‖; 可以。
而,str=―I love China!‖; 不行。
256
数组在编译时分配内存单元,而指针变量只分配一个存放地址的单元。 若未给指针变量赋一个地址,则它并没有具体指向那一个字符数据 。
使用下面方法就可能产生严重的错误。
char *p;
scanf(―%s‖,p);
指针变量的值可以改变,而数组名代表的值不可改变。
如,p++可以 而,a++不行 (p为指针,a为数组名 )。
用指针变量指向一个格式字符串,可以用它代替 printf
函数中的格式字符串。例如:
P指向一个不定的地址
char *p;
p="a=%d,b=%f\n";
printf(p,a,b);
等价于 printf("a=%d,b=%f\n",a,b);
257
字符串指针作函数参数将一个字符串从一个函数传递到另一个函数,可以用字符数组名作参数,也可以用指向字符串的指针变量作参数。
归纳起来,有以下 4种情况:
实参 形参
数组名 数组名
数组名 字符指针变量
字符指针变量 字符指针变量
字符指针变量 数组名
258
void copy_string(from,to)
char from[],to[];
{int i=0;
while (fron[i]!=?\0‘)
{to[i]=fron[i];i++;}
to[i]=?\0‘;}
main()
{char a[]=―Iam a teacher.‖;
char b[]=―You are a student.‖;
printf(―string_a=%s\n string_b=%s\n‖,a,b);
copy_string(a,b);
printf(―string_a=%s\n string_b=%s\n‖,a,b);}
例 9.20 用函数调用实现字符串的复制。
void copy_string(from,to)
Char *from,*to;
{for (;*from!=?\0‘;from++,to++)
*to=*fron;
*to=?\0‘;}
main()
{char *a=―Iam a teacher.‖;
char *b=―You are a student.‖;
printf(―string_a=%s\n string_b=%s\n‖,a,b);
copy_string(a,b);
printf(―string_a=%s\n string_b=%s\n‖,a,b);}
259
#include "string.h"
#include "stdio.h"
main()
{ char a[10],b[10] ; char *s=a,*t=b;
gets(s);gets(t);
while(*s++==*t)
if(*t++=='\0')
{ printf("\n%d\n",0); exit(0);}
if(*--s-*t>0)
printf("\n%d\n",1);
else
printf("\n%d\n",-1);
}
例 strcmp
260
例 用函数实现在数组 w 中插入 x。 W数组已经按由小到大的顺序排列。
void insert(int *w,int x,x,int *n)
{int i,p;
p=0;
w[*n]=x;
while (x>w[p]) p++;
for (i=*n;i>p;i--)
w[i]=w[i-1];
w[p]=x;
++*n;}
261
5.函数的指针和指向函数的指针变量一个函数在编译时被分配给一个入口地址,这个入口地址就称为函数的指针。可以定义一个指针变量指向函数,该指针称为指向函数的指针变量。其定义形式如下:
函数返回值的类型例如,int (*p)( );
char (*p1)( );
p=max;
main
max
min定义 p和 p1为指向函数的指针变量。 p指向一个带整型返回值的函数,p1指向一个带字符型返回值的函数。
数据类型标识符 (*指针变量名 )( )
max
p
262
用函数指针变量调用函数例 9.23 求 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--等运算无意义,
263
把指向函数的指针变量作函数参数例如,
sub(x1,x2)
int (*x1)(),(*x2)();
{int a,b,i,j;
a=(*x1)(i);
b=(*x2)(i,j);
}
sub(f1,f2);则可有
(f1和 f2为函数名 )
当每次调用的函数不是固定的时,用指针变量就比较方便。
264
main()
{int max(),min(),add();
int a,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);}
add(x,y)
int x,y;
{int z;
z=x+y;
return(z);}
process(x,y,fun)
int x,y,(*fun)();
{int result;
result=(*fun)(x,y);
printf(―%d\n‖,result);}
例:设一个函数 process,在调用它的时候,每次实现不同的功能。
输入 a,b,分别求 a,b中的大数,a,b中的小数和 a,b之和。
265
6.返回指针值的函数一般定义形式为,
类型标识符 *指针变量名 ( 参数表 )
例如,int *a(x,y);
例 9.26 有若干个学生的成绩(每个学生有 4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。
266
main()
{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);
}
60 70 80 90
56 89 67 88
34 78 90 66
pointer
pt
267
7.指针数组和指向指针的指针
指针数组的概念一个数组,其元素均为指针类型数据,称为指针数组。
定义形式为:
类型标识 *数组名 [数组长度说明 ]
例如,int *p[5];
char *name[5];
name[0]
name[1]
name[2]
name[3]
name[4]
Follow me
basic
great Wall
FORTRAN
Computer design
268
main()
{static char *name[ ]={"follow me","basic",
"great wall","fortran","computer design"}
int n=5; sort(name,n); print(name,n);}
void sort(name,n)
char *name[ ];int n;
{char *temp;int i,j,k;
for (i=0;i<n-1;i++)
{k=i;
for(j=i+1;j<n;j++)
if (strcmp(name[k],name[j])>0) k=j;
if (k!=i)
{temp=name[i];name[i]=name[k];name[k]=temp;}}}}
void print(name,n)
char *name[];int n;
{int i;
for(i=0;i<n;i++)
printf(%s\n‖,name[i]);}
name[0]
name[1]
name[2]
name[3]
name[4]
Follow me
basic
great Wall
FORTRAN
Computer design
name
269
#include "stdio.h"
main()
{static int a[]={2,6,10,14,18}; int **p,i;
static int *ptr[]={&a[0],&a[1],&a[2],&a[3],&a[4]};
for(i=0;i<5;i++)
a[i]=a[i]/2+a[i];
p=ptr;
printf("%d\n",*(*(p+2)));
printf("%d\n",*(*(++p)));}
pointer
270
指向指针的指针定义形式为,类型标识 **标识符例如,char **p;
p=name+2;

name[0]
name[1]
name[2]
name[3]
name[4]
Follow me
basic
great Wall
FORTRAN
Computer design
name
name+2
p
printf("%o\n",*p);
printf("%s\n",*p); 结果为?
271
例 9.29
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);}
}
name[0]
name[1]
name[2]
name[3]
name[4]
Follow me
basic
great Wall
FORTRAN
Computer design
name
p
name+01234
Follow me
basic
great Wall
FORTRAN
Computer design
272
第十章 结构体与共用体
1.结构体结构体是将不同的数据类型组织在一起而形成的一个有机的整体,C语言提供的结构体类型就是这样一个整体。
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
10010 Li Fun F 18 87.5 Xi‘an
num name sex age score addr
struct是关键字,student
是定义的类型名。 struct student
是定义一个结构体类型,它包括了
num,name,sex,age,score,addr不同类型的数据项。
例如:
273
定义结构体类型的一般形式:
struct 结构体名
{成员 1;
成员 n;
};
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
例如每个成员的定义格式:
类型标识符 成员名定义一个结构体类型
struct student
274
2.定义结构体类型变量的方法先定义结构体类型再定义变量。
有三种定义方法,在定义类型的同时定义变量。
直接定义结构类型变量。
num name sex age score addr
s1
s2
先定义结构体类型再定义变量例如上面定了一个结构体类型 struct student,可以用它来定义变量。如:
struct student s1,s2;
s1和 s2为 struct student类型变量,即它们具有如下的结构:
10010 Li Fun F 19 96.5 Xi‘an
10011 Zhang Xin M 18 98 Xi‘an
275
在定义类型的同时定义变量例如,struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}s1,s2;
定义的一般形式:
struct 结构体名
{成员 1;
成员 n;
}变量名表列 ;
定义了两个具有如下结构的变量 s1和 s2。
num name sex age score addr
s1
s2
276
直接定义结构类型变量
struct
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}s1,s2;
定义一般形式:
struct
{成员 1;
成员 n;
}变量名表列 ;
例如注意,?先定义类型,后定义变量。只能对变量赋值、存取或运算,而不能对一个类型赋值、存取或运算。
成员名可以与程序中的变量名相同,二者代表不同的对象。
277
成员也可以是一个结构体变量,即结构可以嵌套。
例如,struct date
{int year;
int month;
int day;
};
struct staff
{unsigned int number;
char name[20];
char sex;
struct date birthday;
char address[30];
}s1,s2;
number name gender addressbirthday
year month day
278
3.结构体类型变量的引用
C语言规定:可以对最低一级的成员进行引用。当两个结构变量的类型相同时,可以互相赋值。
对成员引用的一般格式:
结构体变量名,成员名成员 (分量 )运算符例如,s1.number s1.birthday.month
s1.name s1.birthday.day
s1.birthday.year s1.addrees
s2.number=s1.number+1
scanf("%d",&s1.number)
互相赋值,
s2=s1;
279
4.结构体类型变量的初始化在定义结构变量时,给其置初值。
struct student
{int num;
char name[20];
char gender;
int age;
float score;
char addr[30];
};
如:
struct student s1={10010,"Li ming",'F',18,87.5,"Xi‘an"};
280
5.结构体数组一个数组的各个元素都是一个结构类型数据,这样的数组就是结构数组。
结构体数组的定义,struct student{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};
struct student s1[5];
结构体数组的初始化:
在定义结构 数组 时,给其置初值。
281
struct student
{int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
}stu[3]= {{10101,"LI Lin",'M',18},{10102,"Zhang Fun",'M',19},
{10104,"Wang Min",'F',20}};
如:
在定义 stu时,元素个数可以不指定即可以写成 stu[ ]。
例 10.2 对候选人得票的统计程序。
282
#include‖string.h‖
struct
{char name[20];
int count;}leader[3]={―li‖,0,― zhang‖,0,― wang‖,0};
main()
{int i,j;
char leader_name[20];
for(i=1;i<10;i++)
{scanf(―%s‖,leader_name);
for(j=0;j<3;j++)
if(strcmp(leader_name,leader[j].name==0) leader[j].count++;}
printf(―\n‖);
for(i=0;i<3;i++)
printf(―%5s:%d\n‖,leader[i].name,leader[i].count);}
283
# define N 20
struct student
{long num;
char name[10];
int age;
float score[7];
float total;
float average;
}stu[N];
请找出下列程序中存在的问题
284
main()
{int i,j;float t;
for (i=0;i<N;i++)
{scanf(%ld,%d‖;&stu[i].num,&stu[i].age);
scanf(―%s‖,stu[i].name);
for(j=0;j<7;j++)
scanf(―%f‖,& stu[i].score[j]);}
for(i=0;i<N;i++)
{ stu[i].total=0;
for(j=0;j<7;j++)
stu [i].total+= stu[i].score[j];
stu[i],average= stu[i].total/7.0;}
for (i=0;i<N;i++)
{temp=i;
for(j=i+1;j<N;j++)
if (stu[j],average >stu[temp],average)
temp=j;
if (temp!=i)
{t= stu[i],average;
stu[i],average= stu[temp],average;
stu[temp],average=t;}}
for (i=0;i<N;i++)
printf(%-10ld%-10s%-6.2f‖,stu[i].num,
stu[i].name,stu[i],average);}
285
6.指向结构体类型数据的指针一个结构体变量的指针就是该变量所占据的内存段的起始地址。可以设一个指针变量,用来指向一个结构体变量。
指向结构体变量的指针
#include "string.h"
main()
{struct student
{long int num;
char name[20];
char sex;
float score;
};
struct student stu_1,*p;
p=&stu_1;
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);
89101
Li Lin
M
89.5
p stu_1
286
在用指针引用结构体成员时,*p两侧的括号不能省略,因为为成员运算符 "."优先于 "*"运算符。
C语言中还可以通过,- >” 引用成员。
如,p- >name (*p).name
p- >sex (*p).sex
p- >score (*p).score
等价于注意:“- >‖的优先级与,( )‖、,[ ]‖和句点,.‖相同。
因而 ++p- >num 等价于 ++(p- >num)
(++p)- >num 先对 p加 1,再引用 num
p++- >num 等价于 (p++) - >num
287
再看一种结构成员为指针的情况:
struct
{int x;
int *y;
}*q;
则,*q- >y 表示 y所指的内容;
*q- >y++ 访问 y所指的内容后 y自增;
(*q- >y)++ 自增 y所指的内容;
*q++- >y 访问 y所指的内容后 q自增。
288
注:不能将一个结构体成员的地址赋给一个指向结构体的指针变量。如,p=&stu.name;
如果 p的初值为 stu,即指向第一个元素,则 p+1后指向下一个元素的起始地址。如:
(++p)- >num
(p++)- >num
用指向结构体的指针作函数参数例 10.5 有一个结构体变量 stu,内含学生学号、姓名和三门课的成绩。要求在 main函数中赋以值,在另一函数 print中将它们打印输出。
289
指向结构体数组的指针
struct student
{int num;
char name[20];
char sex;
int age;
};
struct student stu[3]={{10101,"LI Lin",'M',18},
{10102,"Zhang Fan",'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 Fan
M
19
10104
Wang Min
F
20
p
p
p
290
#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]; };
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);
}
void print(p)
struct student *p;
{printf(format,p->num,p->name,p->score[0],p->score[1],p->score[2]);
printf("\n");
}
Num
name
score[0]
score[1]
score[2]
p stu
291
1249
head
A
1249
1356
B
1475
C
1021
1356 1475 1021
D
NULL
7.用指针处理链表
#define NULL 0
#define LEN sizeof(struct student)
struct student
{long num;
float score;
struct student *next;};
int n;
创建链表
292
struct student *creat()
{struct student *head,*new,*rear; n=0;
head=NULL; new=(struct student *)malloc(LEN);
scanf(―%ld,%f‖,&new - >num,&new- >score);
while(new- >num!=0)
{n=n+1;
if (n==1) head=new;
else rear - >next=new;
rear=new;
new=(struct student *)malloc(LEN);
scanf(―%ld,%f‖,&new- >num,&new - >score);}
rear - >next=NULL;
return(head);}
293
void print(head)
struct student *head;
{struct student *p;
p=head;
if (head!=NULL)
do
{printf(%ld %5.1f\n‖,p - >num,p - >score);
p= p- >next;}while(p!=NULL);
}
输出链表
lian
294
删除链表中指定的结点
struct student *del(head,num)
struct student *head; long num;
{struct student *p1,*p2;
if (head==NULL) {printf(―\nlist null!\n‖);goto end;}
p1=head;
while (num!=p1->num&&p1->next!=NULL)
{p2=p1;p1=p1->next;}
if (num==p1->num)
{if(p1==head) head= p1->next;
else p2->next=p1->next;
printf(―delete:%ld\n‖,num);n=n-1;}
else printf(―%ld has not been found!\n‖,num);
end:return(head);}
295
struct student *insert(head,stud)
struct student *head; *stud;
{struct student *p1,*p2,*p0;p1=head;p0=stud;
if (head==NULL) {head=p0;p0->next=NULL;}
else
{while( (p0->num>p1->num)&&(p1->next!=NULL))
{p2=p1;p1=p1->next;}
if (p0->num<=p1->num )
{if(p1==head) {head= p0;p0->next=p1;}
else if (p1->next!=NULL ) p2->next=p0;p0->next=p1;}
else {p1->next=p0;p0->next=NULL;}}
n=n+1;
return(head);}
对链表的插入操作
296
7.共用体将几个不同的变量共占同一段内存的结构,称为“共用体。
其定义形式如下:
union 共用体名
{成员 1;
成员 n;
}变量名表列 ;
例如,union data
{int i;
char ch;
float f;
}a,b,c;
ch,i,f
ich
f
a
297
使用共用体应注意以下几点:
共用体成员的引用格式与结构体成员的引用一样。
同一个内存段可以用来存放几种不同类型的成员,但在每一瞬间时只能存放其中一种,而不是同时存放几种。
共用体变量中起作用的成员是最后一次存入的成员,在存放一个新的成员后原来的成员就失去作用。
不能对共用体变量名赋值,也不能企图引用变量名来得到成员的值。
不能把共用体变量作为函数参数,也不能使函数带回共用体变量,但可以使用指向共用体变量的指针。
共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。
298
举例,?
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 high;
char low;
}b;
}a={0x3366};
printf("a.x=%x\n",a.x);
printf("a.b.low=%x a.b.high=%x\n",a.b.low,a.b.high);
}
运行结果为,a.x=3366
a.b.low=66 a.b.high=33
299
第十一章 文件
1.文件概述:
二进制文件和文本文件:
文件在磁盘上总是以二进制形式存储的。但是数据在文件中的表现形式不同。以整型数 127为例:
二进制文件 是将内存中的数据按其在内存中的存储形式原样保存在文件中。因此整型数 127在二进制文件中用 2个字节保存为:
文本文件 是将数据按字符形式存放在文件中。因此整型数
127在文本文件中用 3个字节存放:
0011001000110001 00110111
(字符‘ 1’) (字符‘ 2’) (字符‘ 7’)
0111111100000000
(整型数 127)
300
2.文件类型指针:
文件指针的定义形式为:
FILE *标识符,*标识符,,*标识符 ;
例如,FILE *fp;
fp是一个指向 FILE类型结构体的指针变量。可以使指向某一个文件的结构体变量,从而通过该结构变量中的文件信息能够访问该文件。
3.文件的打开与关闭:
对文件作任何读写操作之前必须,打开,该文件。 在程序读写操作结束后必须,关闭,该文件 。
文件的打开 (fopen函数 )
C语言用 fopen()函数来实现打开文件。 用 fopen函数打开一个指定文件后,返回一个指向该文件的指针,在程序中通过这个指针来实现对文件的读写操作
301
fopen函数的 调用 形式为:
FILE *fp;
fp=fopen(文件名,使用文件方式 );
如果函数调用成功,fopen函数的返回值是指向该文件的指针,程序可以使用这个指针对所打开的文件进行读写操作。否则返回一个空指针 —NULL;
使用文件方式 是一个字符串,系统规定了打开文件的几种模式,并用指定字符串表示。
例如:
fp=fopen("A1","r")
只读方式fp指向 A1文件
302
文件使用方式 含义
,r‖ 只读 为输入打开一个文本文件进行读操作
,w‖ 只写 为输出打开一个文本文件进行写操作
,a‖ 追加 向文本文件尾追加数据
,rb‖ 只读 为输入打开一个二进制文件进行读操作
,wb‖ 只写 为输出打开一个二进制文件进行写操作
,ab‖ 追加 向二进制文件尾追加数据
,r+‖ 读写 为读 /写打开一个文本文件
,w+‖ 读写 为读 /写建立一个新的文本文件
,a+‖ 读写 同” r+‖
―rb+‖ 读写 为读 /写打开打开一个二进制文件
,wb+‖ 读写 为读 /写建立一个新的二进制文件,若文件不存在则创建
,ab+‖ 读写 同” r+‖
文件使用方式:
303
文件的关闭 (fclose函数 )
fclose函数用来关闭 fp所指向的文件。该文件必须是用
fopen函数打开的。如果关闭成功则返回 1,否则返回 0。
fclose函数调用的一般形式为:
fclose(文件指针 );
例如:
main()
{FILE * fp;
fp = fopen("d:\tc\lx5_6.c","r");
if (fp == NULL)
{printf("cannot open this file!\n");
exit(0);
}
fclose(fp);
}
进行读写操作如果打开文件失败,则退出程序关闭文件 lx5_6.c
304
其中 ch是要输出的字符,它可以是一个字符常量,也可以是一个字符变量。 fp是文件指针变量,它从函数得到返回值。
如果函数调用成功则返回 ch的值,否则返回 EOF。
fputc函数将一个字符写入文件的当前位置。
其一般调用形式为:
fputc(ch,fp);
4.文件的读写
fputc和 fgetc函数:
fgetc函数从文件中读取当前位置的一个字符返回。
其一般形式为:
ch=fgetc(fp);
字符变量 文件型指针变量
305
例 ls12_1 打开一个 ASCII文件,将文件内容显示到显示器上。然后 输入一行字符串,将其保存到该文件中。
C语言中使用 feof函数来判断文件是否结束。如果是文件结束,函数 feof(fp)的值为 1(真 ),否则为 0(假 )。
#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(0); }
while((c=fgetc(fp))!=EOF)
putchar(c);
gets(str);
while(str[i]!='\0')
{fputc(str[i],fp); i++;}
fclose(fp);
}
306
fread函数和 fwrite函数:
fread函数其一般调用形式为:
fread(buffer,size,count,fp);
表示从 fp所指向的文件的当前位臵读入 count个大小为
size字节的数据块放入 buffer所指向的内存区域。
buffer:是一个指针。
size,要读写的字节数。
count:要进行读写多少个 size字节的数据项。
fp,文件型指针。
fwrite函数其一般调用形式为:
fwrite(buffer,size,count,fp);
表示将从 buffer开始的 count个大小为 size字节的数据块写入 fp所指向的文件的当前位臵。
307
a:\tc\fil.c从键盘上输入 4个学生的有关数据,然后把他们转存到磁盘上。
#include "stdio.h―
define SIZE 4
struct student
{char name[10];
int num;
int age;
char addr[15];
}stu[SIZE];
main()
{FILE *fp;int i;
if( (fp=fopen(―a:\stulist‖,―wb")) == NULL)
{printf("file can‘t open!\n");
exit(0); }
for(i=0;i<SIZE;i++)
scanf(%s%d%d%s‖,stu[i].name,& stu[i].num,& stu[i].age,stu[i].addr);
fwrite(stu,sizeof(struct student),SIZE,fp);
fclose(fp);
308
if( (fp=fopen(―a:\tc\stulist‖,―rb")) == NULL)
{printf("file can‘t open!\n");
exit(0); }
fread(s,sizeof(struct student),SIZE,fp);
fclose(fp);
for(i=0;i<SIZE;i++)
printf(%10s%10d%10d%20s\n‖,stu[i].name,stu[i].num,stu[i].age,stu[i].addr);
}
309
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指向的文件上
310
#include "stdio.h"
main()
{FILE *fp;int k;
char str[80];
if( (fp=fopen("testfile","w")) == NULL)
{printf("The file can‘t be opened!\n");
exit(0); }
scanf("%s%d",str,&k);
fprintf(fp,"%s %d",str,k);
fclose(fp);
if( (fp=fopen("testfile",‖r")) == NULL)
{printf("The input file can‘t be opened!\n");
exit(0); };
fscanf(fp,"%s %d",str,&k);
printf("%s,%d",str,k);
fclose(fp);
}
例 ls12_2.c