? 第一章 C语言概述
? 第二章 数据类型、运算符与表达式
? 第三章 最简单的 C程序设计
? 第四章 逻辑运算和判断选取控制
? 第五章 循环控制
? 第六章 数组
? 第八章 编译预处理
? 第九章 指针
? 第十章 结构体与共用体
? 第十二章 文件的基本操作
? 第十三章 位运算
§ 7.1 概述
1) 除 main外,其它为系统函数、自编函数,
系统函数,由系统提供,放在不同的头文
件中,用户可调用。
自编函数,由用户按语法规则编写。
C程序由一个 main和任意个函数组成。
2) 除 main函数 外,其它函数可相互调用
main ( )
a b c
d x z
?
3)函数不可嵌套定义,但可以嵌套调用。
4)函数分为有参与无参函数
5)程序从 main开始执行,最后又回到 main函
数结束。
§ 7.2 定义与调用
[存储类型符 ] [返回类型符 ] 函数名 ( )
{ 说明部分
语句
}
1,无参函数
?定义形式
注意,
存储类型符有两种,static型 和 缺省型。
返回类型符表示函数的返回值类型。
? 调用方式
第 1种, 变量 =函数名 ( );
注,变量名的类型与函数返回值的类型必须相同,
第 2种, 函数名 ( );
[存储类型符 ] 类型标识符 函数名 (形参表列 )
形参说明
{ 说明部分
语句
}
2,有参函数
? 定义形式
? 调用方式
变量名 =函数名 (实参表列 );
例, 求二数之最大值
int max (x,y)
int x,y;
{int z;
z=x>y? x:y;
return (z);
}
用 return语句,返回函数的值。
或,
int max (int x,int y)
{int z;
z=x>y? x:y;
return (z);
}
3,形参与实参
调用时, 实参值 单向 传递 形参。
函数被调用时,临时分配单元给形参,调用完
毕,这些单元被释放。
注, ? 实参可为表达式,只传递表达式的值。
实参, 出现在调用函数中,形参, 出现被调用函数中。
? 实参、形参类型一致。
? 可在形参表列中对形参说明。
4,函数返回值
变量的值
通过 return语句将流程返回主调函数。
return (表达式的值 );
5,函数声明,
一般的函数被调用之前必须做出说明,
例 1,求二实数之和
#include <stdio.h>
main ( )
{ float add( ); /*函数说明 */
float a,b,c;
scanf(― %f,%f‖,&a,&b);
c=add(a,b); /*函数调用 */
printf ("sum= %f"; c);
}
说明格式,类型符 函数名 ( );
float add( float x,float y); /*函数定义 */
{ float z;
z=x+y;
return z;
}
若函数的定义放在 main( )函数之前 可省
略函数说明 。
例 2,求二实数之和 (将例 1程序改写如下,)
#include <stdio.h>
float add( float x,float y); /*函数定义 */
{ float z;
z=x+y;
return z;
}
main ( )
{ float a,b,c;
scanf(― %f,%f‖,&a,&b);
c=add(a,b); /*函数调用 */
printf ("sum= %f"; c);
}
6,调用系统函数,需根据系统提供的库函数手册而
确定加上预编译命令,如:要调用 getchar ( ) 函
数,需要加上,
#include "stdio.h "
所有系统提供的库函数都放在头文件,h中
§ 7.3 嵌套调用
函数不能嵌套定义,但可以嵌套调用。
a( )
{,
,
调用 b
,
,
}
b( )
{,
,
,
,
, }
main
{,
,
调用 a
,
,
}
例 1,/*---exp81.c---*/
#include <stdio.h>
int fun1(int x,int y);
char fun2(int t)
int a,b;
main( )
{ int u;
a=24; b=42;
printf("main(1)----a=%d,b=%d\n",a,b);
u=fun1(a,b);
printf ("main(2)----a=%d,b=%d,u=%d\n",a,b,u);
}
int fun1 (int c,int y)
{ int z;
char ch;
z=x+y;
ch=fun2(z);
printf("fun1-------ch=%c\n",ch);
return(z);
}
char fun2(int t)
{ t=a+b;
printf("fun2-------t=%d,t=%c\n",t,t);
return(z);
}
运行结果,
main(1)----a=24,b=42
fun2--------t=66,t=B
fun1--------ch=B
main(2)----a=24,b=42,u=66
例 2,用弦截法求方程的根。
x3–5x2+16x–80=0
方法如下,
(1) 取两个不同点 x1,x2,如果 f(x1)和 f(x2)符号
相反, 则 (x1,x2)区间内必有一个根 。 如果 f(x1)
与 f(x2)同符号, 则应改变 x1,x2,直到 f(x1)、
f(x2)异号为止 。 注意 x1,x2的值不应差太大,
以保证 (x1,x2)区间只有一根 。
(2) 连接 f(x1)和 f(x2)两点,此线 (即弦 )交 x轴于 x,见
图 7.6,
)()(
)()(
12
1221
xfxf
xfxxfxx
?
????
再从 x 求出 f(x)。
见图 7.6,
x
y
x
f(x1) f(x)
x1 x2
f(x2) x点坐标可用下式求出,
(3) 若 f(x)与 f(x1)同符号,则根必在 (x,x2)区间内,
此时将 x作为新的 x1。 如果 f(x)与 f(x2)同符号,
则表示根在 (x1,x2)区间内,将 x作为新的 x2,
(4) 重复步骤 (2)和 (3),直到 |f(x)|<?为止,?为一个
很小的数,例如 10- 6。此时认为 f(x)≈0,
根据上述思路画出 n-s流程图,见图 7.7
输入 x1,x2,求 f(x1),f(x2)
直到 f(x1)与 f(x2)异号
求 f(x1)与 f(x2)连线与 x轴
的交点 x
y=f(x),y=f(x1)
y与 y1同号
x1=x
y1=y
x2=x
y2=y
直到 |f(x)|<?
root=x 输出 root
真 假
root函数
图 7.7
其程序由若干个函数构成,
# include "math.h"
float f(x) /* 函数 1定义,计算 f(x)=x3- 5x2+16x- 80*/
float x;
{ float y;
y=((x- 5.0)*x+16.0)*x- 80.0;
return (y);
}
float xpoint (x1,x2) /*函数 2定义,求出弦与 x轴交点 */
float x1,x2;
{ float y;
y=(x1?f(x2)- x2?f(x1)/(f(x2)- f(x1);
return(y);
}
float root(x1,x2); /*函数 3定义,求近似根。 */
float x1,x2;
{ int i;
float x,y,y1;
y1=f(x1);
do
{ x=xpoint(x1,x2); /*函数 2调用 */
y=f(x);
if (y*y1>0) /*f(x)与 f(x1)同符号 */
{y1=y;
x1=x;}
else
x2=x;
} while (fabs(y)>=0.0001);
return (x);
}
main( ) /*主函数 */
{ float x1,x2,f1,f2,x;
do
{ printf(― input x1,x2,\n‖);
scanf(%f,%f,&x1,&x2);
f1=f(x1); /*函数 1调用 */
f2=f(x2); /*函数 1调用 */
}
while (f1?f2>=0);
x=root (x1,x2); /*函数 3调用 */
printf("A root of equation is %8.4f",x);
}
程序运行结果如下,
input x1,x2,
2,6?
A root of equation is 5.000
§ 7.4 递归调用
递归, 一个函数直接或间接地调用自身。
1,直接递归调用,函数直接调用本身
2,间接递归调用,函数间接调用本身
直接调用,
int f(x)
int x;
{ int y,z;
?
z=f (z);
?
}
间接调用,
int f1 (x)
int x;
{ int y,z;
?
z=f2 (y);
?
}
int f2 (t)
int t;
{ int a,b;
?
a=f1 (b);
?
}
以下表示了递归的概念,
显然, 上述例子会无限递归 (无限执行 )。所以,在递
归调用时都必须有条件限制。
n!=
1 (n=0,1)
n?(n–1)! (n>1)
当条件成立,调用递归,否则结束。
例 1,求 n!
1,从数学上定义
2,程序,
#include <stdio.h>
long fac(int n) /*函数定义,计算 n! */
{ long f;
if (n<0)
printf("input error!\n");
else if (n= = 0 |n= =1)
f =1;
else f =n?fac(n –1);
return (f);
}
main ( )
{ int n;
long y;
printf("input a integer! ")
scanf ("%d",&n);
y=fac(n); /*函数调用,计算 n! */
printf("%d!=%15ld",n,y);
}
3,执行过程, 设, 输入 5? (n=5)
第一次调用,y=fac(5) —— 返回,y=5fac(4)
main ( )
{
?
y=5?fac(4);
?
}
fac(4)
{
?
f=4?fac(3);
?
return f;
}
fac(3)
{
?
f=3?fac(2);
?
return f;
} ? –
fac(2)
{
?
f=2?fac(1);
?
return f;
}
fac(1)
{
?
f=f (1);
?
return f;
}
简化表示为,
当变成机器代码时,将其拉成直线 (线性程
序代码 )。
例 2,汉诺塔 (Hanoi)问题
B C
问题, 将 A塔上 n个盘子移至 C(借助于 B)。 移动时,
保证三个塔始终是大盘在下,小盘在上。
A
n个盘子
必须用递归方式解决
1) 先将 A塔 n –1个盘子借助于 C移至 B上
2) 将 A上剩下的一个移至 C上,
3) 将 B上 n –1个盘子借助于 A移至 C上,
可以看到,
1),3)为同一问题,都为 n –1个盘子借助于一个
空塔移至另一塔上。
程序如下,
#include <stdio.h>
void move (char getone,char putone) /*函数定义 */
{ printf(― %c– –>%c\n ‖,getone,putone);
}
void hanoi (int n,char one,char two,char three)
/*将 n个盘从 one借助 two,移到 three*/
{ if (n= =1)
move (one,three);
else
{ hanoi (n–1,one,three,two);
move (one,three); /*函数调用 */
hanoi (n–1,two,one,three); }
}
main ( )
{ int m;
printf (" input the number of diskes ",);
scanf(" %d ",&m);
printf(" The step to moving %3d diskes:\n ",m);
hanoi (m,?A‘,?B‘,?C‘); /*函数调用 */
}
运行情况如下,
input the number of diskes:3 ?
The step to moving 3 diskes,
A ? ?>C
A ? ?>B
C ? ?>B
A ? ?>C
B ? ?>A
B ? ?>C
A ? ?>C
在程序中有两个函数,
? move (getone,putone)
表示从 getone 塔移一个盘子至 putone塔
? hanoi(n,one,two,three)
表示 n个盘子从 one塔借助于 two塔 (空 )移至 three塔。
调用时塔用字符常量 'A',' B ',' C '表示。
§ 7.6 局部变量与全局变量
一、局部变量
凡在函数 (含 main 函数 )内部定义的变量称为
局部变量。
局部性, 局部变量仅在函数内部有效。
2,形参为局部变量。
3,在复合语句中可定义仅复合语句中有效的临时
变量。
1,不同的函数可具有同名的变量,它们占不同的内
存单元,互不影响。
二、全局变量
例, int p=1,q=5;
float f1(a)
int a;
{int b,c;
?
}
char c1,c2;
p,q的作用范围
c1,c2的作用范围
一个源文件中,在所有函数之外定义的变量为
全局变量。
有效性, 自定义位置开始至文件结尾全部有效。
char f2(x,y);
int c,y;
{ int i,j;
?
}
main ( )
}
?
}
1,全局变量所作用到的函数,相当于这些函数的公
共变量。于是,当一个函数对其值进行改变后,另
一个函数使用该变量的值亦相应改变。 好处, 函
数之间值传递。
2,不要随意使用全局变量。一是始终占据内存单
元;二是由于函数依赖于外部定义的变量,减
少了通用性。
3,不在作用域内函数。若使用全局 (外 )变量,需在
函数体内加上 extern保留字。
4,全局和局部变量同名时,局部变量有效。
float f1 (x)
int x;
{extern int a,b;
?
}
int a= 0; b= –1
main ( )
{
?
}
a,b作用域
例, /*----exp81.c----*/
#include<stdio.h>
int fun1(int x,int y);
int a,b,z;
main( )
{ int u,m=0;
a=24; b=42;
printf("(1)----------a=%d,b=%d,m=%d\n",a,b,m);
m=m+10;
u=fun1(a,b);
printf("(2)----------a=%d,b=%d,u=%d,m=%d\n",a,b,u,m);
m=m+10;
a=z?a; b=z?b;
u=fun1(a,b);
printf("(3)----------a=%d,b=%d,u=%d,m=%d\n",a,b,u,m);
}
int fun1(int x,int y)
{ int m;
m=x+y;
printf("chang a&b:---a=%d,b=%d,z=%d,m=%d\n",
a,b,z,m);
a=a+10; b=b+20;
}
z=x+y;
return (z);
(1) ----------a=24,b=42,m=0
运行结果,
chang a&b:---a=34,b=62,z=66,m=66
(2) ----------a=34,b=62,u=66,m=10
chang a&b:---a=42,b=24,u=36,m=36
(3) ----------a=42,b=24,u=36,m=20
注意,
? 上面程序中 a,b,z是全局变量,u,m为局部
变量;
? 若将 int a,b,z; 语句放入 main( )函数中,情
况会怎样?
§ 7.7 动态存储变量与静态存储变量
一、变量的存储类别
程序区
静态存储区
动态存储区 数据,变量存放
内存分配
表达了一个变量存在的空间、时间。
静态存储变量, 存放于静态存储区,在程序整个运行
过程中,始终占据固定的内存单元。
动态存储变量, 存放于动态存储区,根据程序的运
行状态 (如:函数调用 )而临时分配
的单元,且单元并不固定。
变量又可分为四种具体形式
2,静态 (static)变量
3,寄存器型变量
4,外部 (extern)变量
前面学习的局部、全局变量均以上述方式中
的一种形式存储。
1,自动型变量 (auto)
局部变量既可以静态方式,又可以动态方式存储。
动态方式, auto int a,b;
二、局部变量
则, a,b为自动型,存入动态区。在该函数被
调用时才分配单元,函数调用结束时释放。
auto一般省略。以前用到的变量均为 auto型,除
static外。
? 若定义时赋初值,则程序运行中仅在第一次调用
时赋初值,第二次调用不再赋初值,而是使用上
一次调用的值。
则, a,b存入静态区。 函数中的 a,b始终占据固定
存储单元。
静态方式, static int a,b;
#include <stdio.h>
int fac (n) /*函数定义 */
int n;
{ static int f=1;
f=f?n;
return(f);
}
main ( )
{ int i;
for (i=1; i<=5; i++)
printf ("%d!=%d\n",i,fac(i));
}
例:求 n!
运行结果为,
1!= 1
2!= 2
3!= 6
4!= 24
5!= 120
每一次调用 fac(i),打印一个 i!,同时保留
这个 i!的值以便下次再乘 (i+1)。
? 若不赋初值,则系统置初值 0,而动态变量不赋初值
则值不确定。
当动态局部变量在一个函数中反复被用达到数
百次以上,为了提高效率,可将其存入寄存器中
(有限个 ),不存入内存的动态区中。
说明方式 register int i,j=1;
? 不可太多,一般 1––3个
? 必要时使用。
三、全局变量
在函数外部中定义,它们一定存放在静态存
贮区中。
全局变量即可被本文件中各函数用,亦可被其
它源文件中的函数引用。
1,只被本文件中的函数引用
全局变量本身一定是存放在静态区的。但若加
上 staic,即,
static int a,b;
float f1(x)
int x
{?
}
则表明 a,b只被本文件
中各函数引用,即使
与其它文件中的全局
变量同名,也互不影
响。
2,可被其它文件中的函数引用
int a;
main( )
{
?
}
extern int a;
fac(x)
int x
{ ?
z=a??
?
}
文件 f1.c 文件 f2.c用到 f1.c
中的 a
f2.c中的 extern在函数外说明,在函数内说明
已叙述过。
存储类别
总结 见表 7.2
函数内 函数外
作用域 存在性 作用域 存在性
auto ? ? ? ?
register ? ? ? ?
static 局部 ? ? ? ?
static 外部 ? ? 本文件 ?
? ? ? ? 不加 static 全局 (外部 )
§ 7.8 内部函数和外部函数
函数本身在一个文件中为全局的。即一个文
件中定义的函数可被该文件的所有其它函数引用。
一、内部函数 –––只能在本文件中调用
问题,函数能否被其它文件中的函数所
引用呢?
static 类型标识符 函数名 (形参表 )
例, static int max (a,b)
int a,b;
{ ?
?
}
则该函数 max只能被本文件中的其它函数引用,
而不能被其它文件中的函数引用。
既可被本文件中的函数调用,也可被其它文件
中的函数调用。
extern 类型标识符 函数名 (形参表 )
一般系统在调用外部文件的函数中必
须用 extern说明外部函数。
二、外部函数
extern 可省略
例,有一个字符串,内有若干个字符,程序将字
符串中该字符删除去。用外部函数实现。
file1.c(文件 1)
main ( )
{extern enter_string ( ),delete_string( );
extern 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 enter_string (str) /*定义外部函数 enter_string*/
char str[80];
{ gets (str);
} /* 读入字符串 str */
file3.c (文件 3)
extern delete_string (str,ch)
/* 定义外部函数 delete_string */
char str [ ],ch;
{ int i,j;
for (i=j=0; str[i]!=' \0 '; i++)
if (str[i]!=ch)
str[j++]=str[i];
str[i]= ' \0 ';
}
file4.c (文件 4)
extern print_string (str)
/* 定义外部函数 print_string */
char str[ ];
{ printf("%s",str);
}
运行情况如下,
abcdefgc? (输入 str)
c ? (输入要删去的字符 )
abdefg (输出已删去指定字符的字符串 )
连接, link file1+file2+file3+file4
主函数
或者, 在 main ( )的前面加上,
#include "file2.c"
#include "file3.c"
#include "file4.c"
编译, 分别编译 file1.obj,file2.obj,file3.obj,file4.obj