当前位置:首页 >> 计算机软件及应用 >>

第5章函数(C语言基础)


第5章 函数
§5.1 函数的基本概念
C程序由一个main和任意个函数组成。 1) 除main外, 其它为系统函数、自编函数, 系统函数:由系统提供,放在不同的头文 件中,用户可调用。 自编函数:由用户按语法规则编写。

2) 除main函数外,其它函数可相互调用 main ( ) a

b ? c
x z

d

3)函数不可嵌套定义,但可以嵌套调用。 4)函数分为有参与无参函数 5)程序从main开始执行,最后又回到main函 数结束。

例5.1 int max(int a,int b) { if(a>b)return a; else return b; } void main() { int max(int a,int b); int x,y,z; printf("input two numbers:\n"); scanf("%d%d",&x,&y); z=max(x,y); printf("maxmum=%d",z); }

§5.2 函数的定义
1. 无参函数 ?定义形式 [返回类型符] 函数名( ) { 说明部分

语句
} 注意:

返回类型符表示函数的返回值类型。

例: void Hello() { printf ("Hello,world \n"); } 这里, Hello为函数名, Hello函数是一个无参函数,当被 其它函数调用时,输出Hello world字符串。

2. 有参函数
? 定义形式 类型标识符 函数名(形参表列)

形参说明
{ 说明部分

语句 }

例: 求二数之最大值

或:
int max (int x, int y) {int z; z=x>y? x:y; return (z);

int max (x, y)
int x, y; {int z; z=x>y? x:y; return (z); }

}

用return, 语句,返回函数的值。

§ 5.3函数的调用
5.3.1形式参数和实际参数

函数的参数分为形参和实参两种。 ? 形参出现在函数定义中,在整个函数体 内都可以使用,离开该函数则不能使用。 ? 实参出现在主调函数中,发生函数调用 时,主调函数把实参的值传送给被调函 数的形参从而实现主调函数向被调函数 的数据传送。

形参与实参 实参: 出现在调用函数中, 形参: 出现被调用函数中。
单向 调用时: 实参值 传递

形参。

函数被调用时,临时分配单元给形参,调用完 毕, 这些单元被释放。 注: ? 实参可为表达式,只传递表达式的值。 ? 实参、形参类型一致。 ? 可在形参表列中对形参说明。

例5.3: 本程序运行时,输入5,运行结 void main() 果为:n=5,k=6 { 在执行函数过程中,形参m的 int k,n; 值变为6。返回主函数之后,输 printf("input number\n"); 出实参n的值仍为5。可见实参 scanf("%d",&n); 的值不随形参的变化而变化。 k=s(n); printf("n=%d,k=%d\n",n,k); } int s(int m) { 思考:程序做如下改动,会出现什么问题? m=m+1 如果将程序第7行改为printf("n=%d,k=%d,m=%d",n,k,m); return m; 如果将程序第6行改为k=s(n,n); }
如果删除程序中的scanf("%d",&n);

例5.4:请分析程序的运行结果。 #include"stdio.h" void main() {int a=3,b=5; swap(a,b); printf("a=%d,b=%d\n",a,b); } swap(int x,int y) {int temp; temp=x;x=y;y=temp; printf("x=%d,y=%d\n",x,y); }

5.3.2函数的调用
? C语言中,函数调用的一般形式为: ? 函数名(【实际参数表】) ? 对无参函数调用时则无实际参数表。实 际参数表中的参数可以是常数,变量或 其它构造类型数据及表达式。各实参之 间用逗号分隔。

函数调用的方式一般有两种: ?函数表达式: 函数作为表达式中的一项出现在表达式中,以 函数返回值参与表达式的运算。这种方式要求函数是有返回 值的。 ?函数语句: 函数调用的一般形式加上分号即构成函数语句

例5.5求二实数之和 #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); } 其中c=add(a,b)是一个赋值表达式,把add的返回 值赋予变量c。

例5.6 #include"stdio.h" void printstar() { printf("\n***************\n"); } void print_message() { printf("How do you do !\n"); } void main() {printstar(); print_message(); printstar(); }

程序运行结果为: *************** How do you do ! *************** 函数printstar和 print_message都没有返 回值,调用时函数名后直 接加分号作为语句

