C语言函数的递归调用

C语言函数的递归调用

简单的求和函数

  1. 定义一个函数求1到n的和,马上就想到用循环累加,这里用for循环,名称为sumf。
  2. 参数和返回值都采用无符号整型,防止累加的和超出范围,出现意外情况!!!
  3. 将无符号整型自定义为uint,其实就是一种重命名,加强代码的可读性。
    代码如下:
/* filename: sum.c */
#include <stdio.h>

/* compile : gcc sum.c -o sum
       run : ./sum            */

/**/
typedef unsigned int uint;

/**/
uint
sumf (uint n)
{
  uint tmp = 0;
  for (uint i = 1; i <= n; i++)
    tmp = tmp + i;
  return tmp;
}

/**/
void
test_sum (void)
{
  printf ("Sum 1 -   100: %u\n", sumf(100));
  printf ("Sum 1 - 10000: %u\n", sumf(10000));
}

/**/
int
main (int argc, char *argv[])
{
  test_sum ();
  return 0;
}
/* --(:-O-:)-- */

编译运行,结果在意料之中:

songvm@ubuntu:~/works/xdn$ gcc sum.c -o sum
songvm@ubuntu:~/works/xdn$ ./sum
Sum 1 -   100: 5050
Sum 1 - 10000: 50005000

将参数10000改为100000,编译运行,结果如下:

songvm@ubuntu:~/works/xdn$ gcc sum.c -o sum
songvm@ubuntu:~/works/xdn$ ./sum
Sum 1 -    100: 5050
Sum 1 - 100000: 705082704

明显是结果超出表示范围了,解决办不将无符号整型改为无符号长整型!

  1. 自定义无符号长整型ulong
  2. 将printf输出中的转意符改为%lu
    代码如下:
/* filename: sum.c */
#include <stdio.h>

/* compile : gcc sum.c -o sum
       run : ./sum            */

/**/
typedef unsigned long ulong;

/**/
ulong
sumf (ulong n)
{
  ulong tmp = 0;
  for (ulong i = 1; i <= n; i++)
    tmp = tmp + i;
  return tmp;
}

/**/
void
test_sum (void)
{
  printf ("Sum 1 -    100: %lu\n", sumf(100));
  printf ("Sum 1 - 100000: %lu\n", sumf(100000));
}

/**/
int
main (int argc, char *argv[])
{
  test_sum ();
  return 0;
}
/* --(:-O-:)-- */

编译运行,结果如预期:

songvm@ubuntu:~/works/xdn$ gcc sum.c -o sum
songvm@ubuntu:~/works/xdn$ ./sum
Sum 1 -    100: 5050
Sum 1 - 100000: 5000050000

用递归的方法写一个求和函数

  1. 参数还可以更大,这里就不做极限测试了,但要考虑到超出取值范围这种情况的出现!!!
  2. 不用循环也可以实现求和,就是用递归的方法!!!
  3. 说白了,递归就是函数调用自己本身,sum中再次调用sum,执行下去就会无限循环,所以一定要设置一个条件让函数跳出执行,返回一个值,就是当n等于1时返回1;
  4. 当再次调用sum时,参数n都减1,直到n等于1返回,这样就达到了循环的效果。
    代码如下:
/* filename: sum.c */
#include <stdio.h>

/* compile : gcc sum.c -o sum
       run : ./sum            */

/**/
typedef unsigned long ulong;

/**/
ulong
sumf (ulong n)
{
  ulong tmp = 0;
  for (ulong i = 1; i <= n; i++)
    tmp = tmp + i;
  return tmp;
}

/**/
ulong
sum (ulong n)
{
  if (n == 1) return 1;
  return n + sum (n - 1);
}

/**/
void
test_sum (void)
{
  printf ("Use for method \n");
  printf ("Sum 1 -    100: %lu\n", sumf(100));
  printf ("Sum 1 - 100000: %lu\n", sumf(100000));
  printf ("--------------------\n");
  printf ("Use recursion method \n");
  printf ("Sum 1 -    100: %lu\n", sum(100));
  printf ("Sum 1 - 100000: %lu\n", sum(100000));
  printf ("--------------------\n");
}

/**/
int
main (int argc, char *argv[])
{
  test_sum ();
  return 0;
}
/* --(:-O-:)-- */

编译运行,结果如下:

