程序开发中,子程序的作用由函数来完成,一个较大的程序一般分为好多个程序模块,
每个模块来实现一个特定的功能。所以要学会利用函数,减少重复的工作量。
从函数的形式上看,函数分为无参函数和有参函数。一个C程序是由一个源程序文件为
基本的编译单元,从main函数开始,各个函数的定义是平行的,独立的。
1.函数调用的简单例子:
#include<stdio.h>
void main()
{
void printer();
void printer_message();
printer();
printer_message();
printer();
}
void printer()
{
printf("*************************************\n");
}
void printer_message()
{
printf(" To C or Not to C,That is a question!\n");
}
运行结果:
*************************************
To C or Not to C,That is a
*************************************
2.函数定义的一般形式
定义无参函数的一般形式为:
类型标示符 函数名()
{
声明部分
语句部分
}
例如:
void printer()
{
printf("*************************************\n");
}
定义有参函数的一般形式为:
类型标示符 函数名(形式参数列表)
{
声明部分
语句部分
}
例如:
int max(int a,int b)
{
int z;
z=x>y?x:y;
return z;
}
如果在定义函数时没有声明函数的类型,那么默认会认为是整形,因此这里的int可以省略了。
定义空函数的一般形式为:
类型标示符 函数名()
{
}
定义空函数多用于程序设计中需要确定很多模块,先把这个模块写一个大致的结构,用空函数表示出来,
到了后期再慢慢丰富这些个空函数,这个在开发中常常用到。空函数什么工作也不做,没有任何实际作用
3.函数的参数和函数的值
A.在函数的定义和调用中,主调函数所传的参数叫实际参数,而被调函数的参数叫形式参数。
B.在定义的函数中指定的形参,在未出现函数调用时,它不占用内存中的存储单元,只有在发生函数调用时才会被分配到内存单元。在调用结束后形参所在的内存单元被释放。
C.实参可以是常量,变量,表达式,但是他们要有确定的值。
D.在被定义有参的函数中,必定定义形参的类型,而实参和形参的类型应该保持一致或赋值兼容。
E.在函数调用过程中,实参和形参的传递方向是单向的,只能由实参向形参传递,不能逆向。并且调用结束
后形参被释放,实参维持原值。
F.函数的返回值是通过return获得,并且返回值得类型应该和定义的函数的类型一致或赋值兼容,如果不做声明,则返回值自动按函数类型转化返回
#include<stdio.h>
void main()
{
int max(float,float);
float a,b;
int c;
scanf("%f,%f",&a,&b);
c=max(a,b);
printf("Max is %d\n",c);
}
int max(float x,float y)
{
float z;
z=x>y?x:y;
return z; //尽管z的类型是float型,但是函数体的类型却是int型,所以返回的时候会强制转化
}
运行结果:
123.33,1239.2
Max is 1239
4.函数的调用
函数调用的一般形式为:
函数名(实参表)
说明:如果实参列表中有很多个实参,则每个实参用逗号隔开,实参个数、类型应该和形参保持一致
如果实参有多个,不同的编译系统对实参的求值顺序不一样,有的系统自左向右,有的相反。
#include<stdio.h>
void main()
{
int f(int a,int b);
int i=2,p;
p=f(i,++i); //注意这里的顺序会在不同的编译系统里不一样,所以结果也不一样
printf("%d\n",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);
}
运行结果为:
0
说明这里我的系统是从右至左的,也就是说掉时为p=f(3,3)
根据函数在程序中出现的位置来分,有以下3种调用方式:
1.函数语句
例如:上面的printer()函数调用,把函数当成一个语句调用
2.函数表达式
函数出现在一个表达式中,例如:c=2*max(a,b)
3.函数参数
函数作为一个函数的实参来被调用。例如:c=max(a,max(a,b))
下面总结被调用函数的声明和函数原型
1.被调者必须存在,如是库函数则要用#include<xxxxx.h>包含到文件中来。
2.如果是用户自己定义的函数,而该函数的位置在主函数之后(同一文件中)应该在主函数中对其做声明(如果被掉函数是int型可以不用在主函数中声明)。如果在主函数之前可以不用声明。
3.如果被调函数在文件的开头已经声明过了那就不必在主调中声明了。
#include<stdio.h>
void main()
{
int i=2,p;
p=f(i,++i);
printf("%d\n",p);
}
f(int a,int b)
{ //这里没有在主函数中声明
int c;
if(a>b) c=1;
else if(a==b) c=0;
else c=-1;
return(c);
}
5.函数的嵌套调用
C语言中的函数定义是平行并且独立的,C语言不能嵌套定义函数,但是可以嵌套调用函数。
例如:用弦截法求方程f(x)=x^3-5x^2+16x-80=0的根
#include<stdio.h>
#include<math.h>
float f(float x)
{
float y;
y=((x-5.0)*x+16.0)*x-80.0;
return y;
}
float xpoint(float x1,float x2)
{
float y;
y=(x1*f(x2)-x2*f(x1))/(f(x2)-f(x1));
return y;
}
float root(float x1,float x2)
{
float x,y,y1;
y1=f(x1);
do
{
x=xpoint(x1,x2);
y=f(x);
if(y*y1>0)
{
y1=y;
x1=x;
}
else
{
x2=x;
}
} while(fabs(y)>=0.0001); //求浮点型y的绝对值大于等于0.0001时才终止循环
return x;
}
void main()
{
float x1,x2,f1,f2,x;
do
{
//这里为了确保有值所以用了do语句
printf("input x1,x2:\n");
scanf("%f,%f",&x1,&x2);
f1=f(x1);
f2=f(x2);
} while(f1*f2>=0);
x=root(x1,x2);
printf("A root of equation is %8.4f\n",x);
}
运行结果:
[root@localhost c]# ./a.out
input x1,x2:
2,4
input x1,x2:
2,6
A root of equation is 5.0000
6.函数的递归调用
递归调用就是在一个函数的调用过程中直接的或者间接的又把它自己给调用了成为递归调用,
那么递归调用在概念上看似一个死循环,但是它有终止调用条件,所以不构成死循环,这一
点在调用的时候要考虑好。
例如:
int f(int x)
{
int y,z;
z=f(y);
return(2*z);
}
例如:求n的阶乘
#include<stdio.h>
float fac(int n)
{
float f;
if(n<0)
{
printf("n<0,data_error!\n");
}
else if(n==0||n==1)
{
f=1;
}
else
{
f=fac(n-1)*n;
}
return(f);
}
void main()
{
int n;float y;
printf("input the number:");
scanf("%d",&n);
y=fac(n);
printf("The %d!=%-10.0f\n",n,y);
}
运行结果:
[root@localhost c]# ./a.out
input the number:6
The 6!=720