例:编程求3至100之间满足下列条件的各组素数:每组有3个素数,第2个比第1个大2,第3个比第2个大4。
§8.1 概述
分类:
例:
#include<math.h>
main( )
{ int x;
float y;
scanf("%d",&x);
y=sin(x);
printf("%f ",y);
}
int max( int x, int y)
{ int z;
z=x>y ? x : y ;
return(z);
}
main( )
{ int a,b,c;
scanf("%d%d",&a,&b);
c=max(a,b);
printf("max=%d\n",c);
}
§8.2 函数的定义
三、空函数
§8.3 函数的调用
三、函数参数和函数的值
例4:
int sa(int x,int y)
{ if(x>y) return(1.6);
if(x==y) return(0.7);
return(-1.9);
}
main( )
{int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",sa(a,b));
printf("%d\n",sa(6,2*a));
}
说明:
1.实参 → 形参单向值传递。
2.形参为变量时,实参可以是表达式。
3.实参与形参类型一致或赋值兼容。
4.函数的返回值通过 return 语句获得,return 语句可以有0~n个。
5.函数类型决定返回值的类型。
四、对被调用函数的声明
例5
main( )
{int a,b;
void swap(int x,int y);
scanf("%d%d",&a,&b);
swap(a,b);
printf("a=%d,b=%d\n",a,b);
}
void swap(int x,int y)
{int z;
z=x; x=y; y=z;
printf("x=%d,y=%d\n",x,y);
}
可以省略对被调函数声明的几种情形:
1. 被调函数的定义出现在主调函数之前。
2. 在所有函数定义之前,在函数的外部已做了函数声明(例如下图代码)。
3. 被调用函数类型为整型。(不提倡)
例1: 编写求阶乘的函数。
float jiec(int n)
{ float y=1;
int i;
for (i=1;i<=n;i++)
y=y*i;
return (y);
}
main( )
{ int i; float s;
s=0;
for(i=1;i<=10;i++) s=s+jiec(i);
printf("%e\n",s);
}
例2: 编写判断一个整数是否素数的函数。
求一组数中素数之和?
main( )
{int a[100],n,i,num=0;
scanf("%d",&n);
for(i=0;i<n;i++)
{scanf("%d",&a[i]);
if (prime(a[i])==1)
num++;
}
printf("num:%d\n",num);
}
编写函数时考虑:
1. 是否有返回值。→ 函数值类型
2. 函数名不可与关键字或主调函数中其它量重名。
3. 形参:需要从主调函数接收几个数据、类型。
4. 若有返回值需有 return 语句。
例3: 编写函数,判定某数字是否在某正整数中,若在,打印 TRUE,否则打印 FALSE 。
main( )
{ int m,n;
int among(int m, int n );
scanf("%d %d",&m,&n);
if ( among(m,n) )
printf("TRUE\n");
else printf("FALSE\n");
}
int among(int m, int n)
{ int k, z=0;
do
{k=m%10;
if (n==k) {z=1; break; }
m=m/10;
}while(m!=0);
return z ;
}
例4: 统计400~499这些数中4这个数字出现的次数,判一个数有几位4数字用函数实现。
num(int x)
{ int y,k=0;
while (x!=0)
{y=x%10;
if(y==4) k++;
x=x/10;
}
return(k);
}
main( )
{int i,k=0;
for(i=400;i<=499;i++)
k=k+num(i);
printf ("number=%d\n",k);
}
例5: 找出1000之内的所有“完数”,判一个数是否为完数用函数实现 。
wan(int x)
{ int i,k=0;
for (i=1;i<=x/2;i++)
if (x%i==0) k=k+i;
if (k==x) return (1);
else return (0);
}
main( )
{ int i;
for(i=1;i<1000;i++)
if(wan(i)) printf ("%5d",i);
printf ("\n"); }
§8.5 函数的嵌套调用
不能嵌套定义函数,可以嵌套调用函数 。
例6: 写两个函数,分别求两个整数的最大公约数和最小公倍数,用主函数调用这两个函数,并输出结果 。
int gcd(int a,int b)
{int r,t;
if (a<b)
{ t=a; a=b; b=t; }
r=a%b;
while (r!=0)
{ a=b;b=r; r=a%b;}
return (b);
}
int lcm(int a,int b)
{ int r;
r=gcd(a,b);
return(a*b/r);
}
main( )
{ int x,y;
scanf ("%d%d",&x,&y);
printf ("%d\n",gcd(x,y));
printf ("%d\n",lcm(x,y));
}
§8.6 函数的递归调用
在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
递归问题的特点 :
1. 把一个问题转化为一个新问题,新问题与原问题解法相同,只是所处理的对象有所不同,它们是有规律的递增或递减。
2. 必须有某个终止递归的条件。
例7: 用递归法求n!
float f(int m, int n)
{ float z;
if(n==0) z=1;
else z=m*f(m,n-1);
return z;
}
main( )
{ int m,n;
scanf ("%d%d",&m,&n);
printf ("%d, %d, %.0f\n",m,n,f(m,n));
}
§8.7 数组作为函数参数
1、数组元素作函数实参
例1:读程序,写结果。
思考?
例3: 用冒泡法对数组中5个整数按升序排序。
冒泡法的思路是:将相邻两个数比较。
例3: 用冒泡法对数组中n个整数按升序排序。
main( )
{ int a[1000],i,n;
void sort(int a[ ],int n);
scanf("%d",&n);
for (i=0;i<n;i++)
scanf ("%d",&a[i]);
sort(a,n);
for (i=0;i<n;i++)
printf ("%5d",a[i]);
printf ("\n");
}
void sort(int a[ ], int n)
{ int i, j, t;
for(i=1;i<=n-1;i++)
for(j=0;j<=n-i-1;j++)
if (a[j]>a[j+1])
{ t=a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
注意:
数组名作参数的好处:
1、由于只需复制一个地址值,而无须复制全部需要处理的数据,因此节约存储空间并提高效率。
2、由于主调函数和被调函数是在相同的内存区域上对数据进行操作,因此可以实现数据的同步更新。
例4: 有一个3×4的矩阵,求其中的最大元素。
int arrmax(int y[ ], int n)
{ int i,max;
max=y[0];
for(i=1;i<n;i++)
if(y[i]>max) max=y[i];
return(max);
}
main( )
{ int a[3][4]={{1,3,25,17},{22,4,6,18},{15,17,36,12}};
printf ("%d\n",arrmax(a,12));
}
例5: 求3×3矩阵转置
void turn(int b[][3])
{ int i, j, k;
for (i=0;i<3;i++)
for (j=0;j<i;j++)
{ k=b[i][j];
b[i][j]=b[j][i];
b[j][i]=k;
}
}
main()
{ static int a[3][3]={{1,3,5},
{2,4,6},{15,17,34}};
int i, j;
turn(a);
for (i=0;i<3;i++)
{ for (j=0;j<3;j++)
printf ("%5d",a[i][j]);
printf ("\n");
}
}
例6:写一个函数比较两个字符串s1,s2, 如果s1=s2,则返回0,如果s1>s2, 则返回1,如果s1<s2,则返回-1。
int comp(char s1[ ],char s2[ ])
{int z,i;
for(i=0;s1[i]!='\0';i++)
if(s1[i]!=s2[i]) break;
if(s1[i]>s2[i]) z=1;
else if(s1[i]==s2[i]) z=0;
else z=-1;
return(z);
}
main()
{char c1[40],c2[40];
int x;
gets(c1);
gets(c2);
x=comp(c1,c2);
printf("%d\n",x);
}
例7: 调用递归函数求整型数组中10个元素之和。
int sum(int a[ ], int n)
{ int s;
if (n==0) s=a[0];
else s=sum(a,n-1)+a[n];
return(s);
}
main( )
{ int a[10],i;
for (i=0;i<10;i++)
scanf("%d",&a[i]);
printf("%d\n",sum(a,9));
}
8.8 局部变量和全局变量
一、局部变量
在函数内定义的变量。
说明 :
- 每个函数中定义的变量,只在定义它的函数中有效;
- 不同函数可以使用相同名字的变量,但意义不同;
- 形式参数是局部变量 ;
- 可以在复合语句中定义变量,但它们只在本复合语句中有效 。
二、全局变量
- 外部变量:在函数之外定义的变量;
- 外部变量是全局变量;
- 作用范围:从定义变量的位置开始到本源文件结束。
例
例
例:在一维数组中存放10个学生的成绩,写一个函数,求出平均分,最高分和最低分。
分析:
- 1. 平均分由函数值带回
- 2. 设max,min为全局变量
float max,min;
float average(float a[],int n)
{ int i;
float aver,s=a[0];
max=min=a[0];
for (i=1;i<n;i++)
{ if (a[i]>max) max=a[i];
else if (a[i]<min) min=a[i];
s=s+a[i];}
aver=s/n; return(aver);
}
main( )
{ float ave,score[10];
int i;
for (i=0;i<10;i++) scanf ("%f",&score[i]);
ave=average(score,10);
printf ("%6.2f,%6.2f,%6.2f\n",max,min,ave); }
void average(float a[ ], int n, float b[ ])
{ int i;
b[0]=b[1]=b[2]=a[0];
for (i=1;i<n;i++)
{ if (a[i]>b[0]) b[0]=a[i];
else if (a[i]<b[1]) b[1]=a[i];
b[2]=b[2]+a[i];}
b[2]=b[2]/n; }
main( )
{ float x[3], score[10]; int i;
for (i=0;i<10;i++) scanf ("%f",&score[i]);
average(score,10,x);
printf ("max=%6.2f,min=%6.2f,ave=%6.2f\n",x[0],x[1],x[2]);
}
§8.9 变量的存储类别
变量的存储类别
静态存储变量:程序运行期间分配固定的存储空间,存放全局变量或静态局部变量。
动态存储变量:根据需要动态分配存储空间,存放:
- 函数形参变量 ;
- 局部变量(未加 static 说明的);
- 函数调用时的现场保护和返回地址等 。
局部变量的存储方式
局部动态变量:( [ auto ] )
- 函数调用后,值不予保留,即释放存储空间 。
- 再次调用时,原值不能引用 。
局部静态变量: ( static )
- 函数调用后保留原值,即不释放所占存储空间;
- 再次调用时,原值在本函数内仍可使用 。
例
例
小结:
1. 按作用域分为局部变量和全局变量
局部变量:动态局部变量(离开函数,值就消失。)
静态局部变量(离开函数,值仍保留。)
全局变量:值始终保留。
2. 按存在时间分为动态存储和静态存储两种类型
动态存储:调用函数时临时分配单元 。
静态存储:程序整个运行期间都存在 ;
动态存储: 自动变量(本函数内有效)
形式参数(本函数内有效)
静态存储: 全局变量
静态局部变量(本函数内有效)
3. 按变量存放位置分为
内存中静态存储区: 全局变量
静态局部变量
内存中动态存储区: 自动变量
形式参数
4. 关于作用域和生存期的概念
1) 自动变量和形式参数的作用域和存在性一致,离开函数后,值不存在,不能被引用 。
2) 全局变量的作用域和存在性也一致,离开函数后,变量值仍存在,可被引用 。
3) 静态局部变量的作用域和存在性不一致,离开函数后,变量值存在,但不能被引用 。
全局变量的存储方式
静态存储
外部变量
1) 在函数外部定义。
2) 允许其他文件中的函数引用, 需要用 extern 作说明。
静态外部变量
1) 在函数外部用static定义
2) 只能被本文件中的函数引用
存储类别小结
1. 按作用域分为局部变量和全局变量
2. 按存在时间分为动态存储和静态存储两种类型
静态存储是程序整个运行期间都存在 ;
动态存储则是在调用函数时临时分配单元 。
3. 按变量存放位置分为 :
4 . 关于作用域和生存期的概念
1) 自动变量和寄存器变量的作用域和存在性一致,离开函数后,值不存在,不能被引用 。
2) 静态外部变量和外部变量的作用域和存在性也一致 , 离开函数后,变量值仍存在,可被引用 。
3) 静态局部变量的作用域和存在性不一致 , 离开函数后,变量值存在,但不能被引用 。
5. static对局部变量和全局变量的作用不同
1) 对局部变量 :使动态存储方式改变为静态存储 ;
2) 对全局变量 :使变量局部化,但仍为静态存储 ;
3)凡有 static 说明的,其作用域都是局限的。
§8.10 内部函数和外部函数
根据函数能否被其它源文件调用,将函数分为内部函数和外部函数。
1 内部函数 :如果一个函数只能被本文件其它函数所调用,它称为内部函数。
static 类型标识符 函数名(形参表)
2 外部函数 :
[ extern ] 类型标识符 函数名(形参表)
例23 有一个字符串,内有若干个字符,今输入一个字符,程序将字符串中该字符删去。用外部函数实现。
§8.11 如何运行一个多文件的程序
用Turbo C集成环境
用#include命令
例4: 产生15个[10,90]上的数放入a数组中,找出其中的素数放入b数组,并求b数组中元素的和。
要求: 1. 求素数用函数sushu( )完成
2. 求素数的和用函数sum( )完成
#include <time.h>
#include <stdlib.h>
main( )
{ int a[15],b[15],i,m;
randomize( );
for(i=0;i<15;i++)
a[i]=random(81)+10;
m=sushu(a,b);
for(i=0;i<m;i++)
printf("%4d",b[i]);
printf("\n");
printf("sum=%d\n", sum(b,m));
}
int sushu(int a[ ],int b[ ])
{ int i,j,k=0;
for(i=0;i<15;i++)
{for(j=2;j<=a[i]-1;j++)
if(a[i]%j==0) break;
if(j>a[i]-1)
{b[k]=a[i];k++;}
}
return(k);
}
int sum(int b[ ], int n)
{ int i, s=0;
for(i=0;i<n;i++) s=s+b[i];
return(s);
}