病来如山倒,病去如抽丝。
从感到不适,到去医院检查,再开始治疗,中间耗时颇长。现在继续学习进程。
回顾前8次的学习中,我们从C语言的基础知识开始着手,并逐渐接触了部分基本语法知识。
Day 7和 Day 8 的学习中,主要学习的 函数
这样稍有难度的部分,今天我们将继续补充 函数
的知识点。
函数
:
递归
函数我们需要用到自定义的函数
的时候,就得调用它,那么在调用的时候就称之为函数调用。
在C语言中,函数调用的一般形式为:
函数名([参数]);
注意
:
[]包含的部分
省略。[]
中可以是常数,变量或其它构造类型数据及表达式,多个参数之间用,
分隔。函数的形参和实参分别占有不同内存块,
对形参的修改不会影响实参
。
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。
我们以交换两个变量的值的代码示例来给大家进行举例讲解传值调用与传址调用区别。
传值调用:
#include <stdio.h>
void swap(int x, int y) //形参
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int num1 = 10;
int num2 = 20;
swap(num1, num2); //实参
printf("num1 = %d\nnum2 = %d", num1,num2);
return 0;
}
代码运行结果:
可见在传值调用并未影响实参
main
调用swap
时候,只是把num1和num2的值拷贝给x和y,然后num1和num2就不再和swap有关了,swap交换的也是x和y的值因为在传值调用的过程中实参和形参有不同的内存块,修改形参不会影响实参;即形参是实参的临时拷贝。
而要想将num1和num2交换则需要引用调用。
传址调用:
#include <stdio.h>
void swap(int *x, int *y) //形参
{
int temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int num1 = 10;
int num2 = 20;
swap(&num1, &num2); //实参
printf("num1 = %d\nnum2 = %d", num1,num2);
return 0;
}
函数参数完成了交换,原因是
指针
(即地址),而不是要交换的两个数本身,虽然形参在swap结束后被销毁,但是形参是根据要交换的两个数的地址完成交换的,所以对这两个数字产生影响,也就完成交换。函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
示例,
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_line()
{
int i = 0;
for (i = 0; i < 3; i++)
{
new_line();
}
}
int main()
{
three_line();
return 0;
}
在上述代码中,在main()
函数中调用了three()
函数,而在three()函数中又调用了new_line()
函数,这就是嵌套调用,即在一个函数中调用另一个函数。
特别注意:C语言中,函数可以嵌套调用,但是不能嵌套定义!
什么是链式访问?
通俗来讲,就是把一个函数的返回值作为另外一个函数的参数。
貌似与嵌套调用挺像的,但两者是存在区别的:
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
//结果是啥?
//注:printf函数的返回值是打印在屏幕上字符的个数
return 0;
}
打印结果是什么呢?
最后的打印结果是4321。
为什么呢?此处给大家普及一个知识点:
printf()
函数的返回值是正确输出在屏幕上的字符数
例如,输出在屏幕上的数字是43,此时函数的返回值就是2,如果打印在屏幕上的数字是2的话,返回值就是1,那么此处输出的结果也就不难理解了。
另外一个给大家补充的点是:在上述函数中,是怎样的执行流程呢?
printf()
函数,
函数体
内调用它自身称为递归( recursion)示例:
void function(int x)
{
function(x);
}
注意:递归函数必须有结束条件
递归的主要思考方式在于:把大事化小
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
5的阶乘
这个例子进行一下剖析,看一看他的运算过程:
程序在计算5的阶乘的时候,先执行递推,当n=1或者n=0的时候返回1,再回推将计算并返回。
由此可以看出,递归函数必须有结束条件。
递归函数特点:
一句话总结递归:自我调用且有完成状态
任务
猴子第一天摘下N个桃子,当时就吃了一半,还不过瘾,就又多吃了一个。第二天又将剩下的桃子吃掉一半,又多吃了一个。以后每天都吃前一天剩下的一半零一个。到第10天在想吃的时候就剩一个桃子了,问第一天共摘下来多少个桃子?并反向打印每天所剩桃子数。
#include <studio.h>
int getPeachNumber(int n)
{
int num;
if(n==10)
{
return 1;
}
else
{
num = (getPeachNumber(n+1)+1)*2;
printf("第%d天所剩桃子%d个",n,num);
}
return num;
}
int main()
{
int num = getPeachNumber(1);
printf("猴子第一天摘了%d个桃子。\n",num);
return 0;
}
程序分析: 利用递归的方法,递归分为
回推
和递推
两个阶段。
要想知道第1天摘得桃子数,就需知道第2天的,依次类推,推到第10天(1个),再往回推。