C语言允许函数自身调用自身,这种调用就被称为递归。好多人刚开始学习递归的时候,往往被一层层嵌套调用搞糊涂了,搞不清楚到底是怎么调用的?现在就通过一个小例子来演示一下,递归调用时,函数是如何运行的。

void up_and_down(int n)
{printf("---- Level %d: n location 0x%p \r\n",n,&n);if(n<4)up_and_down(n + 1);printf("**** Level %d: n location 0x%p \r\n",n,&n);
}
int main()
{up_and_down(1);system("pause");return 0;
}

首先调用函数给n赋值为1,然后当n小于4时,给n加1,然后继续调用这个函数。直到n的值大于4,就退出函数,运行结果如下:


从打印结果可以看出n的值为1、2、3、4依次递增,然后又从4、3、2、1依次递减,最后退出程序,那么这个程序执行的流程是什么呢?下面用一张图来分析一下。

下面一步一步来分析:


首先n=1,执行函数体内的第一条打印语句。然后执行if语句,由于n当前值为1,小于4,所以又调用函数本身,将1+1的值传递给函数。


接下来执行函数体内的第一条打印语句。然后执行if语句,由于n当前值为2,小于4,所以又调用函数本身,将2+1的值传递给函数。


继续执行函数体内的第一条打印语句。然后执行if语句,由于n当前值为3,小于4,所以又调用函数本身,将3+1的值传递给函数。


继续执行函数体内的第一条打印语句。然后执行if语句,由于n当前值为4,不小于4,所以if条件不成立,不会再调用up_and_down()函数了,而是执行函数体内最后一条打印语句。当这条打印语句执行完之后,退出函数。 由于第4次条用函数,是从第三次调用函数中来的,所以退出第四次函数后,程序就会回到第三次函数调用它的地方。

第四次函数退出后,相当于就返回到了第三次函数的up_and_down(3 + 1);这个位置,由于这个函数已经执行完了,所以代码继续执行,打印最后一行的打印语句。

同样当第三次调用的函数执行完成之后,程序就会返回到调用的位置,也就是返回到第二次调用的函数中。

程序返回到up_and_down(2 + 1);这个语句之后,然后继续执行最后一行的打印语句。当最后一行的打印语句执行完成之后,第二次调用的函数就会返回到调用它的地方,也就是返回到第一次调用函数的内部。

程序返回到up_and_down(1 + 1);这个语句之后,然后继续执行最后一行的打印语句。当最后一行的打印语句执行完成之后,程序返回到第一次调用这个函数的地方。而第一次调用这个函数是在main()函数中,所以程序就会退回到main()函数中。由于main()函数中没有执行其他语句。所以程序整个流程就运行结束了。

通过打印的结果也可以看出,首先打印的都是函数体内第一个行的打印语句,当4次函数调用都完成后,才继续打印函数体中的最后一行打印语句。通过n的地址也可以看出来,每次新调用函数的时候,都会给n重新申请一个地址,然后当函数退出的时候,再将申请的地址释放掉。

由此可见递归函数在执行的时候,还是比较浪费内存空间的,对于资源很有限的单片机来说,还是尽量少用递归函数。难道递归函数真的一点优势都没有吗?当然是有的,通过对于上面的例子观察可以发现,递归函数执行时是先进后出的原则,那么对于需要倒叙执行的程序来说,使用递归函数是非常方便的。
比如在进行进制转换的时候,第一次计算的值要放到结果的最后一位,最后一次计算的值要放在结果的第一位。

比如现在要将10转换为二进制数。计算过程如下:

10%2 ---- 0

10/2=5

5%2 ----- 1

5/2=2

2%2 ----- 0

2/2=1

1%2 ----- 1

首先将10除以2取余数,余数为0,那么这个0就是二转进制的最低位。接下来将10除以2去整,然后再用结果除以2取余,这个就是二进制的倒数第二位,安装这样的规律,依次计算,直到除数结果为1.

也就是传递的参数变为1时,结束递归的调用。 这样10转换为二进制就时 1010,这个数字刚好是计算的数字倒叙排列。

下面就通过递归来实现这个功能

void to_binary(unsigned long  n)
{int r;r = n % 2;if(n >= 2)to_binary(n/2);putchar(r == 0?'0':'1');return;
}
int main()
{to_binary(100);printf("\r\n");system("pause");return 0;
}

定义一个函数来打印二进制的值,首先用这个数字除以2取余,然后将数字除以2取整之后继续调用此函数,运行结果如下:

将100转换为二进制值

程序打印的结果和实际计算的结果一样,说明程序功能是正常的。为了方便查看,可以将函数执行过程打印出来。

