当前位置:首页 >> 数学 >>

C语言程序设计(函数)


C
语 言 程 序 设 计 教 程

第五章

函数

第 五 章 函 数

5.1 C程序结构 5.2 函数定义 5.3 函数调用和函数说明 5.4 函数的嵌套调用和递归调用 5.5 变量的作用域与存储方式 5.6 函数间数据传递 5.7 指针函数 5.8 函数指针

C
语 言 程 序 设 计 教 程

结构化程序设计
在高级语言程序设计中,如果需要解 决一个复杂问题时,通常是将其按照 功能划分为若干个子任务,每个子任 务设计成一个程序,这些子任务称为 模块(module)。 若子任务较复杂,还可以将子任务继 续分解,直到分解成容易解决的子任 务为止,这种自上而下逐步细化的模 块化程序设计方法称为结构化程序设 计(structured programming)。

模块之间功能 独立,彼此之 间有一定的联 系。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

5.1

C程序结构

C语言利用函数实现功能模块的定义,通过函数 之间的调用将各个模块连接成为一个程序。
一个C源程序文件可以由一个或多个函数组成。所有函数 都是独立的。主函数可以调用其它函数,其它函数可以相 互调用。 P166 图5.2

第 五 章 函 数

使用 函数 的好 处:

① 程序结构清晰,可读性好。 ② 减少重复编码的工作量。 ③ 可多人共同编制一个大程序,缩短程 序设计周期,提高程序设计和调试的 效率。

学 习 函 数 的 意 义 所 在。

C
语 言 程 序 设 计 教 程

函数概述
1 函数的概念
函数其实就是一段可以重复调用的、功能相对独 立完整的程序段。 在一个C程序中,有且仅有一个主函数main。C 程序的执行总是从main函数开始,调用其它函数 后最终回到main函数,在main函数中结束整个 程序的运行。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

求一个整数的立方

第 五 章 函 数

int cube (int x) /* 函数定义 */ { return (x * x * x); } main( ) 程序的执行总是 { int f, a; 从main函数开始 printf("\nEnter an integer number:"); scanf("%d", &a); 函数调用 f = cube (a); printf("%d * %d * %d = %d\n", a, a, a, f); 程序运行情况如下: } Enter an integer number:2? 2*2*2=8

C
语 言 程 序 设 计 教 程

函数的分类
? 从用户角度
? 标准函数(库函数):由系统提供。 如:getchar( )、sin(x)等。在程序中可以直接调 用它们。附录Ⅱ列出了C的部分库函数。

第 五 章 函 数

? 用户自定义函数:
根据自己的需要,按照C语言语法规定编写 的一段程序,实现特定的功能。 只要包含相应的
头文件即可直接 使用

C
语 言 程 序 设 计 教 程

? 从函数形式分
无参数函数
在调用无参函数时,主调函数并不将数据传送给 被调用函数,一般用来执行指定的一组操作。
无参函数:如:getchar( ) 在调用无参函数时,主调函数不 需要将数据传递给无参函数。

第 五 章 函 数