5.3.3 函数声明:
一般的函数被调用之前必须做出说明: 说明格式:类型符

例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); }

5.3.4 函数返回值
return (表达式); 由函数名只能得到一个返回值。 变量等 ? 通过return语句将返回值传给函数名,可有 多个return. ? 返回值类型为函数类型。 一般return中的返 回值类型应与函数定义时的类型一致, 不一致时, 以函数定义类型为准。
?不返回函数值的函数,可以明确定义为“空类

型”,类型说明符为“void”。函数被定义为空 类型后,函数中的return语句不能带有表达式

例5.8:
思考: ? ? ?

? i ? 1 ? 2 ? ? ? 100
i ?1

100

如何考虑将函数设 计为有参数或无参 数 函数的通用性 如何设定函数的返 回值

#include <stdio.h> main ( )
{int s; s=f(); printf("%d",s);
}

? int f() ? {int i=1,sum=0; do {sum=sum+i; i++; } while (i<=100); ? return sum; ? }

例5.9:

i ?1 #include <stdio.h> main ( ) {int m,s; scanf(―%d‖,&m); s=f(m); printf("%d",s);
}

?i ? 1? 2 ??? n

n

? int f(int n) ? {int i=1,sum=0; do {sum=sum+i; i++; } while (i<=n); ? return sum; ? }

§5.4 嵌套调用
函数不能嵌套定义,但可以嵌套调用。

嵌套调用 : 调用一个函数的过程中又调用另一函数
main { : : 调用a : : } a( ) {: : 调用b : : } b( )
{: : : : :}

例5.11计算s=52!+82! 本题可编写两个函数,一个是用来计算平方值的函 数f1,另一个是用来计算阶乘值的函数f2。

long f1(int p) { int k; long r; long f2(int); k=p*p; r=f2(k); return r; }

long f2(int q) { long c=1; int i; for(i=1;i<=q;i++) c=c*i; return c; }

main() { int i; long s=0; for (i=5;i<=8;i=i+3) s=s+f1(i); printf("\ns=%ld\n",s); }

例5.12 求三个数中最大数和最小数的差值 int dif(int x,int y,int z) #include <stdio.h> { return max(x,y,z)-min(x,y,z); } int dif(int x,int y,int z); int max(int x,int y,int z) int max(int x,int y,int z); { int r; int min(int x,int y,int z); r=x>y?x:y; void main() return(r>z?r:z); { int a,b,c,d; } scanf("%d%d%d",&a,&b,&c); int min(int x,int y,int z) d=dif(a,b,c); printf("Max-Min=%d\n",d); { int r; r=x<y?x:y; } return(r<z?r:z); main( ) dif函数 max函数 }
调用函数dif 输出 结束 调用函数max 调用函数min

min函数

§5.5递归调用
递归: 一个函数直接或间接地调用自身。

1. 直接递归调用:函数直接调用本身
2. 间接递归调用:函数间接调用本身

以下表示了递归的概念. 直接调用: int f(x) int x; { int y, z; 间接调用: int f1 (x) int x; { int y, z; int f2 (t) int t; { int a, b; ? a=f1 (b); ? }

? z=f (z);
? } }

? z=f2 (y);
?

显然: 上述例子会无限递归 (无限执行)。所以, 在 递归调用时都必须有条件限制。 当条件成立,调用递归,否则结束。 例5.13: 求n!

1. 从数学上定义
1 (n=0, 1) (n>1)

n!=

n· (n–1)!

2. 程序:

#include <stdio.h> long fac(int n) /*函数定义, 计算n!*/ { long f; if (n<0) printf("input error!\n"); else if (n= = 0 ? =1) ? n= 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)

fac(4) main ( ) { ? y=5?fac(4); ? } {

fac(3) {

?
f=4?fac(3);

? f=3?fac(2); ? return f; }?–

?
return f;

}

fac(2)

fac(1) {

{
?

?
f=f (1);

f=2?fac(1);
?

?
return f;

return f;
}

}

简化表示为:

当变成机器代码时,将其拉成直线(线性程 序代码)。

例5.14递归计算x的y次方

