C语言之函数
结构化程序设计主张按功能来分析需求,主要原则自顶向下,逐步求精,模块化等。
主张按功能把软件系统逐步细分,每个功能都负责对数据进行一次处理,每个功能接收一些数据,处理完后输出一些数据,这种处理方式也被称为面向数据流的处理方式。
其最小单位是函数,每个函数负责一个功能,整个软件系统由一个个函数组成,其中作为程序入口的函数被称为主函数。
主函数依次调用其他普通函数,普通函数之间依次调用,从而完成整个软件系统的功能。
一个C程序可由一个主函数和若干个其他函数构成,主函数调用其他函数,其他函数也可互相调用。
函数定义的一般形式:(无参函数/有参函数/空函数)
无参函数形式:
类型标识符 函数名()
{
声明部分;
语句部分;
}
有参函数形式:
类型标识符 函数名(形式参数列表)
{
声明部分;
语句部分;
}
空函数形式:在程序开发初始阶段,可以在将来准备扩充功能的地方写上一个空函数,采用实际函数名,只是这些函数未编好,先占一个位置,以后用一个编好的函数代替它。
类型说明符 函数名()
{
}
函数参数和函数的值
形参与实参
定义函数时,函数名括号里的参数为形参;
调用函数时,被调用函数括号里的为实参;
关于形参与实参的说明:
1)在定义函数中指定的参数,在未出现函数调用时,它们并不占内存中的存储单元。在发生函数调用时才分配内存单元。调用结束后,形参占用的内存也被释放。
2)实参可以是常量,变量或表达式。
3)在被定义的函数中,必须指定形参的类型。
4)实参与形参的类型应相同或兼容。
5)实参向形参传递数据,“值传递”,单向的
6)返回值由return语句获得。如果函数值的类型与return语句中的表达式不一致,则以函数类型为准。即函数类型觉得返回值的类型。
7)对于不带返回值的函数,用void定义函数为无类型(空类型);
函数调用:按函数在程序中出现的位置来分,可以分为三种调用方式。
函数的嵌套调用
C语言不可嵌套定义函数,函数之间是平等的,但可以嵌套调用函数。
//用截距法求方程 #include<stdio.h> #include<math.h> //define function f float f(float x) { float y; y=((x-5)*x+16)*x-80; return y; } //define function of cross point with x: xpoint float xpoint(float x1,float x2) { return ((x1*f(x2)-x2*f(x1))/(f(x2)-f(x1))); } // define root function 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.00001); return x; } int main() { float x1,x2,f1,f2,x; 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 %f\n",x); }
函数的递归调用
在定义一个函数的过程中有出现直接或间接地调用该函数本身,称为函数的递归调用。
int f(int x) { int y,z; z=f(y); return z; }
//求阶层 #include<stdio.h> float frac(int n) { float f; if(n<0) { printf("error"); } else if(n==0||n==1) f=1; else f=frac(n-1)*n; return f; } int main() { float y; int n; printf("input an integer:"); scanf("%d",&n); y=frac(n); printf("%d!=%10f\n",n,y); }
数组作为函数参数
数组名可以做形参和实参,传递数组首地址;
#include<stdio.h> float average(float a[10]) { int i; float aver,sum=0; for(i=0;i<10;i++) { sum=a[i]+sum; } aver=sum/10; return aver; } int main() { float score[10],aver; int i; printf("input 10 scores:\n"); for(i=0;i<10;i++) scanf("%f",&score[i]); printf("\n"); aver=average(score); printf("%f\n",aver); }
说明:1)用数组名做函数参数,应该在主调函数和被调函数分别定义数组;例如score,a,数组。
2)实参数与形参类型一致。
3)在被调用函数中声明了形参数组大小为10,但在实际上,指定其大小是不起任何作用的,因为C语言编译对形参数组大小不做检查,只是将实参数组的首元素的地址传给形参数组。因此,形参数组名获得了实参数组的首元素地址。它们共占统一地址,同一存储单元。score[n]和a[n]具有相同的值。
4)形参数组可以不指定大小,在定义数组时在数组名后面跟一个空的括号。也可设置另一个形参。
5)用数组名作函数实参时,不是把数组元素的值传递给形参,而是把实参数组的元素的地址传递给形参数组。这样两个数组共占用一段内存单元。
局部变量和全局变量:从变量的作用域(空间)角度来分。
局部变量:在一个函数内部定义的变量是内部变量,只在函数范围内有效。本函数才能使用它们。
1)主函数中定义的变量(m,n)也只在主函数中有效,主函数也不能使用其他函数中定义的变量。
2)不同函数中可以使用相同名字的变量,它们代表不同的对象,互不干扰。
3)形参也是局部变量。
4)在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效。
全局变量
对应于局部变量,在函数外部定义的变量称为外部变量,全局变量。其他函数可以调用。
全局变量增加了函数间数据联系的渠道。一般将全局变量名的第一个字母大写。
1)全局变量在程序的全部执行过程都占用内存单元,而不是仅在需要时开辟单元。
2)它使函数的通用性降低了,因为函数在执行时要依赖于其所在的外部变量。
3)使用全局变量过多,会降低程序的清晰性,人们往往难以清楚地判断出每个瞬间各个外部变量的值。因此,要限制外部变量。
4)如果同一源文件中,外部变量与局部变量同名,则在局部变量的作用范围内,外部变量被“屏蔽”,即它不起作用。
变量的存储类别
动态存储方式与静态存储方式(从变量值存在的时间(生存期))
静态存储方式:在程序运行期间由系统分配固定的存储空间的方式。
动态存储方式:在程序运行期间根据需要进行动态分配存储空间的方式。
全局变量全部放在静态存储区,在程序开始时分配空间,占据固定的内存单元。程序执行结束释放内存。
动态存储区存放以下数据:
1)函数形参
2)自动变量(auto)
3)函数调用时的现场保护和返回值等
以上数据在函数调用开始时分配动态内存空间,函数结束释放。如果一个程序调用两次同一函数,分别给函数形参不同的存储空间。
变量有两个属性:存储类型(数据在内存中存储的方式:动态和静态)和数据类型
具体包括四种:
auto变量
如果不专门声明为static来存储类别,都是动态分配存储空间的。
用static声明局部变量
有时希望函数中局部变量的值在函数调用后不消失而保留原值,即其占用内存单元不释放,在下一次调用该函数时,该变量已经有值,即上一次函数调用结束时的值。
1)静态句变量属于静态存储类别,在静态存储区分配空间。整个运行过程不释放内存。
2)只赋初值一次。
3)如果定义局部变量时不赋值的话,则对静态局部变量来说,编译时自动赋初值0或空字符;
对自动变量而言,不赋初值则分配一个不确定的值。
4)虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的。
register寄存器变量
c语言语序将局部变量的值放在CPU中的寄存器中,需要用时直接从寄存器取出参加运算,不必再到内存中去存取,这样提高效率。用关键字 register声明。
1)只有局部自动变量和形参可以作为寄存器变量,其他(全局)不行。
2)由于计算机中寄存器数目有限,不能定义任意多个寄存器变量;
3)局部静态变量不能定义为寄存器变量;
用extern声明外部变量
外部变量时在函数的外部定义的全局变量,它的作用域时从变量的定义处开始,到本程序文件的尾部。编译时分配在静态存储区。有时需要用extern声明外部变量,以扩展外部变量的作用域。
用static声明外部变量
在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用;
在程序设计过程中,常由若干人分别完成不同的各个模块,各人可以独立地在其设计的文件中使用相同的外部变量名而互不相干。只需要在每个文件的开头加上static声明即可。
内部函数和外部函数
函数本质上是全局的,因为一个函数要被另外的函数调用,但也可以指定函数不能被其他文件调用。根据函数能否被其他源文件调用,将函数分为内部函数和外部函数;
内部函数(静态函数)
如果一个函数只能被本文件中的其他函数所调用,它称为内部函数。函数的作用域只局限于所在文件,在不同的文件中有同名的内部函数,互不干扰。在函数名和函数类型前面加static;
static int fun(int a, int b);
外部函数
1)在定义函数时,如果在函数首部的最左端加关键字extern,则表示此函数时外部函数,可供其他函数调用。默认为外部函数。
2)在需要调用此函数的文件中,用extern对函数作声明,表示该函数是在其他文件中定义的外部函数。