songvm@ubuntu:~/works/xdn$ gcc sum.c -o sum
songvm@ubuntu:~/works/xdn$ ./sum
Use for method 
Sum 1 -    100: 5050
Sum 1 - 100000: 5000050000
--------------------
Use recursion method 
Sum 1 -    100: 5050
Sum 1 - 100000: 5000050000
--------------------

结果一样,方法不同

  1. 理解递归对编写函数代码来说非常重要,尤其是在数据结构和算法方面!!!
  2. 上面的sum函数使用了简单的递归调用,可以看出参数n越大,返回的表达式就越长,占用的程序运行资源就越多,对于递归次数较少的的应用来说是可行的。
  3. 如何改变表达式越来越长的这种情况呢,就是采用尾递归调用,使表达式的长度不变!!!
  4. 定义函数sumt,除原有参数n外,再加一个参数s,来保存累加结果,这个累加结果初始值定为1,当n等于1时直接返回s,否则每次调用s都要加n来继续累加,n则减1继续调用。
    代码如下:
/* filename: sum.c */
#include <stdio.h>

/* compile : gcc sum.c -o sum
       run : ./sum            */

/**/
typedef unsigned long ulong;

/**/
ulong
sumf (ulong n)
{
  ulong tmp = 0;
  for (ulong i = 1; i <= n; i++)
    tmp = tmp + i;
  return tmp;
}

/**/
ulong
sum (ulong n)
{
  if (n == 1) return 1;
  return n + sum (n - 1);
}

/**/
ulong
sumt (ulong n, ulong s)
{
  if (n == 1) return s;
  return sumt (n - 1, s + n);
}

/**/
void
test_sum (void)
{
  printf ("Use for method \n");
  printf ("Sum 1 -    100: %lu\n", sumf(100));
  printf ("Sum 1 - 100000: %lu\n", sumf(100000));
  printf ("--------------------\n");
  printf ("Use recursion method \n");
  printf ("Sum 1 -    100: %lu\n", sum(100));
  printf ("Sum 1 - 100000: %lu\n", sum(100000));
  printf ("--------------------\n");
  printf ("Use tail recursion method \n");
  printf ("Sum 1 -    100: %lu\n", sumt(100, 1));
  printf ("Sum 1 - 100000: %lu\n", sumt(100000, 1));
  printf ("--------------------\n");
}

/**/
int
main (int argc, char *argv[])
{
  test_sum ();
  return 0;
}
/* --(:-O-:)-- */

编译运行,结果如预期:

songvm@ubuntu:~/works/xdn$ gcc sum.c -o sum
songvm@ubuntu:~/works/xdn$ ./sum
Use for method 
Sum 1 -    100: 5050
Sum 1 - 100000: 5000050000
--------------------
Use recursion method 
Sum 1 -    100: 5050
Sum 1 - 100000: 5000050000
--------------------
Use tail recursion method 
Sum 1 -    100: 5050
Sum 1 - 100000: 5000050000
--------------------

菲波那契数列

  1. 数列中从第三项开始,每一项都是前两项之和。
  2. 即F(n) = F(n-1) + F(n-2),其中n >= 2
  3. 此处以1和1作为初始值: 1 1 2 3 5 8 13 21 …
  4. 定义递归函数fib和上面的sum雷同,一个参数n,直接返回fib(n-1)与fib(n-2)的和。
  5. 定义尾递归函数fibt,在fib的基础上加三个参数,rsa对应n-1的值,rsb对应n-2的值,n不变,idx每次加1,rsb赋值给rsa,rsb赋值rsa与rsb的和(即fib(n-1)+fib(n-2)),当idx等于n时,返回rsb的值。

代码如下:

/* filename: fib.c */
#include <stdio.h>

/* compile : gcc fib.c -o fib
       run : ./fib           */

/**/
typedef unsigned int  uint;
typedef unsigned long ulong;

/* recursion call */
ulong
fib (ulong n)
{
  if (n == 1 || n == 2) return 1;
  return fib (n - 1) + fib (n - 2);
}

/* tail recursion call */
ulong
fibt (ulong n, ulong rsa, ulong rsb, ulong idx)
{
  if (n == 1 || n == 2) return 1;
  if (n == idx) return rsb;
  return fibt (n, rsb, rsa + rsb, idx + 1);
}

