七、函数
所有c程序都是由一个或多个函数组成,一个函数调用另一个函数,前者称为主调函数,后者被称为被调函数
从用户使用角度来看,可分为:标准函数>>>>>即库函数,由C语言提供,用户可以直接调用
用户函数>>>>>人为定义的函数,用以解决用户的专门需求
从函数形式来看,可分为:无参函数>>>>>在调用这类函数时,主调函数并不将数据传送给被调用函数,一般用来执行指定的一组操作
有参函数>>>>>在调用函数时,在主调函数和被调函数之间有数据传递,主调函数可以将数据传递给被调用函数函数使用,被调用函数
中的数据也可以返回供主调函数使用
从函数作用范围来看,可分为:外部函数>>>>>函数在本质上都具有外部性质,除了内部函数外,其余函数都可以被同一程序的其他源文件中的函数调用
内部函数>>>>>内部函数只限于本文件的其他函数调用,而不允许其他文件中的函数调用
1.函数的定义和调用
(1).定义
一般形式:
类型标识符 函数名(形式参数表) //函数的首部
{
说明部分
执行部分
}
类型标识符:说明函数返回值的类型,称为函数值的类型。函数返回值通过return语句返回,系统默认返回值的类型是int型。如果函数只是用来完成某种操作,没
有函数值返回,则为void型,即空类型
函数名:由用户命名的标识符,同一程序中,函数名必须唯一
形式参数表:即形参,形参表可以为空,表示没有参数,也可以由逗号隔开的多个参数组成,括号不能省略(是识别函数的重要标志),同一函数中,形参名必须
唯一,必须说明其类型且只能在本函数内部使用,无形参时,用void表示
函数体:复合语句,由{}括起来,可分为说明部分和执行部分(也可以只有执行部分,或两者都没有即空函数),实现函数功能
说明部分>>>>>变量的定义或所调用函数的声明
执行部分>>>>>由执行语句组成,语句数量不限
函数返回:
1.在返回函数值时,函数值只能通过return语句返回 return 表达式;
2.在不返回函数值时,一种情况是函数体一直执行到函数末尾的“}”,返回到主调函数;另一种情况是使用不含表达式的return语句返回主调函数
(2).调用
函数调用的一般形式:函数名(实际参数表) //实参个数多于一个时,可以用逗号分开
当函数没有形参时,实际参数表为空,调用格式为:函数名()
调用方式:
1.函数语句:把函数调用作为一个语句,即在函数调用的一般形式加上分号即构成函数语句
puts(str);
2.函数表达式:当函数有返回值时,函数可作为运算对象出现在表达式中,函数返回值参与表达式的运算,这种表达式称为函数表达式
z = 3*max(x,y);
2.函数参数(形参、实参及参数值的传递)和函数的值
形参:形式参数
在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参。
实参:实际参数
函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参。
#include <stdio.h> //计算从m加到n的值 int sum(int m, int n) { int i; for (i = m+1; i <= n; ++i) { m += i; } return m; } int main() { int a, b, total; printf("Input two numbers: "); scanf("%d %d", &a, &b); total = sum(a, b); printf("a=%d, b=%d\n", a, b); printf("total=%d\n", total); return 0; }
形参实参的区别:
-
形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用。
-
实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。
-
实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。
-
函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参。
-
形参、实参可以同名,但是在内存中分配两个不同的存储单元
函数参数的值传递:在调用有参函数时,实参形参之间要进行数据传递,在C语言中,这种传递只能由实参单向传递给形参,这种传递方式称为值传递
形参和实参的功能是传递数据,发生函数调用时,实参的值会传递给形参
3.函数的嵌套调用和递归调用
(1).函数的嵌套调用
C语言的函数定义是相互平行的、独立的,不允许嵌套函数定义,即一个函数内不能包含另一个函数的定义。但是允许在一个函数中对另一个函数进行调用,而另
一个函数中又可以调用第三个函数
原理:执行到main函数中调用a函数的语句时,程序转去执行a函数;在a函数中调用b函数时,转去执行b函数,b函数执行完毕,返回a函数的断点继续执行,a函
数执行完毕返回main函数的断点继续执行
(2).函数的递归调用
一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数,调用可分为直接递归调用和间接递归调用
直接递归调用:函数直接调用它本身
间接递归调用:函数通过其他函数间接调用它自身
使用递归的两个条件:1.要有递归公式。 2.要有终止条件。
用递归实现 1+2+3+…+100 的和
# include <stdio.h> int Sum(int n); //函数声明 int main(void) { int n; printf("请输入n的值:"); scanf("%d", &n); printf("sum = %d\n", Sum(n)); return 0; } int Sum(int n) { if (n <= 0) { return -1; } else if (1 == n) { return 1; } else { return n+Sum(n-1); } }
4.数组作为函数参数
(1).数组元素作为函数实参
数组元素就是下标变量,它与普通变量并无区别。 因此它作为函数实参使用与普通变量是完全相同的,在发生函数调用时,把作为实参的数组元素的值传送给形
参,实现单向的值传送
判别一个整数数组中各元素的值,若大于0 则输出该值,若小于等于0则输出0值
#include <stdio.h> void nzp(int v){ if(v>0) printf("%d ",v); else printf("%d ",0); } int main(void){ int a[5],i; printf("input 5 numbers\n"); for(i=0;i<5;i++){ scanf("%d",&a[i]); nzp(a[i]); } return 0; }
(2).数组名作为函数实参
数组名作为函数参数时,形参和实参都应使用数组名,并且要求实参和形参数组类型、维数相同。按单向传递方式传递地址,即将实参数组名代表的数组首地址传
递给形参数组名,不是将实参数组的每个元素一一传送给形参各数组的元素
数组a中存放了一个学生5门课程的成绩,求平均成绩
#include <stdio.h> float aver(float a[5]){ int i; float av,s=a[0]; for(i=1;i<5;i++) s=s+a[i]; av=s/5; return av; } int main(void){ float sco[5],av; int i; printf("\ninput 5 scores:\n"); for(i=0;i<5;i++) scanf("%f",&sco[i]); av=aver(sco); printf("average score is %5.2f",av); return 0; }
(3).多维数组名作为函数实参
与一维数组类似
5.局部变量和全局变量
按照变量的作用域不同,可分为:局部变量和全局变量
(1).局部变量(内部变量)
在函数内部定义的变量只能在本函数内使用,其他函数不能使用>>>>>局部变量
函数的形参也属于局部变量
(注意:
1.在不同的作用域允许有同名变量出现,这些同名变量分别代表不同的对象,分配不同的内存单元,互不干扰
2.局部变量的定义必须放在函数体中全部可执行语句之前
3.主函数中定义的变量只能在主函数内部使用,主函数中也不能使用其他函数中定义的局部变量
4.当作用域发生嵌套时,如果内外层具有同名的局部变量,则在内层起作用的时内层定义的局部变量,外层定义的变量不可见)
int f1(int a){ int b,c; //a,b,c仅在函数f1()内有效 return a+b+c; } int main(){ int m,n; //m,n仅在函数main()内有效 return 0; }
(2).全局变量(外部变量)
在函数外部任意位置上定义的变量,从变量定义的位置开始,到它所在源文件结束为止,作用域中所有函数都能使用相应的全局变量
(注意:
1.不必要时尽量不要使用全局变量,会使函数独立性降低
2.在同一源文件中,允许全局变量和局部变量同名,在局部变量的作用域内,全局变量不起作用)
int a, b; //全局变量 void func1(){ //TODO: } float x,y; //全局变量 int func2(){ //TODO: } int main(){ //TODO: return 0; }