有参数函数 使用该函数时,必须给该函数提供所需要 的数据信息,按照提供的数据不同,在使用 该函数后获得不同的结果。
在主调(用)函数和被调(用函 数之间通过参数进行数据传递, 如:double sqrt(double x)

C
语 言 程 序 设 计 教 程

5.2 函数的定义
一般格式
现代风格:

函数返回值类型 缺省int型 无返回值void

合法标识符

函数类型 函数名(形参类型说明表) { 说明部分 可执行语句部分 函数的形参表由一个或多个参 }
数组成,多个形参表之间用逗 号分隔。也可以没有形参。

函数体

第 五 章 函 数

例 无参函数 有参函数(现代风格) void int max(int max printstar( (intx, x,int y) ) y) {{ printf(―********** int z; \n‖); } 或 z=x>y?x:y; void return(z); printstar(void ) {} printf(―**********\n‖); }

C
语 言 程 序 设 计 教 程

例 空函数 dummy( ) { } 函数体为空
调用此函数时,什么工作也不做,没有任何实际作用。 在主调函数中写上“dummy();” 表明 “这里要调用 一个函数”, 而现在这个函数没有起作用, 等以后扩充 函数功能时补充上。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

在程序设计中往往根据需要确定若干模块, 分别由一些函 数来实现。 而在第一阶段只设计最基本的模块, 其他一些 次要功能或锦上添花的功能则在以后需要时陆续补上。
在编写程序的开始阶段,可以在将来准备扩充功能的地方 写上一个空函数(函数名取将来采用的实际函数名)只是这 些函数未编好,先占一个位置,以后用一个编好的函数代 替它。 这样做,程序的结构清楚,可读性好,以后扩充新功能方 便,对程序结构影响不大。空函数在程序设计中常常是有 用的。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

对形参的声明 传统风格:
函数类型 函数名(形参表) 形参类型说明 { 说明部分 语句部分 }

第 五 章 函 数

例 有参函数(传统风格) int max(x,y) int x,y; { int z; z=x>y?x:y; return(z); }

两种用法等价。 推荐现代方式。

C
语 言 程 序 设 计 教 程

举例(略)
如下定义都是错误的

第 五 章 函 数

int max(x,y) { int x,y; …… }

int max(x,y) int x, y, z; { z = x > y ? x : y; return( z ); } 注意:形参与函数
体内变量的定义

C
语 言 程 序 设 计 教 程

注意 :
不能在函数体内定义其他函数,即函数不能嵌套 定义。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

5.3 函数的调用
函数调用的一般形式:

函数名(实参表列)
有参函数调用时参数表中列出的参数是实际 参数(简称实参)。 实参的形式为: 参数1,参数2,...,参数n, 各参数间用逗号隔开,实参与形参要保持顺 序一致、个数一致、类型应一致。实参与形 参按顺序一一对应,传递数据。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

?在C语言中,按照函数在主调函数出现的位 置来分,可以有三种函数调用方式。例如:

? 函数语句: 例 printf (―Hello,World!\n‖); ? 函数表达式: 例: m = max (a, b) * 2;

第 五 章 函 数

? 函数参数: 例: printf (―%d‖, max(a,b)); m = max (a, max(b,c));

C
语 言 程 序 设 计 教 程



求1~100的累加和。
int sum ( int x ) 思 { int i,t=0; 考: for (i=1; i<=x; i++) 两 t+=i; 个 return (t); 程 } 序 main( ) 有 { int s; 何 s=sum (100); 不 printf("%d\n", s); 同 }
程序输出结果: 5050

第 五 章 函 数

int sum100( ) { int i,t=0; for (i=1; i<=100; i++) t+=i; return (t);} main( ) { int s; s=sum100( ); printf("%d\n", s); } 程序输出结果: 5050

C
语 言 程 序 设 计 教 程

1.函数的形式参数与实际参数
编一程序,将主函数中的两个变量的值传递给swap函数中的 两个形参,交换两个形参的值。 void swap( int x, int y) 形式参数(形参) { int z; z=x; x=y; y=z; printf("\nx=%d,y=%d",x ,y); } 实际参数(实参) main( ) 单向值传递 { int a= 10,b=20; 程序输出结果: swap( a,b); x=20,y=10 a=10,b=20 printf("\na=%d,b=%d\n",a,b); }

第 五 章 函 数

C
语 言 程 序 设 计 教 程

有关实参和形参的说明:
① 当函数被调用时才给形参分配内存单元。调用结束,所占 内存被释放。

第 五 章 函 数

② 实参可以是常量、变量或表达式,但要求它们有确定的。 ③ 实参与形参要保持类型一致,字符型与整型可以兼容。 ④ 实参与形参的个数必须相等。在函数调用时,实参的值赋 给与之相对应的形参。“单向值传递”。 ⑤实参可以是一个表达式或值。

下一 页

C
语 言 程 序 设 计 教 程

注意:在TC中,实参的求值顺序是从右到左。参看 下例。 (后连续两页举例,可略) void fun(int a,int b) { printf("a=%d,b=%d\n",a,b); } main( ) { int m=5; fun (3+m, m++ ); }

第 五 章 函 数

(TC)程序输出结果: a=9,b=5

(VC)程序输出结果: a=8,b=5

C
语 言 程 序 设 计 教 程

例 参数求值顺序(可略) int f(int a, int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); } main() { int i=2,p; p=f(i,++i); printf("%d",p); } int f(int a, int b) { int c; if(a>b) c=1; else if(a==b) c=0; else c=-1; return(c); } main() { int i=2,p; p=f(i, i++); printf("%d",p); } 运行结果:1 (VC) 0

第 五 章 函 数

运行结果:0

C
语 言 程 序 设 计 教 程

⑵ 函数的返回值
函数的返回值是通过return语句带回到主调函数的。 return 语句格式:

return (表达式); 或 return 表达式 ; 或 return; 功能:用return语句从函数中退出,返回到调用它 的程序中。该语句有两重作用: (1) 从函数中退出,返回到调用它的程序中。 (2) 向调用程序返回一个值。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

函数的类型与函数的返回值(举例)。
输出两个数中的大数。

第 五 章 函 数

int max(int x,int y) { int z; z=x>y?x:y; return (z); /* 返回z的值 */ } void main( ) { int a,b,c; scanf("%d,%d",&a,&b); c=max(a,b); printf("max is %d\n",c); }

说明: ①函数的类型决 定了函数返回值 的类型。若省略 函数的类型,系 统默认其为int型。

C
语 言 程 序 设 计 教 程

?说明:

① 若函数没有返回值,return语句可以省略,就一直执 行到函数体的末尾遇}时,返回调用函数。


第 五 章 函 数

int func ( ) ②return语句中的表达式类型一般应和函数的类型 { 一致,如果不一致,系统自动将表达式类型转换 float f = 5; 为函数类型。f = f / 2; 例 return ( f ); 函数将返回2,而不是2.5 } ③若不需要返回值,应将函数定义为void类型。


注意:如果不将函数调用赋值给任何变量,它的返回值 将被丢弃!

下一

C
语 言 程 序 设 计 教 程

第 五 章 函 数

#include <stdio.h> int sum ( ); void main ( ) 因sum函数无 { return语句, int x; x的值将是无法 x = sum ( ); 预知的! printf (―x = %d\n", x); } int sum ( ) 应加上 { return tot; 语句 int i, tot = 0; for (i = 1; i <= 100; i ++) tot += i; 输出:10 } 返回

C
语 言 程 序 设 计 教 程

计算并输出圆的面积。

第 五 章 函 数

int s(int r) { return 3.14*r*r;} main( ) { int r; scanf("%d",&r); printf("%d\n",s(r)); }

自动转换 为int型 程序运行情况如下: 2? 12

思考: 若要得到单精度实型的圆面积,程序应如何修改

C
语 言 程 序 设 计 教 程

可略
若不要求带回函数值,则应将函数定义为void类型。

第 五 章 函 数

例 无返回值函数 void swap(int x,int y ) { int temp; temp=x; x=y; y=temp; }

