文章目录
概念:
6.1 函数的基本概念
由一个主函数和若干个其他函数构成c程序。同一个函数可以被多个函数调用任意多次。
注意:c语言中调用一个函数之前必须先声明。
a) 一个文件中可以有一个或者多个函数。这个文件我们一般就称为源程序文件
b) 大项目源程序不能都放一个文件中,所以一个c项目是由一个或者多个源程序文件组成的。这些函数可以分别放到这些源程序文件里并被所有的源程序公用。
c) c程序从main函数开始执行,最终也是从main函数中结束整个程序的运行。
d) 函数不能嵌套,不要手工去调用main() 函数,这个是留给系统调用的。
e)
函数分为两类: 库函数, 自定义函数。
库函数: 直接使用,不需用我们自己定义。 例如: strcpy , strcmp , strlen .
自定义函数: 用于解决某种需求而自己写的函数。
什么叫函数: (编写函数)
#include <stdio.h>
#include <string.h>
// 函数的定义,也相当于声明,写在主函数的上面。
void printhello() //自己编写的函数, 将
{
printf("hello world\n");
}
//--------------------------------------------------
int main() //主函数
{
printhello(); // 在主函数中调用自己写的函数。
printf("off");
return 0;
}
函数的定义和返回值
函数参数: 所谓函数的参数,就是我调用函数的时候,希望把一些数据传递给该函数, 这个时候,该函数就需要一些变量来接收这些数据。这些接受数据的变量,就叫函数参数(形式参数)。
函数定义的一般形式:
返回类型 函数名(形式参数列表) //函数里边的参数叫形式参数,简称形参。
{
....
语句
return 返回值;
}
a) 无返回类型 , 无形参
注: void 表示函数没有返回值。
没有返回值的语句可以不用写return.
b) 有参数,有返回值的
- 函数定义的第一行是没有;的直接接{
- 形式参数在函数调用之前并不分配内存。调用的时候分配内存,函数调用结束后形式参数的内存就被释放了,所以形式参数只能在函数内部使用。
- 实参可以是常量、变量、表达式:
- 函数调用的时候实参的值自动赋给了形参,如果实参和形参为数组名(数组名代表数组首地址),则传递进去的是数组首地址,而不是变量值。
- 形参数量和类型 与实参数量和类型要保持一致。
- c语言规定实参变量对形参变量的数据传递是”值传递“ , 也就是单项传递,只由实参传递给形参,不能由形参传递给实参。
- 如果你返回的类型和函数定义的返回类型不同时,则系统会自动转成函数返回值类型。
#include <stdio.h>
int addition(int a, int b) //a,b叫做形参,表示有数据要传递到本函数来,用这些参数接收这些传递来的数据;
{
int sum = a+b;
a = 19; //改变值也没用,因为传递是实-形
return sum; // return a+b;
}
int main() {
int result = addition(3,4*5); //实际参数,实参
printf("result=%d\n",result);
int i = 3, j = 4;
result = addition(i,j);
printf("result=%d\n",result);
}
int whichmax(int a, int b)
{
if(a>b)
return a;
else
return b;
}
int i =3, j = 4;
result = additon(13,18);
6.2 函数调用方法及嵌套调用
6.2.1 函数调用的一般形式
注意严格区分函数调用和函数定义 这两个概念的区别
函数调用 一般形式:
函数名(实参列表);
注意:
a)若调用的是没有形参的函数,实参列表可以没有。但是括号不能省;如果实参列表包含多个参数,则各个参数之间使用 , 分割。
对于函数中,如果形参包含多个参数。 则也用逗号分开。
b) 实参,形参 个数要相等,类型要一致,按顺序对应,一一传递。
6.2.2 函数调用的方式
定义、声明、调用 需要严格区分
函数定义
#inculde <stdio.h>
**// 函数定义:**
int whichmax(int a, int b)
{
if(a>b)
return a;
else
return b;
}
按照函数调用在程序中出现的位置,有三种函数调用的方式。
a ) 把函数作为一个语句,
printhello(); //作为语句调用
b ) 函数出现在一个表达式中,这种表达式称为函数表达式,这个时候要求函数带回一个确定的值以参加表达式的运算。
int main(){
{
**result = whichmax(13,18) * 100;**
printf("%d\n",result);
return 0;
} //结果为 1800;
c ) 甚至函数调用可以作为函数的参数,也就是把函数调用作为一个函数的实参。
int main()
{
result = whichmax(13, **whichmax(12,19)**); //这个也是可以的。
printf("%d\n",result);
return 0;
}
函数声明
c语言要求调用一个函数之前必须声明该函数。
目的:保证所有的函数可以顺利调用其他的函数。
函数定义:包含函数体,确定了函数的具体功能。
函数声明:只是对已定义的函数进行说明。
1、函数的定义 == 函数的声明
2、函数声明应放在最前边所有的函数定义之前
3、必须把函数声明放在任何源代码文件的具体函数之前(一般也就是源代码的开头。 才能保证这些具体的函数调用其他函数时这些被调用的函数是声明过的函数。
函数定义:
类型标识符 函数名(形参列表){
...
}
**函数声明的一般形式:
类型标识符 函数名(形参列表); //逗号与{ 的区别。**
如果必须将函数定义写在后面的话那么必须在源代码最前面进行函数声明。
函数调用
函数调用一般在主函数中进行调用。
#include <stdio.h>
int WhichMax(int a, int b){
if(a>b)
return a;
else
return b;
int main(){
int result = WhichMax(3,5); //这就是函数的调用
printf("%d\n",result);
return 0;
6.2.3 函数的嵌套调用
注意:
- 自建函数不能写在main的主体中。
- c语言中不允许在一个函数中定义另外一个函数(也就是说,不允许函数的嵌套定义)
- c语言中每个函数都是平行和独立的。不允许嵌套定义。
- c语言允许函数嵌套调用。(在调用一个函数的过程中,被调用的函数在去调用第三个函数…)
函数的调用:
#inculde <stdio.h>
**// 函数定义:
int whichmax(int a, int b); //函数声明
void NestedFunction(); //函数声明
int main(){
void NestFunction(); //函数调用
return 0;
} //结果为: move out**
int whichmax(int a, int b) //函数定义
{
if(a>b)
return a;
else
return b;
}
void Nested Function() //函数定义
{
printf("move out\n");
}
函数的嵌套调用:
#inculde <stdio.h>
**// 函数定义:
int whichmax(int a, int b); //函数声明
void NestedFunction(); //函数声明
void NestedFunction1();
int main(){
void NestFunction();
return 0;
} //结果为: move out action**
int whichmax(int a, int b) //函数定义
{
if(a>b)
return a;
else
return b;
}
void NestedFunction() //函数定义 NestedFunction
{
printf("move out ");
**NestedFunction1(); //函数的嵌套调用**
}
void NestedFunction1() //定义函数 NestedFunction1
{
printf("action\n);
}
6.3 函数递归调用解析
6.3.1 函数递归调用的定义
recursion . 递归
调用栈(一块系统分配给咱们这个程序的有特殊用途的内存)
这段内存是有限的,如果一旦超过了这个内存大小,超出了极限,就会出现崩溃现象。
递归调用:
因为自己调用自己会导致死循环问题,所以这种递归调用必须要有一个出开口,也就是说这个出口是递归的结束条件。
递归就是一个函数
递归函数: 在函数体内调用自己,执行递归函数将反复调用其自身,每调用一次就进入了一个新层。(往里边走了一步,前面的都被保留)
6.3.2 函数递归调用的出口
例如:
**计算5的阶乘( 计算 == 1*2*3*4*5)**
分析:
5的阶乘 == 4的阶乘 * 5
4的阶乘 == 3的阶乘 * 4
3的阶乘 == 2的阶乘 * 3
2的阶乘 == 1的阶乘 * 2
1的阶乘 == 1
//递归调用的出口是在1的阶乘,我们可以求出2的阶乘,
2的阶乘 * 3 = 3的阶乘
3的阶乘 * 4 = 4的阶乘
4的阶乘 *** 5 = 5的阶乘**
//求5的阶乘的递归函数的代码
#include <stdio.h>
int Factorial(int n); //函数声明
int main(){
int result = Factorial(5); //函数调用
printf("%d\n",result);
return 0;
}
int Factorial(int n){ //函数定义
int result;
if(n==0)
{
return 1;
}
else
{
result = Factorial(n-1) * n;
}
return result ;
}
递归:
优点: 代码少,简洁,精妙
缺点: 不好理解,调用层次太深,其调用栈(内存)可能会溢出,导致效率和性能降低。
有些问题可能必须使用递归解决。
递归函数的直接和间接调用问题:
- 调用递归函数x的过程中,x函数又调用自己,这就是直接调用。
- 调用递归函数x的过程中要调用 y 函数,然后 y 函数过程中又要调用 x 函数。