#include <stdio.h> int power(int x, int y) /* 计算x 的 y 次方的递归函数 */ { if(y == 0) return 1; /* 任何不等于0的数的0次方为1 */ return x*power(x, y-1); } void main() { int x, y; printf("请输入 x 和 y :"); scanf("%d%d", &x, &y); printf("%d^%d = %d\n", x, y, power(x, y)); }

例5.15: 汉诺塔(Hanoi)问题 问题: 将A塔上n个盘子移至C(借助于B)。 移动时, 保证三个塔始终是大盘在下,小盘在上。 A B

C

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 '表示。

§5.6变量的存储类别和作用域

5.6.1局部变量与全局变量
一、局部变量 凡在函数(含main 函数)内部定义的变量称为 局部变量。 局部性: 局部变量仅在函数内部有效。 1. 不同的函数可具有同名的变量,它们占不同的内 存单元,互不影响。 2. 形参为局部变量。 3. 在复合语句中可定义仅复合语句中有效的临时 变量。

float f1( a ) int a ; { int b, c; ┋ } char f2(x,y) int x,y; { int i, j; ┋ } main() { int m,n; ┋ }

函数 f1 a ,b ,c 有效

函数 f2

x, y, i, j 有效

主函数 m, n 有效

二、全局变量 一个源文件中,在所有函数之外定义的变量为 全局变量。 有效性: 自定义位置开始至文件结尾全部有效。 例: 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作用域

?
}

例5.16 #include <stdio.h> int max (int x, int y) { int z; z = x>y ? x : y; return z; } void main ( ) { extern int a, b; /* 全局变量外部说明 */ printf ("%d\n", max (a,b)); } int a = 10, b = 5; /* 全局变量定义 */

例5.17输入正方体的长宽高l,w,h。求体 积及三个面x*y,x*z,y*z的面积。 int s1,s2,s3; int vs( int a,int b,int c) main() { { int v; int v,l,w,h; v=a*b*c; printf("\ninput length,width and height\n"); s1=a*b; scanf("%d%d%d",&l,&w,&h); s2=b*c; v=vs(l,w,h); s3=a*c; return v; printf("\nv=%d,s1=%d,s2=%d,s3=%d\n",v,s1 } ,s2,s3); } 分析:为何将s1,s2,s3定义为全局变量?

例5.18 外部变量与局部变量同名。 int a=3,b=5; /*a,b为外部变量*/ max(int a,int b) /*形参a,b为内部变量*/ {int c; c=a>b?a:b; return(c); } main() {int a=8; /*a为内部变量*/ printf("%d\n",max(a,b)); }

5.6.2 变量的存储类别
变量的存储类别 表达了一个变量存在的时间。

程序区
内存分配 静态存储区 动态存储区 数据,变量存放

静态存储变量: 存放于静态存储区,在程序整个运行 过程中,始终占据固定的内存单元。

动态存储变量: 存放于动态存储区,根据程序的运 行状态(如:函数调用)而临时分配 的单元,且单元并不固定。

变量又可分为四种具体形式 1. 自动型变量(auto) 2. 静态(static)变量
3. 寄存器型变量

4. 外部(extern)变量 前面学习的局部、全局变量均以上述方式中 的一种形式存储。

局部变量 局部变量既可以静态方式 , 又可以动态方式存储。 动态方式: auto int a, b; 则: a, b为自动型,存入动态区。在该函数被 调用时才分配单元,函数调用结束时释放。

auto一般省略。以前用到的变量均为auto型,
除static外。

静态方式: static int a, b;

则:

a, b存入静态区。 函数中的a, b始终占据固定 存储单元。

? 若定义时赋初值,则程序运行中仅在第一次调
用时赋初值,第二次调用不再赋初值,而是使用

上一次调用的值。

例5.20:求n! #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)); }

运行结果为: 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) 则表明a,b只被本文件 中各函数引用,即使 与其它文件中的全局 变量同名,也互不影

int
{?

}

响。

2. 可被其它文件中的函数引用 extern int a; int a; main( ) { ? } 文件f1.c fac(x)

int x
{?

z=a??
?

文件f2.c 用到f1.c 中的a

}
f2.c中的extern在函数外说明,在函数内说明 已叙述过。

