函数
四、函数的调用
- 1.传值调用 2.传址调用 五、函数的嵌套与链式访问 六、函数的声明和定义 七、函数的递归
一、函数是什么?
类似数学中的函数,在C语言中,函数就是相当于一个代码块,由一个或多个语句块组成,来执行某种功能,具有一定的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
二、C语言中函数的分类
1.库函数
把频繁使用的代码,指定一个标准,整理成一个库,方便引用,提高了效率!
C语言的基础库中提供了一系列类似的库函数,方便程序员 进行软件开发。
使用库函数,必须包含 #include 对应的头文件
如何学习库函数:www.cplusplus.com网站
1.1 如何学会使用库函数?
需要全部记住吗?No
需要学会查询工具的使用:
2.自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间
函数的组成:
int fun_name(int a)
{
statement;//语句项
}
//int 返回类型
//fun_name 函数名
//int 参数类型
//a 传给函数的参数
写一个函数可以找出两个整数中的最大值:
#include <stdio.h>
//get_max函数的设计//get_max就是一个自定义函数,在主函数之前声明,交代函数的功能,下面才能使用
int get_max(int x, int y) //x,y形参//int是返回值最大值类型//如果不需要返回就用void
{
return (x>y)?(x):(y);//需要返回给最大值//并未改变x,y的值,所以是传值调用
}
int main()
{
int num1 = 10;
int num2 = 20;
int max = get_max(num1, num2);//num1,num2实参传给对应的x,y
printf("max = %d\n", max);
return 0;
}
再举个例子
#include <stdio.h>
//实现成函数,但是不能完成任务
void Swap1(int x, int y)
{
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
//正确的版本
void Swap2(int *px, int *py)
{
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main()
{
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);
printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
Swap2(&num1, &num2);
printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
return 0;
}
三. 函数的参数
1 实际参数(实参):
在主函数main中,真实传给函数的参数,叫实参,在{}中。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
2 形式参数(形参):
在函数的定义中,不在main函数中,形式参数是指函数名后括号()中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
四-函数的调用:
如果想要修改实参,那么就是传址调用;不修改实参就是传值调用
1 传值调用:
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。只是单纯的把值传过去!
2 传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量,通过修改形参改变实参。
用一个实例解析更容易懂,我把解析放在了前面的获得最大值题上
五. 函数的嵌套调用和链式访问
1 嵌套调用:
函数可以嵌套调用,但是不能嵌套定义。
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()//void返回类型不需要返回return,就是简单执行一个功能
{
int i = 0;
for(i=0; i<3; i++)
{
new_line();
}
}
}
int main()//主函数//打印出3次hehe
{
three_line();
return 0;
}
2 链式访问:
把一个函数的返回值作为另外一个函数的参数
#include <stdio.h>
#include <string.h>//库函数要引用的头文件
int main()
{
char arr[20] = "hello";
int ret = strlen(strcat(arr,"bit"));//这里介绍一下strlen库函数,求字符串长度
printf("%d\n", ret);
return 0;
}
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));//4321
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
六. 函数的声明和定义
void类型说明函数不需要返回值,就是执行功能就行
1 函数声明:
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数决定不了。
- 函数的声明一般出现在函数的使用之前。要满足先声明后使用。
- 函数的声明一般放在头文件中,.h文件中。
定义在前,就不用声明,定义在后就需要声明。
//函数的声明
int Add(int x, int y);//放在头文件中,text.h
2 函数定义:
函数的定义是指函数的具体实现,交待函数的功能实现。
/函数Add的实现
#include "test.h"//引入头文件
int Add(int x, int y)
{
return x+y;
}
七. 函数递归
1 什么是递归?
2 递归的两个必要条件
- 存在限制条件,当满足这个限制条件的时候,递就完成了接下来就开始归,后便不再继续
- 每次递归调用之后越来越接近这个限制条件。
练习1:(画图理解)
int main()
{
unsigned int num = 0;
scanf("%u", &num);//1234//%u无符号整型占位符
while (num)
{
printf("%d ", num % 10);
num = num / 10;
}
//原理
//1234%10 = 4
//1234/10=123
//123%10=3
//123/10=12
//12%10=2
//12/10=1
//1%10=1
//1/10=0
return 0;
}
1234
4 3 2 1
D:\2022code\1\start\Debug\start.exe (进程 2000)已退出,代码为 0。
按任意键关闭此窗口. . .
//print(1234)
//print(123) 4
//print(12) 3 4
//print(1) 2 3 4
//1 2 3 4
//void print(unsigned int n)//1234
//{
// if (n < 10)
// printf("%d ", n);
// else
// {
// print(n / 10);//123
// printf("%d ", n%10);
// }
//}
void print(unsigned int n)
{
if (n > 9)
{
print(n / 10);
}
printf("%d ", n % 10);
}
int main()
{
unsigned int num = 0;
scanf("%u", &num);//1234
print(num);//按照顺序打印num的每一位
return 0;
}
4 递归与迭代
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。 重复执行一系列运算步骤,从前面的量依次求出后面的量的过程。此过程的每一次结果,都是由对前一次所得结果施行相同的运算步骤得到的
- 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
- 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
- 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。