/**/
void
test_fib (void)
{
  for (int i = 1; i < 20; i++)
    printf ("%lu ", fib (i));
  printf ("\n--------------------\n");

  for (int i = 1; i < 20; i++)
    printf ("%lu ", fibt (i, 1, 1, 2));
  printf ("\n--------------------\n");
}

/**/
int
main (int argc, char *argv[])
{
  test_fib ();
  return 0;
}
/* --(:-O-:)-- */

编译运行,菲波那契数列前20项结果如下:

songvm@ubuntu:~/works/xdn$ gcc fib.c -o fib
songvm@ubuntu:~/works/xdn$ ./fib
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 
--------------------
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 
--------------------

能看出尾递归还是有效率的

  1. 因为fib函数当n大于2时每次递归调用时都调用2次,而fibt函数每次递归调用只调用一次!!!
  2. 可以加个计数变量看一下都调用了多少次!!!
  3. 在函数外加一个静态变量count,初始值赋为0,在函数的第一行令count自增,这样在函数调用结束后输出count,查看函数调用次数。
  4. 再次计数时别忘了重置count为0!!!
    代码如下:
/* filename: fib.c */
#include <stdio.h>

/* compile : gcc fib.c -o fib
       run : ./fib           */

/**/
typedef unsigned int  uint;
typedef unsigned long ulong;

/* define counter */
static int count = 0;

/* recursion call */
ulong
fib (ulong n)
{
  count++;
  if (n == 1 || n == 2) return 1;
  return fib (n - 1) + fib (n - 2);
}

/* tail recursion call */
ulong
fibt (ulong n, ulong rsa, ulong rsb, ulong idx)
{
  count++;
  if (n == 1 || n == 2) return 1;
  if (n == idx) return rsb;
  return fibt (n, rsb, rsa + rsb, idx + 1);
}

/**/
void
test_fib (void)
{
  for (int i = 1; i < 20; i++)
    printf ("%lu ", fib (i));
  printf ("\nCount: %d\n--------------------\n", count);

  count = 0;
  for (int i = 1; i < 20; i++)
    printf ("%lu ", fibt (i, 1, 1, 2));
  printf ("\nCount: %d\n--------------------\n", count);
}

/**/
int
main (int argc, char *argv[])
{
  test_fib ();
  return 0;
}
/* --(:-O-:)-- */

编译运行,结果如下:

songvm@ubuntu:~/works/xdn$ gcc fib.c -o fib
songvm@ubuntu:~/works/xdn$ ./fib
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 
Count: 21871
--------------------
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 
Count: 172
--------------------

仅求前20项,fib函数就调用了21871次,而fibt只调用了172次,尾递归效率之高,出乎意料之外!!!

字符串输出函数

常用printf来输出字符串,putchar只能输出单个字符,可以考虑设计函数来递归调用通过putchar输出字符串(当然循环也可以)。

  1. 字符串以’\0’结尾,设计一个函数out_charactor递归调用,参数为字符指针,当遇到’\0’时输出换行符并退出,否则输出当前字符。
  2. 做为参数的字符指针每次自增,指向下一个字符,继续调用out_charactor。
  3. 写一个测试函数,定义两个字符串,一个是简单的hello world!,另一个是全部为0的字符数组。
  4. 向数组循环填入26个大写字母,填充10次。
  5. 最后分别输出这两个字符串!!!
    代码如下:
/* filename: outchar.c */
#include <stdio.h>

/* recursion call */
void
out_charactor (char *pc)
{
  if (*pc == '\0')
    {
      putchar ('\n');
      return;
    }
  else
    {
      putchar (*pc);
      pc++;
      out_charactor (pc);
    }
}

/**/
void
test_out_charactor (void)
{
  char *str = "Hello world!";
  char buf[261] = { 0 };

  for (int i = 0; i < 10; i++)
    for (int j = 0; j < 26; j++)
      buf[i*26 + j] = 'A' + j;

  out_charactor (str);
  out_charactor (buf);
}

/**/
int
main (int argc, char *argv[])
{
  test_out_charactor ();
  return 0;
}
/* --(:-)-- */

编译运行,结果如下:

songvm@ubuntu:~/works/xdn$ gcc -g outchar.c -o outchar
songvm@ubuntu:~/works/xdn$ ./outchar
Hello world!
ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ

怎么样,会玩递归了吧?下一步研究一下求质数的方法!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值