总结

见表 函数内 作用域 存在性 ? ?

存储类别 auto

函数外 作用域 存在性
? ?

register static 局部 static 外部
不加 static 全局(外部)

? ? ? ?

? ? ? ?

? ? 本文件 ?

? ? ? ?

§5.7编译预处理

?目的: 为了简化程序的编写,提高程序的可移植 性、可读性、模块化。 C语言设计了若干

命令–––编译预处理命令,并可出现于程
序当中。

有三种类型的预处理命令: 1. 宏定义命令; 2. 文件包含命令; 3. 条件编译命令。 为了与C语句区别,这些命令均以“ #”开头。

处理流程: 第一次编译扫描时,将预编译命令处理完, 然后再进行正式编译,生成目标代码。

§5.7.1 宏定义
简化程序的书写, 提高可读性、可移植性。 宏定义分为: 无参数和有参数二种形式。 一、无参数形式

# define 标识符 字符串表达式

实际处理时:

用“ 字符串表达式”替换程序中的标识符(宏名)
例: #define PAI 3.14159 #define Array_size 500 ? int m[Array_size];

相当于:int m[500];

1. 宏定义的作用域: 从开始定义的位置至文件结尾,但允许提前终 止。用命令(#undef标识符) #define M 10.5

?
main ( ) 作用域范围

{?
# undef M

?
}

2. 可以嵌套定义, 即可引用已定义的宏名。
#define PI 3.14159 #define R 3.0 #define L 2?R ?PI #define S PI ?R ?R main { ? prinf("L=%f\n S=%f\n ", L, S); ? }

结果:L=18.84954 S=28.27131 注意: 双引号“

/*L=2?3.0 ? 3.14159*/ /*S=3.14159?3.0 ? 3.0*/

”中的 L 不被替换。

结论: ? 凡程序中常用到的字符序列,如:常数, 公

式,均可用宏定义。
? 经常会改变的数据可用宏定义。

对“输出格式”作宏定义,可以减少书写麻烦。 例5.25

#define P printf #define D "%d\n" #define F "%f\n" main(){ int a=5, c=8, e=11; float b=3.8, d=9.7, f=21.08; P(D F,a,b); P(D F,c,d); P(D F,e,f); }

二、有参形式 合适于字符串表达式序列中有变化的字符, 将这部分字符可定义为参数。 例1: ?r2?3.1415?r ?r 为字符序列, 但r是可变的。 形式: #define 宏名(参数表) 字符串 #define S(r ) 3.14159 ?r ?r ? S(3.0) /*相当于: 3.14159 ?3.0 ?3.0*/ S(4.0) /*相当于: 3.14159 ?4.0 ?4.0*/ ?

例2: #define f(a) a*b main() {int x,y,b,z; x=3;y=4; b=x+y; z=f(x+y); } 编译后: z=f(x+y) 变成: x+y*b 结果为: z=31
(这个结果有可能与初始设想不一致)

初始想法有可能是: 若有:f(x+y) 则应有:(x+y)?b

若有:f(x+y+z) 则应有:(x+y+z)?b

宏定义时必须注意以下几个方面:
1. 宏展开实质为严格的字符替换,把参数看一种 “ 字符串”,所以定义宏时要考虑实参(替换) 的各种可能, 防止出现二义性。 如程序例1中:若有#define s( r) 3.14159?r?r ?

则S(a+b)的结果为: 3.24159?a+b?a+b
为避免出现错误的结果,可将宏定义修改为: #define S( r) 3.1415926?( r)?( r) 2. 宏定义与函数是完全不同的概念。 3. 灵活运用宏定义, 可使程序书写简化.

例5.26 #define MAX(a,b) (a>b)?a:b main(){ int x,y,max; printf("input two numbers: scanf("%d%d",&x,&y); max=MAX(x,y); printf("max=%d\n",max); }

例5.29使用宏定义,计算半径为 2、4、6、8、10时圆的面积和圆 的周长 ");#include <stdio.h> #define PI 3.141592653 #define AREA(R) PI*(R)*(R) #define PERI(R) 2*PI*(R) void main() { int r; for(r=2; r<=10; r+=2) { printf("半径=%2d, 圆面积 =%f\t", r, AREA(r)); printf("圆周长=%f\n", PERI(r)); } }

§5.7.2 文件包含
C语言允许在一个文件预编译时,将另一个文件 原封不动地包含进来。 ?目的:

? 当函数不在一个文件中,既可用link连接编译 后的obj文件,也可用“ 文件包含”方式。
? 当一组符号常量的定义为公用时,可作为一个

独立的文件被其它文件包含。

格式: # include "文件名"

例:
# include "f2.c " ? f1.c

f2.c

: : :

main ( )
{ ?

}

预编译后: f1.c
: :

为f2.c中的内容

main ( )
{ ? } ? 再进行正式编译

1. 一个include只能写一个文件,包含多个文件需多 个include 2. 当f1.c包含f2.c,而f2.c又包含f3.c时,有两种包含形式: ? 只在f1.c中用#include. #include "f2.c" #include "f3.c" ? 分别在f1.c, f2.c中用#include 在f1.c中: #include "f2.c"

在f2.c中: #include "f3.c"

3. 一般用.h扩展名命名被包含文件。 h为head之意 以与一般.c文件区别。如系统stdio.h.系统.h文件 在子目录INCLUDE下。

4. #include后的文件名既可用" ", 也可用< >,两者 区别:
" "首先在当前目录中找,然后再去标准目录中找。 < > 只在标准目录(include目录)中找。 5. 被包含文件和包含文件最后形成一个文件。因此, 全局变量作用域为整个文件。不必用extern说明。

例5.30 将头文件format.h包含在example.c文件中 文件format.h的内容 #define P printf #define NL "\n" #define F "%7.2f" #define F1 F NL #define F2 F F NL 文件example.c的内容 #include <stdio.h> #include "format.h" /* 将format.h文件中的代码放于此位置 */ void main( ) { float x, y; x = 3.2378; y = 2.4623; PR(F1, x); PR(F2, x, y); }


赞助商链接
相关文章:
第4章 (C语言) 函数
5页 免费 c语言 函数 203页 1财富值 C语言函数详解 54页 免费 第9章 (C...在 C 语言中,函数是程序的基本组成单位,因此可以很方便地用函数作为程 序模块...
C语言基础(diyici)
132页 5财富值 第1章 C语言基础 38页 免费 c语言的基础 2页 免费 第2章...C 语言函数的隐含存储类别是__D__。 A.static B.auto C.register D.extern...
计算机二级C语言 第七章 函数
第七章 函 数 【本章要求】 本章要求】 1、C 语言程序中函数的基本概念 2...函数形式参数和实际参数的变化 5、函数返回值类型的一致性问题 6、函数嵌套调用...
C语言函数(5)
94页 5财富值 第5章 C语言函数 66页 2财富值 C语言5函数 4页 2财富值 ...(4)对基本类型的静态局部变量若在说明时未赋以初值,则系统自动赋予 0 值。而...
c语言函数库-第五章(数学函数)
c 语言函数第五章(数学函数) 第五章(数学函数) 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21....
第5章 (C语言) 数组
第11章C语言 结构体的应用... 7页 免费 第6章 C语言 函数及其应用... 9...它以一个字符串数组为基础 做简单的文本编辑。 [例 5 - 5 ] #include<...
c语言基础教程_第三章_运算符和表达式免费版
32页 免费 c语言基础教程_第二章_常量... 22页 免费 c语言基础教程_第九章联合... 12页 免费 c语言基础教程_第五章函数... 60页 免费如...
c语言
c语言第3章(新) 103页 1下载券 c语言第5章 47...C 语言基础知识 本教材采用工学结合的教学模式,以...还要把目标程序和系统提供的库函数等连接起来 产生可...
C语言5函数
C语言 第 5 章 函数 46页 1下载券 C语言函数大全5 25页 1下载券 c语言 ...一个 C 源程序必须包含一个 main 函数 C.C 程序的基本组成单位是函数 D.在...
国二c语言基础知识
第一章 C 语言基本知识 【考点一】 C 程序 用 C 语言编写的程序称为 C ...【考点】putchar 与 getchar 函数 可用于输出或输入单个字符,这两个函数是 ...
更多相关标签: