560 likes | 744 Views
《 程序设计基础( C ) 》. 第六章 函数. 本章内容. 模块化程序设计 函数说明、函数实现与函数调用 参数传递 形参、实参与返回值 传值、传址与传数组 递归函数 变量作用域与存贮类别 库函数. 一、模块化程序设计. 模块化设计思想 一个复杂问题可分解为若干个子问题 每个子问题也可以再分解为更简单的小问题 程序设计转化为层次式结构化的模块设计 自底向上 与 自顶向下 设计方法 模块特点 模块间存在调用接口(输入输出) 各模块可分别编写、测试,再进行总组装 模块可以有一定的可重用性 模块实现细节的独立性(封装). 二、函数设计.
E N D
《程序设计基础(C)》 第六章 函数
本章内容 • 模块化程序设计 • 函数说明、函数实现与函数调用 • 参数传递 • 形参、实参与返回值 • 传值、传址与传数组 • 递归函数 • 变量作用域与存贮类别 • 库函数
一、模块化程序设计 • 模块化设计思想 • 一个复杂问题可分解为若干个子问题 • 每个子问题也可以再分解为更简单的小问题 • 程序设计转化为层次式结构化的模块设计 • 自底向上 与 自顶向下 设计方法 • 模块特点 • 模块间存在调用接口(输入输出) • 各模块可分别编写、测试,再进行总组装 • 模块可以有一定的可重用性 • 模块实现细节的独立性(封装)
二、函数设计 • 函数举例:素数判断 • 函数举例:数组相关 • 函数概念 • 函数声明/函数说明 • 函数调用 • 函数实现/函数定义
函数设计例1:素数 • 输入n,判断n是否为素数 • 输入n,输出n以内的所有素数 • 输入m和n,输出m与n之间所有素数,按每行7个素数的形式输出,如果没有素数,则输出None • 验证哥德巴赫猜想:将4到100的所有偶数分解为两个素数之和
判断素数的函数 • 专判素数的模块 • 函数名称:isprime • 函数声明/函数说明: • int isprime( int n ); • 自变量类型:整型 术语参数 • 结果类型: 整型 术语返回 • 功能说明:若n为素数,则返回1,若n不是素数,则返回0
#include <stdio.h> main() { int n, i, isyes; scanf( "%d", &n ); if ( isyes == 1 ) printf( "Yes" ); else printf( "No" ); } int isprime(int n) { int i, isyes; return isyes; } 函数剥离过程 isyes = 1; for ( i=2; i<=n-1; i++ ) if ( n%i == 0 ) isyes = 0; isyes = isprime( n );
#include <stdio.h> int isprime(int n) { int i,isyes; isyes=1; for (i=2;i<=n-1;i++) if (n%i==0) isyes = 0; return isyes; } main() { int n; scanf( "%d", &n ); if ( isprime(n) ) printf( "Yes" ); else printf( "No" ); } 函数概念 函数实现 形式参数 函数调用 实际参数 局部变量 函数返回
使用return优化函数 int isprime(int n) { int i; for (i=2;i<=n-1;i++) if (n%i==0) return 0; return 1; }
将4到100的所有偶数分解为两个素数之和 main() { int n,i; for ( n=4; n<=100; n+=2 ) for ( i=2; i<n; i++ ) if ( isprime(i) && isprime(n-i) ) { printf("%2d=%2d+%2d\t",n,i,n-i); break; } }
先声明再调用 #include <stdio.h> int isprime( int n ); main() { …… } int isprime( int n ) { …… } 先实现再调用 #include <stdio.h> int isprime( int n ) { …… } main() { …… } 代码的组织顺序
函数设计规范 • 函数调用前必须先出现函数实现或函数声明 • 函数实现与函数声明必须一致 • 函数名称、参数类型及顺序、返回类型 • 函数声明中形式参数名可以省略 • 如:int isprime( int ); • 函数之间的局部变量允许重名 • 无返回值或无参数,类型记为void • 函数内部对形参的修改不影响调用函数
传递数值示例 • int func( int n ) • { • n = n + 1; /* 修改了n的值 */ • return n; • } • main() • { • int a=1, b; • b = func( a ); /* a的值不受影响 */ • printf( “a=%d,b=%d\n”, a, b ); • }
函数设计例2:数组相关 • 数组相关操作 • 计算累加和 • 计算最大值、最小值、平均值 • 排序 • 查找 • 输入、输出 • 传递数组:对数组参数的修改影响调用函数
数组累加和 • 数组累加:long sum( int a[ ], int n ); • 主函数示例 • main() • { • int x[6],i; long s; • for ( i=0; i<6; i++ ) • scanf( “%d”, &x[i] ); • s = sum( x, 6 ); • printf( “%ld\n”, s ); • }
数组累加和 long sum(int a[ ],int n) { long rst = 0; int i; for ( i = 0; i < n; i++ ) rst += a[i]; return rst; }
数组相关函数 • float average( int a[ ], int n ); • int max( int a[ ], int n ); • int min( int a[ ], int n ); • void sort( int a[ ], int n ); • int find( int a[ ], int n, int x ); • void sca( int a[ ], int n ); • void prt( int a[ ], int n );
输入数组 void sca(int a[ ], int n) { int i; for ( i=0;i<n;i++ ) scanf(“%d”,&a[i]); } 返回类型为void,省略return语句
输出数组 void prt(int a[ ], int n) { int i; for ( i=0;i<n;i++ ) printf(“%d ”, a[i]); printf( “\n” ); }
数组排序 void sort( int a[ ], int n ) { int i, j, t; for ( i = 0; i < n-1; i++ ) for ( j = 0; j < n-1-i; j++ ) if ( a[j]>a[j+1] ) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } } 对a数组的修改在函数结束后仍起作用
排序程序 • #include <stdio.h> • void sca( int a[ ], int n ) • { …… } • void prt( int a[ ], int n ); • { …… } • void sort( int a[ ], int n ) • { …… } • main() • { • int a[10]; • sca( a, 10 ); sort( a, 10 ); prt( a, 10 ); • }
参数传递上的灵活性 • 数组传递上的偏移 • prt( a, 10 ); 输出a[0]至a[9] • prt( a, 5 ); 输出a[0]至a[4] • prt( a+1, 5 ); 输出a[1]至a[5] • prt( a+4, 3 ); 输出a[4]至a[6] • 输入10个整数,对中间8个排序 • sca( a,10 ) • sort( a+1, 8 ); • prt( a, 10 );
三、参数传递 • 函数调用与返回的具体过程 • 传递数值
int max(int a,int b) { int c; if ( a>b ) c = a; else c = b; return c; } main() { int x,y,z; scanf(“%d%d”,&x,&y); z=max(x,y); printf(“%d\n”,z); z=max(3,5); printf(“%d\n”,z); } 调用过程(形参、实参与返回)
main函数有变量x,y,z 调用max函数时,实参为x,y,形参为a,b 实参x,y被复制到max函数的形参变量a,b max函数用形参a,b进行计算,结果放在c中 return c时,变量c的值被复制到指定寄存器,并返回main函数 main函数从指定寄存器取数送到z变量 调用过程说明
四、递归函数 • 递归函数正向设计 • 阶乘、排序 • 递归函数运行跟踪 • 阶乘 • 递归函数运行求解 • 阶乘、数列
求阶乘 n! 1 n1 n! = n * (n-1)! n>1 long jcheng(int n); 递归函数实现方法 long jcheng(int n) { long rst; if ( n <= 1 ) rst = 1; else rst = n*jcheng(n-1); return rst; } [例6.15] 递归阶乘举例
递归函数运行跟踪 • 以阶乘为例 • 计算机运行jcheng(3)的过程 • 板书分析
递归函数求解 • 简单函数:没有使用静态与全局变量,函数功能也与文件/系统无关。 • 原则:多次调用函数时如果所有参数一样,则运行结果一定一样,与调用位置无关。 • 简单递归函数:只有一个参数(多数为n),没有使用静态与全局变量。
当n=1时,可得jcheng(1)返回1。 当n=2时,计算rst = 2 * jcheng(1),由第1步可知,rst=2,故jcheng(2)返回2。 当n=3时,同理有rst =3*jcheng(2)=3*2=6,故jcheng(3)返回6。 递归函数实现方法 long jcheng(int n) { long rst; if ( n <= 1 ) rst = 1; else rst = n*jcheng(n-1); return rst; } 阶乘求解举例
prtlist(0)输出“0 ”。 prtlist(1)先输出“1 ”,再调用prtlist(0),由上一步,输出“0 ”,重复一次,输出“1 ”和“0 ”,故prtlist(1)输出“1 0 1 0 ”。 prtlist(2)先输出“2 ”,再调用prtlist(1),输出“1 0 1 0 ”,重复一次,故prtlist(2)输出“2 1 0 1 0 2 1 0 1 0 ” void prtlist( int n ) { if ( n == 0 ) printf( “0 ” ); else { printf( “%d ”, n ); prtlist( n-1 ); printf( “%d ”, n ); prtlist( n-1 ); } } 数列求解举例
五、变量作用域与存贮类别 • 变量作用域 • 内存分配形式 • 存贮类别 • 动态局部变量 • 静态局部变量 • 全局变量
变量作用域 • 局部变量 • 形式参数以及在函数内部定义的变量 • 只有该函数才能访问 • 全局变量 • 在函数外部定义的变量 • 所有函数都能访问
全局变量举例 int a[10]; void scaX(void) { int i; for ( i=0;i<10;i++ ) scanf(“%d”,&a[i]); } void prtX(void) { int i; for ( i=0;i<10;i++ ) printf(“%d ”, a[i]); }
续 void sortX(void) { int i, j, t; for ( i = 0; i < 9; i++ ) for ( j = 0; j < 9-i; j++ ) if ( a[j] < a[j+1] ) { t=a[j];a[j]=a[j+1];a[j+1]=t;} } main() { scaX(); sortX(); prtX(); }
变量在内存中安排 • 静态区段(数据段) • 所有全局变量 • 程序装入内存时安排变量地址并初始化 • 在程序运行中地址不变 • 动态区段(堆栈段) • 一般局部变量 • 在函数调用时才给变量安排地址并初始化 • 各次调用之间安排的地址可能不同 • 分配区段(堆区) 见第七章malloc函数
变量存贮类别(1) • 动态变量(auto) • 只适用于局部变量 • 在类型前加auto修饰,也可省略不写 • 如:auto int i; 或 int i; • 变量安排在动态区段 • 此前所有程序的局部变量都是动态变量
变量存贮类别(2) • 静态变量(static) • 在类型前加static修饰 • 静态局部变量 • 该变量将安排在静态区段,有固定地址,并只在程序装入时初始化一次。 • 静态全局变量 • 限制该全局变量只能被同一个源程序内的各函数所访问。
int d=1; void func2(int p) { static int d=4; d+=p++; printf(“p=%d d=%d\n”, p, d); } main() { int i, a=5; for (i=0; i<2; i++) { func2(a); d+=a++; printf(“a= %dd=%d\n”, a, d); } } 【例6.24】阅读程序(全局变量和静态变量)
静态变量的作用 • 使得函数能“记住”上一次调用时的运行情况,简单智能。 • 如计算阶乘时,可从上一次结果继续乘 • 相对“稳定”的常量,不需要每次调用时都进行初始化,加快运行 • 如各月天数,static int days=[31,28,31…
变量存贮类别(3) • 外部变量(extern) • 只用于全局变量,多个源程序时使用。 • 其中一个源程序不加extern修饰 • 其它源程序都要加extern修饰 • 寄存器变量(register) • 只用于局部变量,有“可能”加快程序运行 • 可按auto理解 • 一般不建议使用
输入输出函数 stdio.h 数学函数 math.h 字符串函数 string.h 常用子程序 stdlib.h 控制台函数 conio.h 动态分配函数 malloc.h 图形函数 graphics.h 其它如: dos.h、bios.h、time.h 函数帮助(ctrl-F1) 在函数名位置上 在#include文件名上 在空白处 六、C常用库函数(附录IV)
max函数:返回最大值所在下标 • int max( int a[ ], int n ) • { • int i,k=0; • for ( i=1; i<n; i++ ) • if ( a[k] < a[i] ) • k = i; • return k; • }
sort函数:选择法,从大到小 • void sort( int a[ ], int n ) • { • int i,k,t; • for ( i=0; i<n-1; i++ ) • { • k = max( a+i, n-i ) + i; • if ( k!=i ) • { t=a[k]; a[k]=a[i]; a[i]=t; } • } • }
find函数:查找并返回下标 • int find( int a[ ], int n, int x ) • { • int i; • for ( i=0; i<n; i++ ) • if ( a[i] == x ) • return i; • return -1; • }
统计出现次数 • int count( int a[ ], int n, int x ) • { • int ct=0, i; • for ( i=0; i<n; i++ ) • if ( a[i] == x ) • ct++; • return ct; • }
输出只出现一次的数 • void prt1( int a[ ], int n ) • { • int i; • for ( i=0; i<n; i++ ) • if ( count(a,n,a[i]) == 1 ) • printf( “%d\t”, a[i] ); • printf( “\n” ); • }
输出出现3次及以上的数 • void prt3( int a[ ], int n ) • { • int i; • for ( i=0; i<n; i++ ) • if ( count(a,i,a[i])==0 && /*a[0]至a[i-1],0次*/ • count(a+i,n-i,a[i])>=3 ) /*a[i]至a[n-1],3次*/ • printf( “%d\t”, a[i] ); • printf( “\n” ); • }
从a数组选择出现3次的数到数组b中,并返回相应个数从a数组选择出现3次的数到数组b中,并返回相应个数 • int get3( int a[ ], int n, int b[ ] ) • { • int i, k=0; • for ( i=0; i<n; i++ ) • if ( count(a,i,a[i])==0 && /*a[0]至a[i-1],0次*/ • count(a+i,n-i,a[i])>=3 ) /*a[i]至a[n-1],3次*/ • b[k++] = a[i]; • return k; • }