void printstar() { printf("**********"); } main() { int a; a=printstar(); printf("%d",a); }
编译错误!

返回

C
语 言 程 序 设 计 教 程

对被调函数的声明和函数原型

变量要先定义后使用,函数 也如此。即被调函数的定义 要出现在主调函数的定义之 前。如swap函数:

void swap(int x, int y) { …} main( ) {… swap(a,b); }

第 五 章 函 数

如果非整型函数在主调函数之后定 义,则应在主调函数中或主调函数 之前对被调函数进行声明。

C
语 言 程 序 设 计 教 程

对被调函数进行声明的一般形式 函数类型 函数名(参数类型1 参数名1,…); 或 函数类型 函数名(参数类型1,参数类型2,…); 作用: 告诉编译系统函数类型、参数个数及类 型,以便检验。 函数说明位置: 程序的数据说明部分(函数内或外)

第 五 章 函 数

函数定义与函数说明不同

C
语 言 程 序 设 计 教 程

例: 函数说明举例
main( ) { float add(float,float);
函数原型声明

//

第 五 章 函 数

float a, b ,c; scanf ("%f,%f", &a, 说明 &b); c = add (a, b); void main ( ) printf ("sum is %f", c); { } float a, b, c; float add (float x, float y) scanf ("%f,%f", &a, &b); { c = add (a, b); float z; printf ("sum is %f", c); z = x + y; } return (z); }

float add (float x, float y) { float z; z = x + y; 被调函数出现在主调 return (z); 函数之前,不必函数 }

C
语 言 程 序 设 计 教 程

注意
1 当被调函数的返回值为int型时,在主调函数中不 必对被调函数进行原型说明,在调用函数中对于没 有进行原型说明的被调函数,C语言编译系统将默 认它为int的函数。 2 对被调用函数要求: 必须是已存在的函数 库函数: 必须加相应的头文件。 用户自定义函数: 函数类型说明

第 五 章 函 数

C
语 言 程 序 设 计 教 程



#include <stdio.h> #include <math.h> void showerror ( );

//声明showerror函数的原型

第 五 章 函 数

返回

void main ( ) { int a; scanf ("%d", &a); while (a < 0) { showerror( ); scanf ("%d", &a); } printf ("sqrt(a) = %.2f\n", sqrt(a)); }

调用showerror 函数

void showerror( ) //函数的定义,无参数无返回值 { printf("input error!\n"); //函数体,没有声明变量 }

C
语 言 程 序 设 计 教 程

函数的嵌套与递归调用
1、函数的嵌套调用
C规定:函数定义不可嵌套,但可以嵌套调用函数

main( )
?
第 五 章 函 数

a函数 ?

b函数

?
调用函数b

?
?

调用函数a ?

?

?

?

结束

——函数嵌套调用的示意图

C
语 言 程 序 设 计 教 程

【例】计算三个数中最大数与最小数的差。
#include <stdio.h> int dif (int x, int y, int z); int max (int x, int y, int z); int min (int x, int y, int z); void main ( ) { int a, b, c, d; scanf ("%d%d%d", &a, &b, &c); d = dif (a, b, c); printf ("Max - Min = %d\n", d); } int dif (int x, int y, int z) { return (max(x, y, z) – min(x, y, z)); } int max (int x, int y, int z) { int r; r = x > y ? x : y; return (r > z ? r : z); } int min (int x, int y, int z) { int r; r = x < y ? x : y; return (r < z ? r : z); }

第 五 章 函 数

main( )
调用函数dif

dif函数 调用函数max

max函数

输出 结束

min函数 调用函数min

C
语 言 程 序 设 计 教 程

2、函数递归调用 定义:函数直接或间接的调用自身叫函数的递归调用
int f (int x) { int y, z; …… z = f (y); ……. return (2*z); } f() int f1 (int x) { int y,z; …… z = f2 (y); ……. return (2*z); } f1( ) 直接递归 调f 调f2 调f1 int f2 (int t) { int a,c; …… c = f1 (a); ……. return (3+c); } f2( ) 间接递归

第 五 章 函 数

C
语 言 程 序 设 计 教 程

说明
? 在原则上C编译系统对递归函数的自调用次数 没有限制。 ? 每调用函数一次,在内存堆栈区分配空间, 用于存放函数变量、返回值等信息,所以递归次 数过多,可能引起堆栈溢出。即实际嵌套的层数 要受系统资源条件的限制。
根据公式必须确定递归函数的出口,即结束递归调用的条件。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

【例2】求n的阶乘n! 方法一:利用循环 因为n! = n * (n-1) * (n-2) * … * 2 * 1,我们完全可以用循环语 句来编写这个非递归函数factn:

第 五 章 函 数

long factn (int n) { long L = 1; int i; for (i = 1; i <= n; i++) L *= i; return ( L ); }

C

方法二:利用递归 当n = 1,0时 (n - 1)! 当n > 1时

语 1 言 程 n! = 序 n* 设 计 int fac(int n) 教 { 程 int f;

递归结束条件

if(n<0) printf("n<0,data error!"); else if(n==0||n==1) f=1; else f=fac(n-1)*n; return(f); } 第 五 main() 章 { 函 int n, y; 数 printf("Input a integer number:"); scanf("%d",&n); y=fac(n); printf("%d! =%15d",n,y); }

运行情况如下: Input a integer number:4 4!=24

C
语 言 程 序 设 计 教 程

递归调用过程
main( ) { … fact(4) { … 回 推 fact(3) { … fact(2) { … fact(1) { …

y=fact(4);
第 五 章 函 数

f=4*fact(3); f=3*fact(2); f=2*fact(1); f=1; … return 24 } … return 6 } … return 2 } … return 1 }



}

