C 程序设计教材,,C 程序设计,
第二版谭浩强 编著清华大学出版社出版
2000年 4月 27日第一章 概 述
1-1 C语言发展的 历史背景
67~70年推出 BCPL (Basic Combined
Programming Language),被 称为 B语言。
72~73 在 B语言基础上设计出 C语言。
UNIX操作系统用 C语言改写成功,使 C
语言的突出优点引起广泛注意。
1-1 C语言发展的 历史背景
78年,,The C programming Language,
名著出版 ( K&R著 )。 83年美国国家标准化协会 (ANSI)公布新标准,被称为 ANSI
C。
目前在微型机上应用的版本有
Microsoft C,Turbo C等,各版本略有差异。
1-2 简单的 C程序例
main( )
{ int a ; / * 定义一个整型变量 a * /
int b ;
int c ;
a=45 ; b=24 ;
c= a+b ;
printf(”a=%d,b=%d,c=%d,\n”,a,b,c);
/ * 输出变量 a,b,c的值 * /
}
给 a,b 赋值的语句表达式 a+b赋值给 c
上述程序可以该改写为以下形式:
int sum( x,y ) // 定义 一个名为 sum 的函数
int x,y ; // x,y为函数的形式参数(整数类型)
{ int z; z=x+y; return(z); } //函数体
main( )
{ int a,b,c;
a=45 ; b=24 ; / * 给 a,b 赋值 * /
c=sum(a,b) ;
printf(“a=%d,b=%d,c=%d,\n”,a,b,c); / * *
输出变量 a,b,c的值 * / }
主函数被调函数
1-3 C 语言特点
1,语言简洁、紧凑,使用、方便灵活。
关键字少( 32个),程序书写形式自由。
2,运算符丰富( 34种)。
(见 P.375附录 )
3,数据结构丰富
(尤其是指针类型使用灵活、多样)。
1-3 C 语言特点
4,语法限制不严。
5,可访问物理地址,有汇编语言的大部分功能。
6,生成的目标代码质量高,程序执行效率高。
1-4 C 程序上机步骤编辑源程序( myprog.c)
编译成目标程序( myprog.obj)
与库程序和其他目标程序连接成可执行的程序( myprog.exe)。
第二章数据类型、运算符与表达式
2-1 数据类型程序 = 数据结构 + 算法 + 程序设计方法 + 语言工具和环境
C语言提供的数据结构是以数据类型的形式出现的。
C的数据类型有:
基本类型,
整型、字符型、实型、枚举类型构造类型,
数组、结构(体),联合指针类型空类型
2-2 常量与变量在程序执行过程中,其值 不能被改变的量为 常量 。在程序中,常量可以用符号来表示。
在程序执行过程中,其值 可以被改变的量为 变量 。每一个变量须有一个 变量名,
它在内存中占有一个内存单元。变量名的标识符须 字符或下划线开头,长度不限。
大小写字母表示不同 的标识符。
变量须 先定义后使用 。
定义变量时,须指出该变量的数据类型。如
int x ;
float y ;
int a = 256 ;
2-3 整型数据在程序中,整型常量可以用十进制、八进制,
十六进制表示。
整型变量分:
关键字 PC中占的位数 数的范围基本型 int 16 Bit -32768~32767
短整型 short int 16 Bit -32768~32767
长整型 long int 32 Bit -231~231 -1
无符号型 unsigned 16 Bit 0~65535
无符号长整型 unsigned long 32 Bit
请同学们考虑,
int a = 15 ; int b = - 15 ;
unsined int c = 15 ; unsined int d =
65600 ;
a,b,c,d 四个整型变量在内存中存放的形式如何?
应该是:
a 00000000 00001111
b 11111111 11110001
c 00000000 00001111
d 00000000 01000000
请看图解
int b = - 15 ;
负数是以补码形式在内存中存放的。
-15 的
1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
反码,1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
补码,1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1
15 8 7 0
原码:
补码为 反码 + 1
2-4 实型数据在程序中,实型常量可以用十进制形式和指数形式表示(如 56e3 即为 56000)。
实型变量分:
单精度 ( float) 32 Bit
数值范围为 10 - 38 ~ 10 38
双精度 ( double) 64 Bit
数值范围为 10- 308 ~ 10 308
见下面讲解
32位浮点数的表示方法为阶符 1 位,阶码 7 位,数符 1 位,尾数
23位。因而阶数最大为 127。 转换为十进制数,2127=10n
n = 127 lg2 = 127 * 0.301 = 38
数符阶符尾数尾数 阶码浮点数存放形式( 32位):
8位 8位 8位 8位
31 2423 16 15 8 7 0
阶符
1
位数符
1
位尾 数 23 位 ( 0~22)阶数 7位
(24~31)
2127 =10n
n =127 * lg2 = 38
阶数最大值
27-1=127
2-5 字符型数据在程序中,字符常量用字符加 单引号表示,如 ’ A?,?a?。 还可以用以开头的特殊形式表示,如 \ n,\ t,\ 102 等。
一个字符变量占内存 一个 字节。
字符变量 定义形式为
char a ; a = 67 ;
char x =?A? ;
字符串常量 是用 双引号 括起来的字符系列,如
” China”,”How do you do?”,”A”
等。
请注意不能把字符串常量赋值给字符变量,如
char a1 ; a1 =,A” ;
char a =,Hello!” ;
这是错误的。因为字符变量只占一个内存字节,而 C语言规定,在内存中,
每一个字符串尾部都加上一个特殊的字符 ’ \0 ’ 。 这个字符不能显示不能打印,它的 ASCII值为 0
HERE
字符串在内存中存放形式:
H e l l o ! \0
A \0
” Hello!”
” A”
2-6 算术运算符与算术表达式
C语言中,运算符很丰富。
算术运算符有:
+ 加法 运算符,- 减法运算符,*
乘法运算符,/ 除法运算符,%求余运算符。
++ 自增 运算符,- - 自减 运算符 。
例如 main( )
{ int i = 36 ; i ++ ;
printf(” i = %d”,i); }
请注意,自增自减运算符在表达式中的位置:
++ i 先自增,后使用。
i ++ 先使用,后自增。
如 main( )
{ int i,j = 5 ; i = j ++ ;
printf(”i = %d,j = %d”,i,j) ; }
本程序运行结果输出是? 应该是 i = 5,j =6 而不是 i = 6,j = 6
先赋值 后自增上例程序若改为
main( )
{ int i,j = 5 ;
i = ++ j ;
printf (”i = %d,j = %d”,i,j) ; }
程序运行结果输出是?
应该是 i = 6,j = 6
先自增 后赋值
2-7 赋值运算符与赋值表达式赋值运算符 ( = )的作用是将一个数据赋值给一个变量。如赋值表达式 x = 25
即将 25 赋值给变量 x。 请注意变量的数据类型以及被赋值的数,即赋值运算符两边的数据类型。如已定义 x 是整型变量,而 x = 25.45 ; 则 小数被截。
复合赋值运算符
+= - = *= / = %= 等,如
x+=3 即 x=x+3
x / =a+b 即 x=x/(a+b)
请 看赋值表达式 x += x - =x *x
它的求解步骤是先进行 x - =x *x 运算,
即 x = x - x *x
若 x的初值为 3,则此时为 - 6,再进行
x =x+x 运算,得 x为 - 12。
2-8 逗号运算符与逗号表达式逗号运算符 即,,”
逗号表达式的形式是将两个表达式用逗号连接起来,即 表达式 1,表达式 2
如 x+5,y+12 先求解表达式 1后求解表达式 2 。请看逗号表达式
x++,x+y
若 x,y的 初始值分别为 12,3,则这个逗号表达式的值为 16。
逗号运算符的运算级别最低。
第三章 最简单的 C程序
3-1 C语句
C语句即计算机的操作命令。
C语句分为,
表达式语句 如 a+15 ;
控制语句 ( if goto while for switch
等 )
函数调用语句 printf(,Hello” ) ;
空语句 ;
3-2 程序的三种基本结构顺序结构分支结构循环结构
C 程序的组成:
若干语句以及说明部分组成函数,
若干函数以及编译预处理命令组成源文件,
若干源文件组成 C程序。
3-3 数据输出
C没有输入输出语句,输入输出操作是由函数来实现。
3-3-1 字符输出函数 putchar
功能 输出一个字符
char a = 65 ; putchar(a) ;
putchar(?A?) ;
putchar(?\n?) ;
请注意,能否 putchar(”A”) ;
3-3-2 格式输出函数 printf
函数的参数为 格式控制,输出表列 如
printf(” a=%d,x=%f”,a,x ) ;
格式控制是以,,括起来的字符串,输出表列是用逗号分开的变量名或表达式。
格式控制中,%以及其后格式字符为格式说明。
格式字符,d 用十进制整数形式输出
f 用十进制小数形式输出格式控制 输出表列格式输出函数 printf 的格式字符表格式字符 说 明
d 以带符号的十进制形式输出整数
o 以无符号的八进制形式输出整数
x 以无符号的十六进制形式输出整数
u 以无符号的十进制形式输出整数
c 以字符形式输出(一个字符)
3-4 数据输入
3-4-1 字符输入函数 getchar 无 参函数功能 输入一个字符 如
#include <stdio.h>
main( )
{ char c1 ; c1 = getchar( ) ;
putchar(c1) ; }
本程序运行时,若键入 a,则在显示屏上显示
a (键 ’ a’,然后回车送入内存)
a
这个 a是 putchar(c1)的 输出
3-4-2 格式输入函数 scanf
一般形式
scanf( 格式控制,地址表列)
main( )
{ int a,b,c ;
scanf(”%d%d%d”,&a,&b,&c) ;
printf(”%d,%d,%d\n”,a,b,c) ; }
本程序运行时,执行到 scanf等待用户键入数据。键入数据时,每个数据之间须用空白字符分开。
格式控制 地址表列格式输入函数 scanf 的格式字符表格式字符 说 明
d 以十进制形式输入整数
o 以八进制形式输入整数
x 以十六进制形式输入整数
c 输入一个字符
s 输入一个字符串,以空白字符结束
f 以十进制小数形式输入实数使用格式输入函数 scanf须注意,
1,函数的后一半参数是地址表列,而不是变量表列。如 scnaf(”%d,%d”,&x,&y) ;
不 能写为
scnaf(”%d,%d”,x,y) ;
应该是地址 &x,&y
2,函数的前一半参数格式控制字符串中,除格式说明以外,其他字符,则在输入时应键入这些字符。
第四章 逻辑运算和判断选取控制
4-1 关系运算符与关系表达式关系运算符,<(小于 ) <=(小于等于 )
>(大于 ) >=(大于等于 ) ==(等于 ) !=(不等于 )
优先级序为,< <= > >= == !=
关系表达式是用关系运算符将两个表达式连接起来的表达式。如 a+b>x+y
a<b+5 a>=3
关系表达式的值是一个逻辑值(真或假),
4-2 逻辑运算符与逻辑表达式逻辑运算符,&& (逻辑与 ) ||(逻辑或 ) !(逻辑非 )
&& 和 || 是双目运算符,! 是单目运算符。
优先级序为,! (最高 ) 算术运算符关系运算符 &&和 || 赋值运算符 (最低 )
逻辑表达式是用逻辑运算符连接起来的表达式。
如 a&&b x+y||a !a
逻辑表达式的值是真( )或假( )。
逻辑表达式的应用举例:
判断某年是不是闰年。
我们知道,地球绕太阳一转,需
365 天 5 小时 48 分 46 秒,
试设想,假如需 365 天 6 小时,则每
4 年一个闰年(设每个被 4 能除尽的年份为闰年)。
假如需 365 天 5 小时 45 分 36 秒
(即 5 小时 45.6 分),则每 100年减去一个闰年。但被减去太多,每 400 年再增加一个闰年。
逻辑表达式应用举例判别某年是否闰年,要看
( 1) 该年份(例 1996)能被 4整除,但不能被 100整除,
( 2) (该年份能被 4整除 ),又能被 400整除,
满足以上两条件之一即为闰年。设某年
year,因而有以下逻辑表达式:
( (year%4==0) && ( year%100!=0) ) ||
(year%400==0)
该逻辑表达式的值为 1即闰年;为 0则非闰请思考逻辑表达式中括号能省吗?
4能整除 100不能整除与 或
400能整除
4-3 if 语句
4-3-1 C 中的 if 语句有三种形式:
1,if (表达式 ) 语句
2,if (表达式 ) 语句 1 else 语句 2
3,if (表达式 1) 语句 1
else if (表达式 2) 语句 2
:
else if (表达式 m) 语句 m
else 语句 n
4-3-2 if 语句的嵌套
if语句中又包含 if语句
exa432.c
请注意 if 与 else的配对使用。
4-3-3 条件运算符?,
条件运算符是一个三目运算符,条件表达式的一般形式为:
表达式 1? 表达式 2,表达式 3
先求解表达式 1,若为非 0值,则求解表达式 2,否则求解表达式 3。如条件运算符应用举例
exa433.c
main( )
{ int a=33,b=44 ;
int max;
max=(a>b)? a+50,b+16 ;
printf(”%d\n”,max) ;
}
4-4 switch语句
switch 语句是分支选择语句,一般形式:
switch (表达式 )
{ case 常量表达式 1,语句 1; break;
case 常量表达式 2,语句 2; break;
:
Case 常量表达式 n,语句 n; break;
default,语句 n+1 }
当 switch表达式的值与某一个 case后的表达式的值相等时,就执行此 case
break表示执行语句后转出
default表示 1~n
表达式都不满足若所有 case后的常量表达式都与
switch表达式不匹配,则执行 default后的语句。
如果 case语句后不加 break,则执行了某一个 case语句后继续执行以下
case语句,包括 default后语句。
第五章 循环控制循环控制可用:
if 语句和 goto语句
for 语句
while 语句
do-while 语句
5-1 goto语句构成循环
main( )
{ int i,sum=0 ;
i =1;
loop,if ( i<100) // loop 语句标号
{ sum = sum +i ;
i++;
goto loop; }
printf(“”%d”,sum);
5-2 for 语句
C 中的 for 语句最灵活最常用,一般形式:
for (表达式 1 ; 表达式 2 ; 表达式 3) 语句;
它的执行过程是:
1) 先求解表达式 1 ; 2)求解表达式 2,若其值为真(非 0),则执行 for
内嵌语句,然后求解表达式 3; 3)转回求解表达式 2,直到其值为假,跳出循环。
例:
main( )
{ int i,sum=0 ;
for( i =1; i<100; i++ )
sum = sum +i ;
printf(”%d”,sum); }
现在我们来讨论:
1,省去表达式 1;
2,省去表达式 2;
3,省去表达式 3;
4,省去表达式 1,2,3,(分号不能
for ( ; ; ) ;
死循环 (或等中断 )
死循环,除非循环体中有 if语句等请看以下 for 语句:
for( i =1,sum=0 ; i<100; i++ )
sum = sum +i ;
for ( i =1,sum=0 ; i<100 ;
sum=sum+i,
i++ ) ;
for ( i=0; (c=getchar( )) !=’ \n’ ; i+=c ) ;
表达式 3
是一个逗号表达式循环空语句 是从键盘输入一个字符赋值给 c,判是否为回车不是,则继续循环
5-3 while,do-while 语句
while语句形式:
while (表达式 ) 语句;
它的执行过程是先判断表达式后执行内嵌语句。
do-while语句形式:
do 语句 while(表达式);
它的执行过程是先执行内嵌语句,后判断表达式。
5-4 break,continue 语句
5-4-1 break 语句它的功能是跳出(中止)循环,例:
for(r =1; r <=10; r ++)
{ area = PI * r * r ;
if (area>100) break ;
print(”%f\n”,area) ; }
5-4-2 continue 语句它的功能是提前结束本次循环。 例:
main( )
{ int year ;
//exa451.c
float money =10000 ;
for( year =1; year<= 20; year++ )
{ money= money *1.06125;
if( money> 20000) break;
printf (”%d %10.2f \n,,year,
money) ; }
printf(”**%d year
超过 20000不再存共 10位,
小数点后 2位已知 1999年是兔年,列出本世纪的所有兔年年份,统计有几个兔年。
main( )
//exa452.c
{ int i =0,year ;
for ( year =1900; year<2000; year ++ )
{ if ( ( year-1999)%12 ! = 0 )
continue ;
i ++ ;
printf(”%d \n”,year) ; }
如果不是兔年退出本次循环本程序运行结果输出:
1903
1015
1927
1939
1951
1963
1975
1987
1999
There are 9 Rabbit years
第六章 数 组数组是构造类型的数据。
6-1 一维数组的定义和引用定义方式,类型说明符 数组名 [常量表达式 ];
如 int a [ 5 ] ;
数组引用时,只能引用数组的元素,而不能指望引用整个数组。数组元素的表示形式为:
数组名 [下标 ]
main( )
{ int i,a[10] ;
for ( i =0 ; i <=10; i ++) a[ i ]=i ;
for ( i =9 ; i >= 0 ; i --) printf(”%d,,a[ i ]) ; }
C中数组元素下标从 0开始一维数组的初始化定义数组时对数组元素赋初值,如
static int
a[10]={1,3,5,7,9,11,13,15,17,19};
也 可以
static int a[10]={1,3,5,7,9};
//不赋值的元素初值为 0。还可以这样
static int a[ ]={1,3,5,7,9};
少于 10个元素可以数组长度允许空缺一维数组程序举例冒泡法排序 设有 5 个数排序:
12
56
25
3
78
56
25
12
78
3
56
25
78
12
3
56
78
25
12
3
78
56
25
12
3
大循环 第 1次 第 2次 第 3次 第 4次原数组第 1次大循环后数组排序后的数组小循环(比较) 4次 3次 2次 1次程序如下:
main( )
{ int a[5],i,j,t ; printf(”Input 5
numbers:\n”);
for ( i=0;i<5;i++) scanf(”%d”,&a[i]);
for (j=1;j<=4;j++)
{ for(i=1;i<=5-j;i++)
if (a[i]<a[i+1])
{ t=a[i];a[i]=a[i+1];a[i+1]=t;} }
for(i=0;i<5 ;i++) printf(”\n%d”,a[i]);
大循环 4次小循环元素上小下大则互换
6-2 二维数组二维数组的定义的一般形式:
类型说明符 数组名 [常量表达式 ] [常量表达式 ]
如 float a [3][4] ;
数组 a共有 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,3,5,7},{9,11,13,15},
{17,19,21,23}};
或 static int a[3][4]
={1,3,5,7,9,11,13,15,
17,19,21,23} ;
也可以 static int a[3][4]
={ {1,3,5,7},{9},{0,19,21} } ;
缺省的数组元素为 0
二维数组程序举例
main( )
{ static int a[2][3]
={ {1,3,5},{7,9,11} };
int b[3][2]; int i,j;
printf(” array a,\n,);
for (i=0;i<=1;i++)
{ for (j=0;j<=2;j++)
{ printf(” %5d“,a[i][j]);
b[i][j]=a[i][j]; }
二维数组程序举例(续)
printf(” array b,\n,) ;
for ( i=0; i<=2; i++ )
{ for ( j=0; j<=1; j++ )
printf(” %5d“,b[i][j] );
printf(,\n” );
}
}
十进制 5位整数形式换行
6-3 字符数组字符数组的每一个元素为字符,字符数组定义的形式:
char 数组名 [常量表达式 ]; 可 定义同时赋值:
static char ch1[ 6] =
{?H?,?e?,?l?,?l?,?o?,?!? };
static char ch1[ ] =
{?H?,?e?,?l?,?l?,?o?,?!? };
static char ch1[ ] = {,Hello!” }; / *用字符串常量给字符数组赋值,字符串结尾字符数组的输出
static char ch1[ ] = {,Hello!” };
printf(”%s”,ch1) ;
输出为
Hello! // 输出字符不包括结尾字符 \0
字符数组的输入
static char ch1[ 7] ;
scanf(”%s”,ch1) ; //此时键入 Hello!回车,
注意不能超过 6个字符,不足可以。
数组名即地址字符串处理函数
1 输出一个字符串 puts(字符数组名 )
static int string1[ ]={”China”};
puts(string1);
China
2 输入一个字符串 gets(字符数组名 )
gets(string2);
3 字符串拷贝 strcpy(字符数组 1,字符数组 2)
字符数组 str1的长度须大于等于 str2。
字符数组 str1 必须是字符数组名,而字符数组 str2可以是字符数组名,也可以是字符串常量。
如 strcpy (str1,”China”);
4 字符串比较 strcmp(字符数组 1,字符 数组 2)
strcmp(str1,str2); str1与 str2 均可是字符数组名或字符串常量。 Strcmp函数的返回值是:
str1=str2,则 函数的返回值为 0;
第七章 函 数
7-1 概 述
C 语言的基本单位是函数。函数相当于其他语言中的子程序。一个 C程序由一个主函数和若干个 函数组成。一个函数中可以调用若干个函数,反之,一个函数可以被多个函数调用,调用次数不限。
一个 C源程序文件 (***.c)是一个编译单位。
一个 C程序可由一个源程序文件或多个源程序文件组成。
C 程序的执行从 main函数开始,在 main函数结束,main函数由系统定义。
函数与函数之间是平行的独立的,不能在一个函数中定义另一个函数(所谓嵌套定义)。
函数可以互相调用(不能调用 main 函数),被调用的函数可以调用别的函数
(嵌套调用),并可以调用自己。
在程序设计中,常将一些常用的功能模块编写成函数,存放在函数库中(通常由软件厂商提供),函数库中的函数称为库函数。除了标准库函数外,很多软件公司还为用户提供许多实用的非标准的库函数。
用户要尽可能熟悉库函数。
7-2 函数的定义
1 无参函数的定义形式类型标识符 函数名( )
{ 说明部分语句 } //{ }中称为函数体,
若空缺,则为空函数。
2 有参函数的定义形式类型标识符 函数名(形式参数表列)
形式参数说明
{ 说明部分 语句 }
7-3 函数的参数和函数的返回值
7-3 -1 形式参数和实际参数
int sum( x,y ) // x,y为形参
int x,y ; // 形参说明
{ int z ; z=x+y ; return(z) ; }
main( )
{ int a,b,c ;
a=45 ; b=24 ; c = sum (a,b) ; // a,b为实参
printf(“a=%d,b=%d,
返回值函数的形参与函数的实参数据类型应一致。
实参变量对形参变量的数据传递是单向的值传递。只是由实参将它的值传递给形参,而不能由形参传回给实参。
函数调用时,系统给形参分配内存单元,调用结束后,形参单元被释放。
实参在函数调用前后不会变化。
ANSI新标准规定,形参说明可以类似
PASCAL所用方法,
int sum ( int x,int y )
形参表中作类型说明
7-3-2 函数的返回值函数的返回值是通过 return 语句获得的。
定义函数时应指定函数的类型(如缺省,
则约定为整型),函数的类型决定函数返回值的类型。
函数中若没有 return语句(系统会给一个警告),并不表示函数不带回返回值,
而只是不带回一个用户所需要的、有用的函数值。如果明确函数不带回值,则须在定义函数时,在函数名前冠以 void,表示空类型或无类型。如:
void putchar( c)
7-4 函数的调用
7-4-1 函数调用的一般形式无参函数调用,函数名 ( );
有参函数调用,函数名 (实参表列);
实参与形参须一一对应。
例 7-3-1
7-4 函数的调用
7-4-2 函数调用方式按函数调用在程序中的位置来分,可分为
1 函数语句 如
getchar( ) ;
2 函数表达式 如
c =100+sum(a,b) ;
3 函数 参数 如
printf(”%d\n”,sum(a,b)) ;
sum1= sum(c+sum(a,b) ;
函数的返回值作为另一函数的参数
7-4-3 对被凋函数的说明若函数 1调用函数 2,我们通常称函数 1
为主调函数,函数 2为被调函数。
被调函数如果是库函数,则须在主调函数所在的源程序文件的开头用 include命令包含相应的头文件(如 include < stdio.h
> )。
被调函数如果是用户自己定义的函数,
且被调函数与主调函数同在一个源文件中,
则一般需在主调函数中对被调函数的类型作说明(被调函数定义在前,则可省)。
说明的形式为函数调用语句前没有类型标识符
7-5 函数的嵌套调用被调函数也可以调用其他函数,层层嵌套,层数不限(例略)。
7-6 函数的递归调用函数直接或间接调用自己,称函数的递归调用。 C语言中,允许有限次的有终止的递归调用。例如用递归的方法求 n! 或求
xn就 是递推的问题,只要 n有限大,都能用函数的递归调用来求解。
函数的递归调用的应用例 - Hanoi塔问题设有三根针,名为 A,B,C。 在 A针 上放有 n
个大小不等的盘子(如图)。
A B C要求将 A针中的盘子搬到 C针中去,规定每次只能搬动一个盘,可以借助于 B针过渡,并必须保证每针中的盘,大盘在下,小盘在上。
搬动过程
A B C
程序如下:
void moveone ( get1,put1) // 搬一个盘函数
char get1,put1;
{ printf(”%c->%c\n”,get1,put1) ; }
void hanoi ( n,a,b,c) // 搬 n个盘函数
char a,b,c ; int n ;
{ if (n ==1) moveone( a,c ) ;
else { hanoi (n-1,a,c,b ) ;
moveone (a,c ) ;
hanoi (n-1,b,a,c ) ; }
hanoi函数递归调用递归调用
main ( )
{ int m ;
printf(”Input the number of the
diskes:\n”);
scanf(”%d”,&m) ;
printf(”The step to moving %3d
diskes,\n”,
m);
hanoi( m,?A?,?B?,?C? ) ;
}
本程序运行情况如下:
调用 hanoi函数
A-->C
A-->B
C-->B
A-->C
B-->A
B-->C
A-->C
7-7 数组作为函数参数数组作为函数参数是指数组名和数组的元素作函数参数。
数组的元素作函数(实际)参数与变量作函数参数用法一样,是实参向形参的单向的数值传递。
数组名可以作函数的形参和实参。请看程序例,有一个名为 score 的一维数组,
存放某班 30位同学的某课成绩,求全班平均成绩。
Exa771.c
请注意,数组名作函数参数时,不是单向的(实参到形参)值传递,而是实参数组的起始地址(数组名即数组的起始地址)
传递给形参数组,使两个数组共同占有一段内存。
因而,函数调用前后,实参数组是有可能被调用所改变的。
7- 8 局部变量和全局变量从变量的作用域来区分,变量分为局部变量和全局变量。函数内部定义的变量称为局部变量,它只在本函数内有效。在本函数外即无效,即不能在本函数外使用。
函数的形式参数同于局部变量,也只在本函数内有效。 不同函数中允许使用同名的变量(如常用的 i,j,n等)。我们在此前举例中多为局部变量。
在函数以外定义的变量称为外部变量,也称全局变量。全局变量为本程序文件的各函数所共用。它的使用范围是从变量被定义开例 7-8-1 求 10个学生成绩(一个一维数组)
的平均值、最高分和最低分。
float max=0,min=0; //定义两个全局变量
float average( 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]; }
数组作函数参数例 7-8-1 续
aver=sum/n ; return(aver) ; }
main( )
{ int i; float av,score[10] ;
for( i=0;i<10 ; i++)
scanf(” %f”,&score[i]) ;
av =average(score,10) ; //数组名作实参
printf(” max=%5.1f\nmin=%5.1f\navera
ge=%5.1f\n”,max,min,av) ;
如:
85 78.5 56 76 100 88.5 90 66 95 75
(回车)
max=100.0
min= 56.0
average= 81.1
本例中,在 main函数和 average函数中都用到了变量 max 和 min。 可见全局变量增加了函数间数据联系渠道。函数只能有一个返回值(本例中为 aver),但通过调用函数
average,我们得到了三个有用的值,其中如外部变量定义在后,而使用该变量的函数在前(或要使用别的程序文件中定义的外部变量),则在该函数中应对该被使用的外部变量作说明。说明的方式是:
extern x,y ; // x,y 为在后面定义或别的源程 //序文件中定义的函数。
此外,全局变量一般不要和局部变量同名,如果同名,则则在该具备变量所在的函数中,全局变量不起作用。
外部变量不宜用得太多,太多了被占的内存多,且程序的通用性不好。
7- 9 动态存储变量和静态存储变量
7- 9-1 变量的存储类别(数据的存储类别属性)
从变量的生存期(时间域)来分,变量可分为动态存储变量和静态存储变量。
动态存储变量是在程序运行时根据需要动态分配存储空间的变量。即需要时,分配给内存单元,不需要时就释放内存单元。
静态存储变量局是指程序运行期间分配固定的存储空间的变量。程序运行结束才释放内存单元。它的寿命与程序周期一样长。
静态存储区动态存储区堆 栈 区
Commend.com
程序代码区存放全局变量和静态局部变量存放局部变量和形式参数
7- 9-2 局部变量的存储方式一,局部变量如果不作特别说明就是存放在动态存储区中的动态存储变量,对它们的存储空间分配和释放由编译程序自动处理的,因而又被称为自动变量。自动变量由关键字 auto作存储类型说明。而通常
auto可省略。函数的形式参数与它一样。
它们的寿命与函数一样长。
二、静态局部变量是函数内部定义的需要特别说明的变量,它的寿命与程序一样长。函数调用结束,静态局部变量所占的内存空间不释放,其值保留。它的定义
7- 9-2 全局变量的存储方式全局变量(外部变量)在函数外定义,
编译时给全局变量分配静态存储空间。全局变量都是静态存储变量,可以由各个函数所引用。
如果一个文件中定义的外部变量只允许本文件中的函数调用,不允许别的文件中的函数调用,则称为静态外部变量,需用
static来说明。如 static int x ; //在函数外
7- 9-3 变量存储类别小结为了使大家对各中存储类别的变量有一按作用域分类 作用域(空间)
局部变量全局变量动态局部变量 (自动 )
静态局部变量寄存器变量函数形式参数函数外静态变量外部(全局)变量本函数或分程序内有效,
函数或分程序外不能引用只限本文件引用允许其它文件引用名 称按寿命分 类 寿命(存在时间)
动 态存储变量静 态存储变量自动局部变量寄存器变量函数形式参数函数外静态变量外部(全局)变量寿命与函数相同离开函数值就消失离开函数值仍保留寿命与程序执行周期相同名 称静态局部变量
7- 10 C库函数简介
C语言本身并不包括库函数,但各软件公司为方便用户在编译系统中提供大量的库函数。除 ANSI标准库函数外,各版本的 C
编译系统还提供实用的非标准库函数,其中与操作系统接口函数以及计算机的屏幕和图形函数各系统有不少差别。
标准库函数请参看,C程序设计题解与上机指导》(清华版)第四部分( P
176)。
学习使用库函数的方法之一是多使用系统的帮助文件 HELP,那里有各函数的说请大家多多使用 HELP!
本周上机时间:
周二 周三下午
2.00~5.00
下周起时间请与陈冬红老师联系
第二版谭浩强 编著清华大学出版社出版
2000年 4月 27日第一章 概 述
1-1 C语言发展的 历史背景
67~70年推出 BCPL (Basic Combined
Programming Language),被 称为 B语言。
72~73 在 B语言基础上设计出 C语言。
UNIX操作系统用 C语言改写成功,使 C
语言的突出优点引起广泛注意。
1-1 C语言发展的 历史背景
78年,,The C programming Language,
名著出版 ( K&R著 )。 83年美国国家标准化协会 (ANSI)公布新标准,被称为 ANSI
C。
目前在微型机上应用的版本有
Microsoft C,Turbo C等,各版本略有差异。
1-2 简单的 C程序例
main( )
{ int a ; / * 定义一个整型变量 a * /
int b ;
int c ;
a=45 ; b=24 ;
c= a+b ;
printf(”a=%d,b=%d,c=%d,\n”,a,b,c);
/ * 输出变量 a,b,c的值 * /
}
给 a,b 赋值的语句表达式 a+b赋值给 c
上述程序可以该改写为以下形式:
int sum( x,y ) // 定义 一个名为 sum 的函数
int x,y ; // x,y为函数的形式参数(整数类型)
{ int z; z=x+y; return(z); } //函数体
main( )
{ int a,b,c;
a=45 ; b=24 ; / * 给 a,b 赋值 * /
c=sum(a,b) ;
printf(“a=%d,b=%d,c=%d,\n”,a,b,c); / * *
输出变量 a,b,c的值 * / }
主函数被调函数
1-3 C 语言特点
1,语言简洁、紧凑,使用、方便灵活。
关键字少( 32个),程序书写形式自由。
2,运算符丰富( 34种)。
(见 P.375附录 )
3,数据结构丰富
(尤其是指针类型使用灵活、多样)。
1-3 C 语言特点
4,语法限制不严。
5,可访问物理地址,有汇编语言的大部分功能。
6,生成的目标代码质量高,程序执行效率高。
1-4 C 程序上机步骤编辑源程序( myprog.c)
编译成目标程序( myprog.obj)
与库程序和其他目标程序连接成可执行的程序( myprog.exe)。
第二章数据类型、运算符与表达式
2-1 数据类型程序 = 数据结构 + 算法 + 程序设计方法 + 语言工具和环境
C语言提供的数据结构是以数据类型的形式出现的。
C的数据类型有:
基本类型,
整型、字符型、实型、枚举类型构造类型,
数组、结构(体),联合指针类型空类型
2-2 常量与变量在程序执行过程中,其值 不能被改变的量为 常量 。在程序中,常量可以用符号来表示。
在程序执行过程中,其值 可以被改变的量为 变量 。每一个变量须有一个 变量名,
它在内存中占有一个内存单元。变量名的标识符须 字符或下划线开头,长度不限。
大小写字母表示不同 的标识符。
变量须 先定义后使用 。
定义变量时,须指出该变量的数据类型。如
int x ;
float y ;
int a = 256 ;
2-3 整型数据在程序中,整型常量可以用十进制、八进制,
十六进制表示。
整型变量分:
关键字 PC中占的位数 数的范围基本型 int 16 Bit -32768~32767
短整型 short int 16 Bit -32768~32767
长整型 long int 32 Bit -231~231 -1
无符号型 unsigned 16 Bit 0~65535
无符号长整型 unsigned long 32 Bit
请同学们考虑,
int a = 15 ; int b = - 15 ;
unsined int c = 15 ; unsined int d =
65600 ;
a,b,c,d 四个整型变量在内存中存放的形式如何?
应该是:
a 00000000 00001111
b 11111111 11110001
c 00000000 00001111
d 00000000 01000000
请看图解
int b = - 15 ;
负数是以补码形式在内存中存放的。
-15 的
1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
反码,1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
补码,1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1
15 8 7 0
原码:
补码为 反码 + 1
2-4 实型数据在程序中,实型常量可以用十进制形式和指数形式表示(如 56e3 即为 56000)。
实型变量分:
单精度 ( float) 32 Bit
数值范围为 10 - 38 ~ 10 38
双精度 ( double) 64 Bit
数值范围为 10- 308 ~ 10 308
见下面讲解
32位浮点数的表示方法为阶符 1 位,阶码 7 位,数符 1 位,尾数
23位。因而阶数最大为 127。 转换为十进制数,2127=10n
n = 127 lg2 = 127 * 0.301 = 38
数符阶符尾数尾数 阶码浮点数存放形式( 32位):
8位 8位 8位 8位
31 2423 16 15 8 7 0
阶符
1
位数符
1
位尾 数 23 位 ( 0~22)阶数 7位
(24~31)
2127 =10n
n =127 * lg2 = 38
阶数最大值
27-1=127
2-5 字符型数据在程序中,字符常量用字符加 单引号表示,如 ’ A?,?a?。 还可以用以开头的特殊形式表示,如 \ n,\ t,\ 102 等。
一个字符变量占内存 一个 字节。
字符变量 定义形式为
char a ; a = 67 ;
char x =?A? ;
字符串常量 是用 双引号 括起来的字符系列,如
” China”,”How do you do?”,”A”
等。
请注意不能把字符串常量赋值给字符变量,如
char a1 ; a1 =,A” ;
char a =,Hello!” ;
这是错误的。因为字符变量只占一个内存字节,而 C语言规定,在内存中,
每一个字符串尾部都加上一个特殊的字符 ’ \0 ’ 。 这个字符不能显示不能打印,它的 ASCII值为 0
HERE
字符串在内存中存放形式:
H e l l o ! \0
A \0
” Hello!”
” A”
2-6 算术运算符与算术表达式
C语言中,运算符很丰富。
算术运算符有:
+ 加法 运算符,- 减法运算符,*
乘法运算符,/ 除法运算符,%求余运算符。
++ 自增 运算符,- - 自减 运算符 。
例如 main( )
{ int i = 36 ; i ++ ;
printf(” i = %d”,i); }
请注意,自增自减运算符在表达式中的位置:
++ i 先自增,后使用。
i ++ 先使用,后自增。
如 main( )
{ int i,j = 5 ; i = j ++ ;
printf(”i = %d,j = %d”,i,j) ; }
本程序运行结果输出是? 应该是 i = 5,j =6 而不是 i = 6,j = 6
先赋值 后自增上例程序若改为
main( )
{ int i,j = 5 ;
i = ++ j ;
printf (”i = %d,j = %d”,i,j) ; }
程序运行结果输出是?
应该是 i = 6,j = 6
先自增 后赋值
2-7 赋值运算符与赋值表达式赋值运算符 ( = )的作用是将一个数据赋值给一个变量。如赋值表达式 x = 25
即将 25 赋值给变量 x。 请注意变量的数据类型以及被赋值的数,即赋值运算符两边的数据类型。如已定义 x 是整型变量,而 x = 25.45 ; 则 小数被截。
复合赋值运算符
+= - = *= / = %= 等,如
x+=3 即 x=x+3
x / =a+b 即 x=x/(a+b)
请 看赋值表达式 x += x - =x *x
它的求解步骤是先进行 x - =x *x 运算,
即 x = x - x *x
若 x的初值为 3,则此时为 - 6,再进行
x =x+x 运算,得 x为 - 12。
2-8 逗号运算符与逗号表达式逗号运算符 即,,”
逗号表达式的形式是将两个表达式用逗号连接起来,即 表达式 1,表达式 2
如 x+5,y+12 先求解表达式 1后求解表达式 2 。请看逗号表达式
x++,x+y
若 x,y的 初始值分别为 12,3,则这个逗号表达式的值为 16。
逗号运算符的运算级别最低。
第三章 最简单的 C程序
3-1 C语句
C语句即计算机的操作命令。
C语句分为,
表达式语句 如 a+15 ;
控制语句 ( if goto while for switch
等 )
函数调用语句 printf(,Hello” ) ;
空语句 ;
3-2 程序的三种基本结构顺序结构分支结构循环结构
C 程序的组成:
若干语句以及说明部分组成函数,
若干函数以及编译预处理命令组成源文件,
若干源文件组成 C程序。
3-3 数据输出
C没有输入输出语句,输入输出操作是由函数来实现。
3-3-1 字符输出函数 putchar
功能 输出一个字符
char a = 65 ; putchar(a) ;
putchar(?A?) ;
putchar(?\n?) ;
请注意,能否 putchar(”A”) ;
3-3-2 格式输出函数 printf
函数的参数为 格式控制,输出表列 如
printf(” a=%d,x=%f”,a,x ) ;
格式控制是以,,括起来的字符串,输出表列是用逗号分开的变量名或表达式。
格式控制中,%以及其后格式字符为格式说明。
格式字符,d 用十进制整数形式输出
f 用十进制小数形式输出格式控制 输出表列格式输出函数 printf 的格式字符表格式字符 说 明
d 以带符号的十进制形式输出整数
o 以无符号的八进制形式输出整数
x 以无符号的十六进制形式输出整数
u 以无符号的十进制形式输出整数
c 以字符形式输出(一个字符)
3-4 数据输入
3-4-1 字符输入函数 getchar 无 参函数功能 输入一个字符 如
#include <stdio.h>
main( )
{ char c1 ; c1 = getchar( ) ;
putchar(c1) ; }
本程序运行时,若键入 a,则在显示屏上显示
a (键 ’ a’,然后回车送入内存)
a
这个 a是 putchar(c1)的 输出
3-4-2 格式输入函数 scanf
一般形式
scanf( 格式控制,地址表列)
main( )
{ int a,b,c ;
scanf(”%d%d%d”,&a,&b,&c) ;
printf(”%d,%d,%d\n”,a,b,c) ; }
本程序运行时,执行到 scanf等待用户键入数据。键入数据时,每个数据之间须用空白字符分开。
格式控制 地址表列格式输入函数 scanf 的格式字符表格式字符 说 明
d 以十进制形式输入整数
o 以八进制形式输入整数
x 以十六进制形式输入整数
c 输入一个字符
s 输入一个字符串,以空白字符结束
f 以十进制小数形式输入实数使用格式输入函数 scanf须注意,
1,函数的后一半参数是地址表列,而不是变量表列。如 scnaf(”%d,%d”,&x,&y) ;
不 能写为
scnaf(”%d,%d”,x,y) ;
应该是地址 &x,&y
2,函数的前一半参数格式控制字符串中,除格式说明以外,其他字符,则在输入时应键入这些字符。
第四章 逻辑运算和判断选取控制
4-1 关系运算符与关系表达式关系运算符,<(小于 ) <=(小于等于 )
>(大于 ) >=(大于等于 ) ==(等于 ) !=(不等于 )
优先级序为,< <= > >= == !=
关系表达式是用关系运算符将两个表达式连接起来的表达式。如 a+b>x+y
a<b+5 a>=3
关系表达式的值是一个逻辑值(真或假),
4-2 逻辑运算符与逻辑表达式逻辑运算符,&& (逻辑与 ) ||(逻辑或 ) !(逻辑非 )
&& 和 || 是双目运算符,! 是单目运算符。
优先级序为,! (最高 ) 算术运算符关系运算符 &&和 || 赋值运算符 (最低 )
逻辑表达式是用逻辑运算符连接起来的表达式。
如 a&&b x+y||a !a
逻辑表达式的值是真( )或假( )。
逻辑表达式的应用举例:
判断某年是不是闰年。
我们知道,地球绕太阳一转,需
365 天 5 小时 48 分 46 秒,
试设想,假如需 365 天 6 小时,则每
4 年一个闰年(设每个被 4 能除尽的年份为闰年)。
假如需 365 天 5 小时 45 分 36 秒
(即 5 小时 45.6 分),则每 100年减去一个闰年。但被减去太多,每 400 年再增加一个闰年。
逻辑表达式应用举例判别某年是否闰年,要看
( 1) 该年份(例 1996)能被 4整除,但不能被 100整除,
( 2) (该年份能被 4整除 ),又能被 400整除,
满足以上两条件之一即为闰年。设某年
year,因而有以下逻辑表达式:
( (year%4==0) && ( year%100!=0) ) ||
(year%400==0)
该逻辑表达式的值为 1即闰年;为 0则非闰请思考逻辑表达式中括号能省吗?
4能整除 100不能整除与 或
400能整除
4-3 if 语句
4-3-1 C 中的 if 语句有三种形式:
1,if (表达式 ) 语句
2,if (表达式 ) 语句 1 else 语句 2
3,if (表达式 1) 语句 1
else if (表达式 2) 语句 2
:
else if (表达式 m) 语句 m
else 语句 n
4-3-2 if 语句的嵌套
if语句中又包含 if语句
exa432.c
请注意 if 与 else的配对使用。
4-3-3 条件运算符?,
条件运算符是一个三目运算符,条件表达式的一般形式为:
表达式 1? 表达式 2,表达式 3
先求解表达式 1,若为非 0值,则求解表达式 2,否则求解表达式 3。如条件运算符应用举例
exa433.c
main( )
{ int a=33,b=44 ;
int max;
max=(a>b)? a+50,b+16 ;
printf(”%d\n”,max) ;
}
4-4 switch语句
switch 语句是分支选择语句,一般形式:
switch (表达式 )
{ case 常量表达式 1,语句 1; break;
case 常量表达式 2,语句 2; break;
:
Case 常量表达式 n,语句 n; break;
default,语句 n+1 }
当 switch表达式的值与某一个 case后的表达式的值相等时,就执行此 case
break表示执行语句后转出
default表示 1~n
表达式都不满足若所有 case后的常量表达式都与
switch表达式不匹配,则执行 default后的语句。
如果 case语句后不加 break,则执行了某一个 case语句后继续执行以下
case语句,包括 default后语句。
第五章 循环控制循环控制可用:
if 语句和 goto语句
for 语句
while 语句
do-while 语句
5-1 goto语句构成循环
main( )
{ int i,sum=0 ;
i =1;
loop,if ( i<100) // loop 语句标号
{ sum = sum +i ;
i++;
goto loop; }
printf(“”%d”,sum);
5-2 for 语句
C 中的 for 语句最灵活最常用,一般形式:
for (表达式 1 ; 表达式 2 ; 表达式 3) 语句;
它的执行过程是:
1) 先求解表达式 1 ; 2)求解表达式 2,若其值为真(非 0),则执行 for
内嵌语句,然后求解表达式 3; 3)转回求解表达式 2,直到其值为假,跳出循环。
例:
main( )
{ int i,sum=0 ;
for( i =1; i<100; i++ )
sum = sum +i ;
printf(”%d”,sum); }
现在我们来讨论:
1,省去表达式 1;
2,省去表达式 2;
3,省去表达式 3;
4,省去表达式 1,2,3,(分号不能
for ( ; ; ) ;
死循环 (或等中断 )
死循环,除非循环体中有 if语句等请看以下 for 语句:
for( i =1,sum=0 ; i<100; i++ )
sum = sum +i ;
for ( i =1,sum=0 ; i<100 ;
sum=sum+i,
i++ ) ;
for ( i=0; (c=getchar( )) !=’ \n’ ; i+=c ) ;
表达式 3
是一个逗号表达式循环空语句 是从键盘输入一个字符赋值给 c,判是否为回车不是,则继续循环
5-3 while,do-while 语句
while语句形式:
while (表达式 ) 语句;
它的执行过程是先判断表达式后执行内嵌语句。
do-while语句形式:
do 语句 while(表达式);
它的执行过程是先执行内嵌语句,后判断表达式。
5-4 break,continue 语句
5-4-1 break 语句它的功能是跳出(中止)循环,例:
for(r =1; r <=10; r ++)
{ area = PI * r * r ;
if (area>100) break ;
print(”%f\n”,area) ; }
5-4-2 continue 语句它的功能是提前结束本次循环。 例:
main( )
{ int year ;
//exa451.c
float money =10000 ;
for( year =1; year<= 20; year++ )
{ money= money *1.06125;
if( money> 20000) break;
printf (”%d %10.2f \n,,year,
money) ; }
printf(”**%d year
超过 20000不再存共 10位,
小数点后 2位已知 1999年是兔年,列出本世纪的所有兔年年份,统计有几个兔年。
main( )
//exa452.c
{ int i =0,year ;
for ( year =1900; year<2000; year ++ )
{ if ( ( year-1999)%12 ! = 0 )
continue ;
i ++ ;
printf(”%d \n”,year) ; }
如果不是兔年退出本次循环本程序运行结果输出:
1903
1015
1927
1939
1951
1963
1975
1987
1999
There are 9 Rabbit years
第六章 数 组数组是构造类型的数据。
6-1 一维数组的定义和引用定义方式,类型说明符 数组名 [常量表达式 ];
如 int a [ 5 ] ;
数组引用时,只能引用数组的元素,而不能指望引用整个数组。数组元素的表示形式为:
数组名 [下标 ]
main( )
{ int i,a[10] ;
for ( i =0 ; i <=10; i ++) a[ i ]=i ;
for ( i =9 ; i >= 0 ; i --) printf(”%d,,a[ i ]) ; }
C中数组元素下标从 0开始一维数组的初始化定义数组时对数组元素赋初值,如
static int
a[10]={1,3,5,7,9,11,13,15,17,19};
也 可以
static int a[10]={1,3,5,7,9};
//不赋值的元素初值为 0。还可以这样
static int a[ ]={1,3,5,7,9};
少于 10个元素可以数组长度允许空缺一维数组程序举例冒泡法排序 设有 5 个数排序:
12
56
25
3
78
56
25
12
78
3
56
25
78
12
3
56
78
25
12
3
78
56
25
12
3
大循环 第 1次 第 2次 第 3次 第 4次原数组第 1次大循环后数组排序后的数组小循环(比较) 4次 3次 2次 1次程序如下:
main( )
{ int a[5],i,j,t ; printf(”Input 5
numbers:\n”);
for ( i=0;i<5;i++) scanf(”%d”,&a[i]);
for (j=1;j<=4;j++)
{ for(i=1;i<=5-j;i++)
if (a[i]<a[i+1])
{ t=a[i];a[i]=a[i+1];a[i+1]=t;} }
for(i=0;i<5 ;i++) printf(”\n%d”,a[i]);
大循环 4次小循环元素上小下大则互换
6-2 二维数组二维数组的定义的一般形式:
类型说明符 数组名 [常量表达式 ] [常量表达式 ]
如 float a [3][4] ;
数组 a共有 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,3,5,7},{9,11,13,15},
{17,19,21,23}};
或 static int a[3][4]
={1,3,5,7,9,11,13,15,
17,19,21,23} ;
也可以 static int a[3][4]
={ {1,3,5,7},{9},{0,19,21} } ;
缺省的数组元素为 0
二维数组程序举例
main( )
{ static int a[2][3]
={ {1,3,5},{7,9,11} };
int b[3][2]; int i,j;
printf(” array a,\n,);
for (i=0;i<=1;i++)
{ for (j=0;j<=2;j++)
{ printf(” %5d“,a[i][j]);
b[i][j]=a[i][j]; }
二维数组程序举例(续)
printf(” array b,\n,) ;
for ( i=0; i<=2; i++ )
{ for ( j=0; j<=1; j++ )
printf(” %5d“,b[i][j] );
printf(,\n” );
}
}
十进制 5位整数形式换行
6-3 字符数组字符数组的每一个元素为字符,字符数组定义的形式:
char 数组名 [常量表达式 ]; 可 定义同时赋值:
static char ch1[ 6] =
{?H?,?e?,?l?,?l?,?o?,?!? };
static char ch1[ ] =
{?H?,?e?,?l?,?l?,?o?,?!? };
static char ch1[ ] = {,Hello!” }; / *用字符串常量给字符数组赋值,字符串结尾字符数组的输出
static char ch1[ ] = {,Hello!” };
printf(”%s”,ch1) ;
输出为
Hello! // 输出字符不包括结尾字符 \0
字符数组的输入
static char ch1[ 7] ;
scanf(”%s”,ch1) ; //此时键入 Hello!回车,
注意不能超过 6个字符,不足可以。
数组名即地址字符串处理函数
1 输出一个字符串 puts(字符数组名 )
static int string1[ ]={”China”};
puts(string1);
China
2 输入一个字符串 gets(字符数组名 )
gets(string2);
3 字符串拷贝 strcpy(字符数组 1,字符数组 2)
字符数组 str1的长度须大于等于 str2。
字符数组 str1 必须是字符数组名,而字符数组 str2可以是字符数组名,也可以是字符串常量。
如 strcpy (str1,”China”);
4 字符串比较 strcmp(字符数组 1,字符 数组 2)
strcmp(str1,str2); str1与 str2 均可是字符数组名或字符串常量。 Strcmp函数的返回值是:
str1=str2,则 函数的返回值为 0;
第七章 函 数
7-1 概 述
C 语言的基本单位是函数。函数相当于其他语言中的子程序。一个 C程序由一个主函数和若干个 函数组成。一个函数中可以调用若干个函数,反之,一个函数可以被多个函数调用,调用次数不限。
一个 C源程序文件 (***.c)是一个编译单位。
一个 C程序可由一个源程序文件或多个源程序文件组成。
C 程序的执行从 main函数开始,在 main函数结束,main函数由系统定义。
函数与函数之间是平行的独立的,不能在一个函数中定义另一个函数(所谓嵌套定义)。
函数可以互相调用(不能调用 main 函数),被调用的函数可以调用别的函数
(嵌套调用),并可以调用自己。
在程序设计中,常将一些常用的功能模块编写成函数,存放在函数库中(通常由软件厂商提供),函数库中的函数称为库函数。除了标准库函数外,很多软件公司还为用户提供许多实用的非标准的库函数。
用户要尽可能熟悉库函数。
7-2 函数的定义
1 无参函数的定义形式类型标识符 函数名( )
{ 说明部分语句 } //{ }中称为函数体,
若空缺,则为空函数。
2 有参函数的定义形式类型标识符 函数名(形式参数表列)
形式参数说明
{ 说明部分 语句 }
7-3 函数的参数和函数的返回值
7-3 -1 形式参数和实际参数
int sum( x,y ) // x,y为形参
int x,y ; // 形参说明
{ int z ; z=x+y ; return(z) ; }
main( )
{ int a,b,c ;
a=45 ; b=24 ; c = sum (a,b) ; // a,b为实参
printf(“a=%d,b=%d,
返回值函数的形参与函数的实参数据类型应一致。
实参变量对形参变量的数据传递是单向的值传递。只是由实参将它的值传递给形参,而不能由形参传回给实参。
函数调用时,系统给形参分配内存单元,调用结束后,形参单元被释放。
实参在函数调用前后不会变化。
ANSI新标准规定,形参说明可以类似
PASCAL所用方法,
int sum ( int x,int y )
形参表中作类型说明
7-3-2 函数的返回值函数的返回值是通过 return 语句获得的。
定义函数时应指定函数的类型(如缺省,
则约定为整型),函数的类型决定函数返回值的类型。
函数中若没有 return语句(系统会给一个警告),并不表示函数不带回返回值,
而只是不带回一个用户所需要的、有用的函数值。如果明确函数不带回值,则须在定义函数时,在函数名前冠以 void,表示空类型或无类型。如:
void putchar( c)
7-4 函数的调用
7-4-1 函数调用的一般形式无参函数调用,函数名 ( );
有参函数调用,函数名 (实参表列);
实参与形参须一一对应。
例 7-3-1
7-4 函数的调用
7-4-2 函数调用方式按函数调用在程序中的位置来分,可分为
1 函数语句 如
getchar( ) ;
2 函数表达式 如
c =100+sum(a,b) ;
3 函数 参数 如
printf(”%d\n”,sum(a,b)) ;
sum1= sum(c+sum(a,b) ;
函数的返回值作为另一函数的参数
7-4-3 对被凋函数的说明若函数 1调用函数 2,我们通常称函数 1
为主调函数,函数 2为被调函数。
被调函数如果是库函数,则须在主调函数所在的源程序文件的开头用 include命令包含相应的头文件(如 include < stdio.h
> )。
被调函数如果是用户自己定义的函数,
且被调函数与主调函数同在一个源文件中,
则一般需在主调函数中对被调函数的类型作说明(被调函数定义在前,则可省)。
说明的形式为函数调用语句前没有类型标识符
7-5 函数的嵌套调用被调函数也可以调用其他函数,层层嵌套,层数不限(例略)。
7-6 函数的递归调用函数直接或间接调用自己,称函数的递归调用。 C语言中,允许有限次的有终止的递归调用。例如用递归的方法求 n! 或求
xn就 是递推的问题,只要 n有限大,都能用函数的递归调用来求解。
函数的递归调用的应用例 - Hanoi塔问题设有三根针,名为 A,B,C。 在 A针 上放有 n
个大小不等的盘子(如图)。
A B C要求将 A针中的盘子搬到 C针中去,规定每次只能搬动一个盘,可以借助于 B针过渡,并必须保证每针中的盘,大盘在下,小盘在上。
搬动过程
A B C
程序如下:
void moveone ( get1,put1) // 搬一个盘函数
char get1,put1;
{ printf(”%c->%c\n”,get1,put1) ; }
void hanoi ( n,a,b,c) // 搬 n个盘函数
char a,b,c ; int n ;
{ if (n ==1) moveone( a,c ) ;
else { hanoi (n-1,a,c,b ) ;
moveone (a,c ) ;
hanoi (n-1,b,a,c ) ; }
hanoi函数递归调用递归调用
main ( )
{ int m ;
printf(”Input the number of the
diskes:\n”);
scanf(”%d”,&m) ;
printf(”The step to moving %3d
diskes,\n”,
m);
hanoi( m,?A?,?B?,?C? ) ;
}
本程序运行情况如下:
调用 hanoi函数
A-->C
A-->B
C-->B
A-->C
B-->A
B-->C
A-->C
7-7 数组作为函数参数数组作为函数参数是指数组名和数组的元素作函数参数。
数组的元素作函数(实际)参数与变量作函数参数用法一样,是实参向形参的单向的数值传递。
数组名可以作函数的形参和实参。请看程序例,有一个名为 score 的一维数组,
存放某班 30位同学的某课成绩,求全班平均成绩。
Exa771.c
请注意,数组名作函数参数时,不是单向的(实参到形参)值传递,而是实参数组的起始地址(数组名即数组的起始地址)
传递给形参数组,使两个数组共同占有一段内存。
因而,函数调用前后,实参数组是有可能被调用所改变的。
7- 8 局部变量和全局变量从变量的作用域来区分,变量分为局部变量和全局变量。函数内部定义的变量称为局部变量,它只在本函数内有效。在本函数外即无效,即不能在本函数外使用。
函数的形式参数同于局部变量,也只在本函数内有效。 不同函数中允许使用同名的变量(如常用的 i,j,n等)。我们在此前举例中多为局部变量。
在函数以外定义的变量称为外部变量,也称全局变量。全局变量为本程序文件的各函数所共用。它的使用范围是从变量被定义开例 7-8-1 求 10个学生成绩(一个一维数组)
的平均值、最高分和最低分。
float max=0,min=0; //定义两个全局变量
float average( 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]; }
数组作函数参数例 7-8-1 续
aver=sum/n ; return(aver) ; }
main( )
{ int i; float av,score[10] ;
for( i=0;i<10 ; i++)
scanf(” %f”,&score[i]) ;
av =average(score,10) ; //数组名作实参
printf(” max=%5.1f\nmin=%5.1f\navera
ge=%5.1f\n”,max,min,av) ;
如:
85 78.5 56 76 100 88.5 90 66 95 75
(回车)
max=100.0
min= 56.0
average= 81.1
本例中,在 main函数和 average函数中都用到了变量 max 和 min。 可见全局变量增加了函数间数据联系渠道。函数只能有一个返回值(本例中为 aver),但通过调用函数
average,我们得到了三个有用的值,其中如外部变量定义在后,而使用该变量的函数在前(或要使用别的程序文件中定义的外部变量),则在该函数中应对该被使用的外部变量作说明。说明的方式是:
extern x,y ; // x,y 为在后面定义或别的源程 //序文件中定义的函数。
此外,全局变量一般不要和局部变量同名,如果同名,则则在该具备变量所在的函数中,全局变量不起作用。
外部变量不宜用得太多,太多了被占的内存多,且程序的通用性不好。
7- 9 动态存储变量和静态存储变量
7- 9-1 变量的存储类别(数据的存储类别属性)
从变量的生存期(时间域)来分,变量可分为动态存储变量和静态存储变量。
动态存储变量是在程序运行时根据需要动态分配存储空间的变量。即需要时,分配给内存单元,不需要时就释放内存单元。
静态存储变量局是指程序运行期间分配固定的存储空间的变量。程序运行结束才释放内存单元。它的寿命与程序周期一样长。
静态存储区动态存储区堆 栈 区
Commend.com
程序代码区存放全局变量和静态局部变量存放局部变量和形式参数
7- 9-2 局部变量的存储方式一,局部变量如果不作特别说明就是存放在动态存储区中的动态存储变量,对它们的存储空间分配和释放由编译程序自动处理的,因而又被称为自动变量。自动变量由关键字 auto作存储类型说明。而通常
auto可省略。函数的形式参数与它一样。
它们的寿命与函数一样长。
二、静态局部变量是函数内部定义的需要特别说明的变量,它的寿命与程序一样长。函数调用结束,静态局部变量所占的内存空间不释放,其值保留。它的定义
7- 9-2 全局变量的存储方式全局变量(外部变量)在函数外定义,
编译时给全局变量分配静态存储空间。全局变量都是静态存储变量,可以由各个函数所引用。
如果一个文件中定义的外部变量只允许本文件中的函数调用,不允许别的文件中的函数调用,则称为静态外部变量,需用
static来说明。如 static int x ; //在函数外
7- 9-3 变量存储类别小结为了使大家对各中存储类别的变量有一按作用域分类 作用域(空间)
局部变量全局变量动态局部变量 (自动 )
静态局部变量寄存器变量函数形式参数函数外静态变量外部(全局)变量本函数或分程序内有效,
函数或分程序外不能引用只限本文件引用允许其它文件引用名 称按寿命分 类 寿命(存在时间)
动 态存储变量静 态存储变量自动局部变量寄存器变量函数形式参数函数外静态变量外部(全局)变量寿命与函数相同离开函数值就消失离开函数值仍保留寿命与程序执行周期相同名 称静态局部变量
7- 10 C库函数简介
C语言本身并不包括库函数,但各软件公司为方便用户在编译系统中提供大量的库函数。除 ANSI标准库函数外,各版本的 C
编译系统还提供实用的非标准库函数,其中与操作系统接口函数以及计算机的屏幕和图形函数各系统有不少差别。
标准库函数请参看,C程序设计题解与上机指导》(清华版)第四部分( P
176)。
学习使用库函数的方法之一是多使用系统的帮助文件 HELP,那里有各函数的说请大家多多使用 HELP!
本周上机时间:
周二 周三下午
2.00~5.00
下周起时间请与陈冬红老师联系