为了方便查看函数执行效率,这里可以将函数执行的时间打印出来。
测试代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
void to_binary(unsigned long  n)
{int r;r = n % 2;printf("n-%d,r-%d\r\n",n,r);if(n >= 2)to_binary(n/2);putchar(r == 0?'0':'1');return;
}
typedef union _LARGE_INTEGER
{struct{long LowPart ;// 4字节整型数long  HighPart;// 4字节整型数};long long QuadPart;// 8字节整型数} LARGE_INTEGER; int main(int argc, char *argv[])
{int i = 0,val = 0;clock_t startTime,endTime;int time = 0;LARGE_INTEGER secondcount= {0};LARGE_INTEGER startcount= {0};LARGE_INTEGER stopcount= {0};QueryPerformanceFrequency(&secondcount);     //获取每秒多少CPU Performance Tick  单位us //printf(" 3:系统计数频率为: %d \r\n",secondcount.QuadPart);QueryPerformanceCounter(&startcount);      //计时开始to_binary(100);                   //递归调用 QueryPerformanceCounter(&stopcount);     //计时结束time=( ((stopcount.QuadPart - startcount.QuadPart)*1000*1000)/secondcount.QuadPart);printf(" \r\n\r\n  程序运行时间为: %d us\r\n\r\n",time);system("pause");return 0;
}

这里添加了一个测试递归调用函数执行时间,测试的原理就是,在函数执行前记录一下系统的计数值,然后执行完成之后,在记录一下系统计数值,这两个值的差就是系统的执行时间。
执行结果如下:


可以看到计算一次100的二进制数需要1.163ms,这对于计算机来说太慢了。如果计算一个更大的数,那岂不是更费时间,用数字100000000测试一下。

花费的时间为5.1ms左右,可见数字越大函数嵌套的就越深,就会越费内存。执行时间也就越久。

以后再使用递归函数的时候,一定要注意使用环境,否则如果在一个资源比较少的单片机上,调用了一个嵌套比较深的递归函数,很可能计算到一半系统资源就会被耗尽,导致程序崩溃,而且很难查找到原因。

C语言小知识---递归函数的使用相关推荐

  1. 纸上得来终觉浅(c语言小知识总结)

    纸上得来终觉浅(c语言小知识总结) 1.数组的初始定义 对于一个初始定义的数组,内部的值是随机的,若用{}(哪怕其中没有元素)也会让数组内元素初始化,默认为0. 若是用循环语句进行赋值,在一个长度为2 ...

  2. eem二级c语言题库哪种比较好,c语言小知识,供初学者参考

    1 用预处理指令 define 声明一个常数 用以表明 1 年中有多少秒 忽略闰年问题 define SECONDS PER YEAR 60 60 24 365 UL 2 写一个 标准 宏 MIN 这 ...

  3. C语言小知识:typedef\函数模板\

    (1)typedef用法: typedef为C语言的关键字,作用是为一种数据类型定义一个新名字.这里的数据类型包括内部数据类型(int,char等)和自定义的数据类型(struct等).在编程中使用t ...

  4. C语言小知识---为什么要使用指针

      刚开始学习C语言的时候,感觉最难理解的就是指针,什么指针变量,变量指针,指向指针的变量,指向变量的指针?一堆概念,搞得人云里雾里的,今天不讨论这些概念的问题,从最底层来分析C语言中为什么要使用指针 ...

  5. C语言小知识---特殊的字符串

      字符串在程序中很常见,和整数.小数.字符其他数据类型一样,是代码中必不可少的一部分.可是字符串却又和其他类型不完全一样,还有自己的个性.   下面直接通过一段代码来看. 定义了一个字符1.一个字符 ...

  6. C语言小知识---奇葩的小数

      看到这个标题,好多人可能会想,小数有什么奇葩的,这不和整数一样,加减乘除计算起来也没啥区别呀?奇葩在哪里?   下面通过一个简单的例子来看一看,定义一个小数一个整数,然后打印出来. 核心代码其实只 ...

  7. C语言小知识---printf()函数

      说起printf()函数,写代码的同学肯定都很熟悉,这是C语言中标准的打印函数,在调试代码或者信息输出的时候会经常用到. 其中printf函数的转换说明如下: 转换说明修饰符 printf()中的 ...

  8. C语言小知识---数据类型

      看到这个题目好多人肯定会迷惑,数据类型有什么可说的,这不是编程的基础吗?凡是会写代码的肯定都熟悉数据类型,不就是char,int,float,double这些每天都用成百上千次的的类型吗?各位看官 ...

  9. C语言小知识---printf()函数转换符的意义

      printf()函数大家已经很熟悉了,它的转换符在打印数据的时候也会经常使用,比如%c,%d,%f等.那么为什么打印的时候一定需要转换符呢?系统难道不能自动识别吗?转换符存在的意义又是什么?    ...

最新文章

  1. Win8:Snap 实现
  2. 【中级软考】什么是“敏捷过程的开发方法(敏捷方法agile)“(极限编程XP、特征驱动开发FDD、并列争球法Scrum、水晶法Crystal、开放源码法、自适应软件开发 ASD方法)
  3. 格兰因果模型可以分析哪些东西_如何系统地学习统计学,指导入门数据分析
  4. Oralce EBS Alert
  5. 基于函数的二叉树的描述
  6. 【nginx】关于fastcgi_cache
  7. sigmoid激活函数
  8. 原创 | 一文了解人工智能对精准扶贫的作用及数字乡村建设现状
  9. paypal支付接口开发-Golang版
  10. 【好工具】强烈安利的文献管理软件 Mendeley
  11. 服装系统mysql设计_服装行业ERP系统的设计与实现-店铺模块(SSH,MySQL)(含录像)
  12. Vue学习(学习打卡Day16)
  13. OpenCV切割图片
  14. 如何计算CRC校验码(循环冗余检验码)
  15. VisualSVN server下载安装
  16. 8月9日个人训练小结
  17. linux_设备驱动_设备树
  18. 科大讯飞 唤醒python_用Python来使用科大讯飞语音识别,so easy
  19. 管理-管理:管理百科
  20. Centos7安装jq

热门文章

  1. H264 profile and level
  2. 今天发个技术贴,增加自信心
  3. secucrecrt配置文件导入和保存
  4. 网络工程师考试2005年上半年下午试题解析(一)
  5. 机器学习总结之第一章绪论
  6. Redis在windows下安装说明
  7. asp.net 网站模板怎么用,就是16aspx上面下下来的模板,里面有个sln文件,其他全是文件夹的东西...
  8. 巧用推荐墙入口,APP轻松盈利
  9. AJAX - 创建XMLHttpRequest 对象
  10. 反射--获取当前子类父类的泛型类型