回代

C
语 言 程 序 设 计 教 程

5.5 变量的作用域与存储方式
问题:一个变量在程序的哪个函数中都能使用吗?
? 变量的作用域 即变量的作用范围(或有效范围)。表现为变量有的 可以在整个程序或其它程序中进行引用,有的则只能在 局部范围内引用。 按其作用域范围可分为两种:即局部变量和全局变量

第 五 章 函 数

? 变量的生存期 变量从被生成到被撤消的这段时间。实际上就是变量 占用内存的时间。 按其生存期可分为两种:即动态变量和静态变量

变量只能在其生存期内被引用,变量的作用域 直接影响变量的生存期。作用域和生存期是从空间 和时间的角度来体现变量的特性。

C
语 言 程 序 设 计 教 程

C语言只允许在三种地方定义变量

函数内部的声明部分 复合语句中的声明部分 所有函数的外部

内部变量 外部变量

第 五 章 函 数

? 定义 在函数内或复合语句中定义的变量,为内部变量 。 ? 作用域 仅限于函数内和复合语句中,离开函数和复合语句后 不可再引用。

C
语 言 程 序 设 计 教 程

1、局部变量作用域

int f1 ( int x, int y ) { int z; z = x > y ? x : y; return (z); } void f2 ( ) { printf ("%d\n", z ); }

局部变量

变量x、y、 z的作用域

第 五 章 函 数

引用错误!

C
语 言 程 序 设 计 教 程

? 说明 (1) 主函数main( )中定义的变量也是局部变量,它只能在 主函数中使用,其它函数不能使用。同时,主函数中也不能 使用其它函数中定义的局部变量。

第 五 章 函 数

int f3 (int x); void main ( ) { int a = 2, b; 错误! b=a+y; printf ("%d\n", b); } int f3 ( int x ) { int y; 错误! y = a + 5; return ( y ); }

局部变量

变量a、b 的作用域
局部变量

变量x、y 的作用域

C
语 言 程 序 设 计 教 程

? 说明 (2) 形参变量属于被调用函数的局部变量;实参变量则可属于 全局变量或调用函数的局部变量。 (3) 允许在不同的函数中使用相同的变量名,它们代表不同的 对象,分配不同的单元,互不干扰,也不会发生混淆。 void subf ( ) { #include <stdio.h> int a, b; 变量名相同 void subf ( ); a = 6, b = 7; void main ( ) printf("subf: a = %d, { b = %d\n",a,b); int a, b; } a=3, b=4; printf ("main: a = %d, b = %d\n", a, b); 运行结果: subf ( ); main: a = 3, b = 4 printf ("main: a = %d, b = %d\n", a, b); subf: a = 6, b = 7 } main: a = 3, b = 4

第 五 章 函 数

C
语 言 程 序 设 计 教 程

? 说明 (4) 在复合语句中定义的变量也是局部变量,其作用域 只在复合语句范围内。其生存期是从复合语句被执行的时刻到 复合语句执行完毕的时刻。

第 五 章 函 数

#include <stdio.h> main中的局部变量 void main ( ) { int a = 2, b = 4; 复合语句中的局部变量 { int k, b; k = a + 5; 复合语句中 mian中 变量k、b 变量a、b b = a * 5; 的作用域 的作用域 printf ("k = %d\n", k); 输出k = 7 printf ("b = %d\n", b); } 输出b = 10 printf ("b = %d\n", b); 输出b = 4 a = k + 2; 错误! }

C
语 言 程 序 设 计 教 程

2、全局变量作用域
? 定义 在函数外部作定义说明的变量,也称为外部变量 。它 不属于哪一个函数,而属于一个源程序文件。 ? 作用域 从定义变量的位置开始到本源文件结束

第 五 章 函 数

C
语 言 程 序 设 计 教 程

例如:
int a=1,b=5; float f1(x); int x; {int c,d; ┆ } char c1,c2; char f2(x,y) int x,y; {int i,j; ┆ } main() {int m,n; ┆ }

第 五 章 函 数

全局变量a、b作 用域

全局变量c1、c2作 用域

C
语 言 程 序 设 计 教 程

float n = 0; #include <stdio.h> #include <math.h> int sign ( ); //计算数n的平方根 float sqr ( ) { 错误! if ( n > 0 ) return (sqrt(n)); else return (-1); } void main ( ) { 局部变量 int s; float t; 局部 scanf ("%f", &n); 变量 s = sign ( ); //取符号 s、 t t = sqr ( ); //取平方根 printf ("s = %d t = %f ", s, t); 的作 用域 } //取数n的符号 int sign ( ) { int r = 0; if (n > 0) r = 1; if (n < 0) r = -1; return ( r ); } 定义全局变量, 并赋初值

第 五 章 函 数

局部变量

全 局 变 量 n 的 作 用 域

局部变量r 的作用域

C
语 言 程 序 设 计 教 程

? 说明
(1) 应尽量少使用全局变量。 ? 全局变量在程序全部执行过程中始终占用存储单元不 利于内存空间的动态分配。 ? 降低了函数的独立性、通用性、可靠性及可移植性 ? 降低程序清晰性,容易出错

(2) 全局变量定义必须在所有的函数之外,且只能定义一 次,并可赋初始值。
第 五 章 函 数

(3) 同一个源文件中全局变量和局部变量可以同名。若全局 变量与局部变量同名,则全局变量被屏蔽不其作用。

C
语 言 程 序 设 计 教 程

注意:局部变量与全局变量同名极易导致程序员犯逻辑错误。

#include <stdio.h> int a = 10; //全局变量 void main ( ) { int a = 100; //局部变量(与全局变量同名) printf ("a = %d\n", a); }
运行结果: a = 100

第 五 章 函 数

C
语 言 程 序 设 计 教 程

(4) 对全局变量进行说明,可扩展全局变量的作用域。全局 变量说明的一般形式为:

extern 类型说明符 全局变量名1, …,全局变量名n;

第 五 章 函 数

C
语 言 程 序 设 计 教 程

第 五 章 函 数

void gx ( ), gy ( ); void main ( ) 全局变量说明 { extern int x, y; printf ("1: x = %d\ty = %d\n", x, y); 说明后 y = 246; 不可缺省! 不可赋初值! 的作用域 gx ( ); gy ( ); } 全局变量说明 extern int x, y; void gx ( ) { x = 135; printf ("2: x = %d\ty = %d\n", x, y); 说明后 } 全局变量定义 的作用域 int x = 0, y = 0; void gy ( ) 未说明前 运行结果: { printf ("3: x=%d\ty=%d\n", x, y); 的作用域 1: x = 0 y=0 } 2: x = 135 y = 246 3: x = 135 y = 246

C
语 言 程 序 设 计 教 程

(5) 全局变量定义与全局变量的说明有所区别,全 局变量与局部变量一样,也遵循先定义后使用的原 则,只能定义一次。 而且最好定义在使用它的函数之前,如果在变量定 义之前的函数要使用它,只能对这个全局变量进行 说明,而不能再次定义。 在函数体外进行的函数说明也使该函数具有全局的 性质。书P183例5.13

第 五 章 函 数

C
语 言 程 序 设 计 教 程

5.5.2 变量的存储类别及变量的生存期
? 概述

变量的作用域影响着变量的生存期。 原因: 程序中的变量都占用一定的内存,但并不是所有 的变量在程序开始执行的时刻就占用内存。

第 五 章 函 数

为了节省内存的使用,在程序运行时,只有在必 要时才为变量分配内存,当变量占有内存时,变 量被生成了,当变量不再有用时,程序将撤销变 量所占的内存,变量就消失了。 变量的生存期就是指变量从被生成到被撤销的这 段时间,实际上就是变量所占用内存的时间。

C
语 言 程 序 设 计 教 程

思考:1. 何时为变量分配内存单元? 2. 将变量分配在内存的什么区域? 3. 变量占据内存的时间(生存期)?

在C程序运行时,占用的存储空间通常分为三部分:
程序执行时 的机器指令

程序代码区
静态存储区
静态存储变量

第 五 章 函 数

数据

动态存储区
存储分配

动态存储 变量

C
语 言 程 序 设 计 教 程

变 量 的 属 性

数据类型: 决定为变量分配内存单元的长度, 数据的存放形式, 数的范围。 存储类别: 决定了变量的生存期, 给它分配在哪个存储区。

第 五 章 函 数

存储器类型:寄存器、静态存储区、动态存储区 变量也有三种存储类型: auto---------自动型 static -------静态型 register-----寄存器型

C
语 言 程 序 设 计 教 程

变量定义语句的一般形式

存储类别 数据类型 变量名1, … , 变量名n ;
auto(自动的) register(寄存器的)static (静态的) extern(外部的)

第 五 章 函 数

1.自动变量(auto变量) 动态分配存储空间,局部变量,函数的形参, 可 复合语句中的局部变量都为自动变量。 省 main() main() 等价 {int x,y; {auto int x,y; … … 自动变量 } }

C
语 言 程 序 设 计 教 程

实际上,不加特别声明(static)的局部变量都是自 动变量。
注意:在函数外部定义的没有带存储类型说明符的全 局变量是外部变量,属于静态存储类型。 如:int k; //k为外部变量,属静态存储类型 void func ( ) { …… } auto int k; void func ( ) { …… }

第 五 章 函 数

错误!

C
语 言 程 序 设 计 教 程

2 寄存器存储类别
寄存器变量是将局部变量的值放在CPU通用寄存器中, 以此来提高程序执行效率。

用关键字register说明。例如:
register int a,b; 注意:

第 五 章 函 数

(1)只有局部自动变量和形式参数可说明为寄存器变量。 (2)一个计算机系统中的寄存器的数目是有限。

(4)局部静态变量不能定义为寄存器变量,不能写成:
register static int a,b,c;

C
语 言 程 序 设 计 教 程

3.静态变量(static类别)

静态变量

局部静态变量(或称内部静态变量) 全局静态变量(或称外部静态变量)

第 五 章 函 数

除形参外,局部变量和全局变量都可以定义为静 态变量。 static int a; 全局静态变量 main( ) { float x,y; 不 …} 自动变量 能 f( ) 省 { static int b=1; 局部静态变量 …… }

C
语 言 程 序 设 计 教 程

4.外部变量(extern类别)
在函数外定义的变量若没有用 static说明,则是外部变量。 外部变量只能隐式定义为extern类别,不能显式定义。

第 五 章 函 数

外部变量

int c; static int a; main( ) { float x,y; …} char s; f( ) { static int b=1; …… }

全局静态变量 自动变量

局部静态变量

C
语 言 程 序 设 计 教 程

全局变量的存储方式
全局变量可以用extern和static存储类别。当未对全局变量 指定存储类别时,隐含为extern类别。

1. 外部存储类别
当在一个文件中要引用另一个文件的全局变量或在全局 变量之前要引用它时,可用extern说明。由于全局变量在整 个程序的运行过程中“永久性”地占用固定的存储单元,所 以它存放在静态数据区中,属于静态存储变量。

第 五 章 函 数

2. 静态存储类别

由static说明的全局变量称为静态全局变量,它只能由本 文件引用,即使在其它文件中用extern说明也不能使用,相 当于限制了全局变量作用域的扩展。

C
语 言 程 序 设 计 教 程

局部变量 存储类别

全局变量

auto

register

static

static
静态存储区

extern

寄存器 存储区 动态区 生存期 函数调用开始至结束

程序整个运行期间

作用域 定义变量的函数或复合语句内 本文件 其它文件 赋初值 每次函数调用时 编译时赋初值,只赋一次
第 五 章 函 数

未赋初值

不确定

自动赋初值0或空字符

?局部变量默认为auto型,全局变量默认为extern型 ?register型变量个数受限,且不能为long, double, float型 ?局部static变量具有全局寿命和局部可见性 ?extern不是变量定义,可扩展外部变量作用域

C
语 言 程 序 设 计 教 程

例 在一个文件内声明外部变量。

第 五 章 函 数

int p=1,q=5; float f1(int a) { extern char c1,c2; …… } char c1,c2; char f2(int x,int y) { …… } main( ) { …… }

定义外部变量 外部变量声明

定义外部变量 思考:在f1函数中声明c1、 c2的作用是什么?如何修改 程序使所有函数都可以使用 外部变量而又不需要声明?

C
语 言 程 序 设 计 教 程

例不在一个文件内声明外部变量。

原文件prg1.c int a, b; //外部变量定义 int max ( ); //外部函数声明 main ( ) { int c; a = 4, b = 5; c = max ( ); printf ("max = %d\n", c); }

原文件prg2.c extern int a, b; //外部变量声明 int max ( ) { return (a > b ? a : b); }

第 五 章 函 数

运行结果: 编译、链接、运行 max = 5

C
语 言 程 序 设 计 教 程

? 静态局部变量与自动变量之比较 ? 静态局部变量与自动变量均属于局部变量 ? 静态局部变量生存期长,为整个源程序。自动变量生 存期短。 ? 静态局部变量的生存期虽然为整个源程序,但是其作 用域仍与自动变量相同
void func ( ); void main ( ) { int a; a = s + 5; …… }
void func ( ) { static int s; …… }

错误!

第 五 章 函 数

定义静态 局部变量s

s的作用域

C
语 言 程 序 设 计 教 程

?例 局部静态变量值具有可继承性
main() { void increment(void); increment(); increment(); increment(); } void increment(void) { int x=0; x++; printf(―%d\n‖,x); }
运行结果:1 1 1

第 五 章 函 数

main() { void increment(void); increment(); increment(); increment(); } void increment(void) { static int x=0; x++; printf(―%d\n‖,x); }
运行结果:1 2 3

C
语 言 程 序 设 计 教 程

函数参数的传递方式
根据实参传递给形参值的不同,通常有值传递方式和 地址传递方式两种。 1、值传递方式 ? 方式: 函数调用时,为形参分配单元,并将实参的值复制到 形参中;调用结束,形参单元被释放,实参单元仍保 留并维持原值。 ? 特点: ① 形参与实参占用不同的内存单元 ② 单向传递

第 五 章 函 数

C
语 言 程 序 设 计 教 程

例: 交换两个数(值传递方式)
#include <stdio.h> void swap (int a, int b); void main ( ) { int x = 7, y = 11; printf ("before swapped: "); printf ("x=%d, y=%d\n", x, y); swap (x, y); printf ("after swapped: "); printf ("x=%d, y=%d\n", x, y); } void swap (int a, int b) { int temp; temp = a; 运行结果: a = b; before swapped: x = 7, y = 11 b = temp; after swapped: x = 7, y = 11 } x

① 调用前 7 y

11

x
a

② 调用 y 7

11
11

7

b

③ swap x a

第 五 章 函 数

7 11 7
temp

y b

11 7 11

Why?

7

x

④ 调用结束 y 11 7

C
语 言 程 序 设 计 教 程

2、地址传递方式 ? 方式: 函数调用时,将数据的存储地址作为参数传递给形参 ? 特点: ① 形参与实参占用同样的存储单元 ② 双向传递 ③ 实参和形参必须是地址常量或变量

用指针变量来作为函数的形参 P191
第 五 章 函 数



5.20,5.21

用数组名作为函数参数
用全局变量来作为函数的形参p198 例5.23

C
语 言 程 序 设 计 教 程

例 交换两个数 swap(int *p1, int*p2) { int p; p=*p1; *p1=*p2; *p2=p; } main() { int a,b; scanf("%d,%d",&a,&b); printf(“a=%d,b=%d\n”,a,b); printf(“swapped:\n”); swap(&a,&b); printf(”a=%d,b=%d\n",a,b); }
调前: a 5 p1 &a 调swap: p2 &b b 9 a 5 b 9 a 9 b 5 b 5

p1
&a 交换: p2 &b a 9

第 五 章 函 数

返回:

C
语 言 程 序 设 计 教 程

5.6.2 地址传递
在主调函数与被调函数分别定于数组,且类型应一致 形参数组大小(多维数组第一维)可不指定。

形参数组名是地址变量

?数组名作为实参传递
第 五 章 函 数

C
语 言 程 序 设 计 教 程

用冒泡法将10个整数排序。 void printarr(int b[10])

第 五 章 函 数

void sort(int b[ ],int n); void printarr(int b[ ]); main( ) { int a[10] = {11,22,63,97,58,80,45, 32,73,36}; printf("Before sort:\n"); printarr(a); sort(a,10); printf("After sort:\n"); printarr(a); }

{ int i; for (i=0; i<10; i++) printf("%5d",b[i]); printf("\n");}

void sort(int b[ ], int n) { int i,j,t; for (i=1; i<n; i++) for (j=0; j<n-i; j++ ) if (b[j]>b[j+1]) { t=b[j];b[j]=b[j+1];b[j+1]=t; }}

C
语 言 程 序 设 计 教 程

例 求学生的平均成绩

形参用数组定义, ?int stu[ ]

第 五 章 函 数

#include <stdio.h> float average(int stu[10], int n float average(int stu[10], int n); { int i; void main() float av,total=0; { int score[10], i; for( i=0; i<n; i++ ) float av; total += stu[i]; printf("Input 10 scores:\n"); av = total/n; for( i=0; i<10; i++ ) return av; scanf("%d", &score[i]); } av=average(score,10); stu score printf("Average is:%.2f", av); 0 12 } 1 23
实参用数组名 2 . . 9 56 …. …. 88

C
语 言 程 序 设 计 教 程

例 数组排序----简单选择排序 void sort(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; if(k!=i) { t=array[i]; array[i]=array[k]; array[k]=t; } } }

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

a array i=0

第 五 章 函 数

49 9 68 57 32 9 49 99 27 13 76 88

0 1 2 3 4 5 6 7 8 9

k j j j j j j j j j

k k

main() 语 例 数组排序----简单选择排序 { int a[10],i; 言 for(i=0;i<10;i++) 程 void sort(int array[],int n) 序 scanf("%d",&a[i]); { int i,j,k,t; 设 sort(a,10); 计 for(i=0;i<n-1;i++) 教 for(i=0;i<10;i++) { k=i; 程 printf("%d ",a[i]); for(j=i+1;j<n;j++) if(array[j]<array[k]) k=j; printf("\n"); } if(k!=i) a { t=array[i]; 49 0 9 array array[i]=array[k]; 68 1 13 k 第 57 五 2 array[k]=t; j k i=1 章 32 3 } j k 函 9 4 49 j 数 } 99 5 j }
C

27 13 68 76 88

6 7 8 9

j j j j

k k

C
语 言 程 序 设 计 教 程

例: 将任意两个字符串连接成一个字符串 (数组名作为函 数参数实现地址传递方式)
#include <stdio.h> void mergestr (char s1[ ], char s2[ ], char s3[ ]); void main ( ) { char str1[ ] = {"Hello "}; char str2[ ] = {"china!"}; char str3[40]; mergestr (str1, str2, str3); printf ("%s\n", str3); } void mergestr (char s1[ ], char s2[ ], char s3[ ]) { int i, j; for (i = 0; s1[i] != '\0'; i++) //将s1复制到s3中 s3[i] = s1[i]; for (j = 0; s2[j] != '\0'; j++) //将s2复制到s3的后边 s3[i+j] = s2[j]; s3[i+j] = '\0'; //置字符串结束标志 }

第 五 章 函 数

运行结果: Hello china!

C
语 言 程 序 设 计 教 程

调用前
s1 str1

调用

连接
s2 str2

补\0

调用结束

'H' 'e'

'l'

'l'

'o'

''

'\0'

'c' 'h'

'i'

'n' 'a' '!' '\0'

s3[i+j] = '\0';
第 五 章 函 数

'H' 'e'
s3 str3

'l'

'l'

'o'

''

'c'

'h' 'i'

'n'

'a'

'!'

'\0'

for (i = 0; s1[i] != '\0'; i++) s3[i] = s1[i];

for (j = 0; s2[j] != '\0'; j++) s3[i+j] = s2[j];

C
语 言 程 序 设 计 教 程

例 求二维数组中最大元素值
j j 5 6 34 7 8 12

int max_value(int array[3][4]) j { int i,j,k,max; 1 3 1 3 5 7 i 1 3 max=array[0][0]; 5 7 i i 多维形参数组第一维维数 可省略 2 4 2 4 6 ,第二维必须相同 8 2 4 for(i=0;i<3;i++) 6 8 for(j=0;j<4;j++) ? int array[][4] 15 17 15 17 34 12 15 17 34 12 if(array[i][j]>max) max=5 max=3 max=1 max=array[i][j]; j return(max); j 第 五i 1 3 } 5 7 1 3 5 7 1 3 章 main() 函 2 4 6 8 i 2 4 6 8 2 4 数 { int a[3][4]={{1,3,5,7}, i 15 17 34 12 15 17 34 12 {2,4,6,8},{15,17,34,12}}; 15 17 max=7 printf("max max=7 value is %d\n",max_value(a)); max=34 }

j
5 7

6 34

8 12

C
语 言 程 序 设 计 教 程

第 五 章 函 数

例 求二维数组中各行元素之和 get_sum_row(int x[][3], int result[] ,int row, int col) { int i,j; for(i=0;i<row;i++) result { result[i]=0; a sum_row for(j=0;j<col;j++) x 18 3 6 9 result[i]+=x[i][j]; 12 1 4 7 } } main() { int a[2][3]={3,6,9,1,4,7}; int sum_row[2],row=2,col=3,i; get_sum_row(a,sum_row,row,col); for(i=0;i<row;i++) printf("The sum of row[%d]=%d\n",i+1,sum_row[i]); }

C
语 言 程 序 设 计 教 程

5.6.3 利用全局变量传递数据
如果想让多个函数都能对某个存储单元进行 存取,还可以采用全局变量的方式,因为对 所在的源文件中所有函数而言,全局变量都 是可以使用的 。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

例5.23 利用全局变量实现两个整数的交换。
#include "stdio.h" int a,b; /*定义全局变量a,b*/ void change() { int temp; temp=a; a=b; b=temp; printf("\na=%d,b=%d",a,b); } main() { printf("input a,b:"); scanf("%d,%d",&a,&b); printf("a=%d,b=%d\n",a,b); change(); printf("a=%d,b=%d\n",a,b); }

第 五 章 函 数

程序运行结果: input a,b:2,3 a=2,b=3; a=3,b=2; a=3,b=2;

C
语 言 程 序 设 计 教 程

采用全局变量可能会造成各模块相互之间影 响,使得程序中函数的通用性和可移植性降 低。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

5.7指针函数
1.指针函数的定义 指针函数的一般定义形式为: 数据类型 *函数名(参数表列) { 函数体 } float *a(int x,int y);

第 五 章 函 数

C
语 言 程 序 设 计 教 程

例5.24 编写一个指针函数search(s,t),函数功能为在字符串s

中查找一个任意子串t,如果找到,则返回字符子串在s中第一 次出现的起始位置,否则返回NULL。

第 五 章 函 数

char *search(char *s,char *t) { char *ps=s,*pt,*pc; while(*ps!= '\0') /*在字符串s查找一个任意子串t*/ { for(pt=t,pc=ps;*pt!= '\0' &&*pt= =*pc;pt++,pc++); if(*pt= ='\0') return(ps); ps++; } return(0); /*返回NULL,NULL的ASCII值为0*/ }

C
语 言 程 序 设 计 教 程

5.8 函数指针
5.8.1 函数指针 1.函数指针定义 函数指针定义形式为: 数据类型 (*指针变量名)( ); 例如:int (*p)(); 指针变量p为指向一个返回值为整型的函数 指针。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

2.函数指针初始化与赋值
在利用函数指针调用函数时,首先必须让函数指针指向 被调函数,也就是给函数指针赋值过程。 赋值过程可以在定义变量(即初始化)或者 在程序中 通过赋值语句完成。

第 五 章 函 数

函数指针初始化的一般形式为: 数据类型 (*指针变量名)( ) =函数名; 函数指针赋值的一般形式为: 指针变量名=函数名;

C
语 言 程 序 设 计 教 程

3.利用函数指针调用函数 当函数指针指向一个函数后,就可利用该 指针来调用它所指向的函数。 调用方式: (*指针变量名) (实参表列) 或者 指针变量名(实参表列) 例如:change(a,b);与 (*p)(a,b),p(a,b) 都 表示调用函数change,作用相同。

第 五 章 函 数

C
语 言 程 序 设 计 教 程

5.8.2 用函数指针作函数参数
前面我们介绍过,函数的参数可以是变量、指针、数组等 同样函数指针也可以作为参数实现函数地址的传递, 最常用就是在被调函数中利用传递过来的函数指针来对函数 进行调用。

第 五 章 函 数

例如下面的一个函数: int x1(int a) { … } void f( int (*x)(int) ) /函数的形参为函数指针*/ { int m,i; m=(*x)(i); … }


相关文章:
C语言程序设计之函数
C语言程序设计函数_工学_高等教育_教育专区。1、使用函数判断数的符号 2、使用函数求奇数和 3、使用函数统计一个整数中数字的个数 4、数字金字塔 5、使用函数...
C语言编程函数
C语言编程函数_理学_高等教育_教育专区。编程求由键盘输入的三个数中最大值寻找...,求 要求:(1) 能检查输入数据的合法性,要求n>=m; (2)用递归完成程序设计...
C语言程序设计
C语言程序设计课后答案 62页 3下载券C​语​言​程​序​设​计 暂无评价|0人阅读|0次下载|举报文档程序设计 第1题 题目:编写函数 fun,函数的功能...
《C语言程序设计》C函数定义和使用
C 语言程序设计》实验报告 实验名称:函数定义和使用 系别: 计算机系 专业:计算机科学与技术 班级:五班 姓名: 学号: 实验日期: 教师审批签字: 实验 5 ⒈ ...
C语言编程练习题绝对经典!
利用以下公式,编程计算π 的值,直到最后一项的绝对值( 用函数 fabs() )小于 ...C语言程序设计练习题(含... 53页 免费 C语言编程练习题绝对经典... 25页...
C语言程序设计—函数—实验报告
C语言程序设计函数—实验报告_实习总结_总结/汇报_实用文档。C语言程序设计函数—实验报告 实验报告 专业 软件工程 班级 X 班 学号_ XXXXXXXXXXX_ 报告退发...
C语言程序设计练习题(答案)
C语言程序设计练习题(答案)_IT认证_资格考试/认证_教育专区。1.1 上机实训...连接的目的是将 程序和系统提供的资源(如函数库、头文件等)建立连接,真正生成...
《C语言程序设计》实验报告函数
C 语言程序设计 》课程实验报告 实验名称 学号_ 函数 姓年月日 实验地点: 名 _ __ 班别月日 实验日期: 指导老师: 成评绩: 语: 实验报告日期: 年 ≡...
C语言程序设计练习题(含程序及参考答案)
C 语言练习题 (所提供的源程序均采用自定义函数方式解决,如不 采用函数方式,也可直接在 main 函数中借鉴该思想 编程,因时间有限,所有程序未能一一验证,仅供参 ...
更多相关标签:
c语言退出程序函数 | c语言函数库程序 | c语言程序函数调用 | c语言程序设计 | c语言程序设计 谭浩强 | c语言程序设计入门 | c语言程序设计培训 | c语言与程序设计 |