c语言函数递归调用

函数的递归调用

在调用一个函数的过程中又出现直接或间接地调用该函数本身,这种用法称为函数的递归调用。

例如:

int f ( int x )

{int x,z;

z=f( x );

//在执行f函数的过程中又要调用f函数

return (2+z);}

在调用函数f的过程中,又要调用f函数(本函数),这是直接调用本函数。

如果在调用f1函数过程中要调用f2函数,又在调用f2的数过程中又要调用 f1,这就是间接调用本函数。

这两种递归调用都是无终正的自身调用,程序中不应出现这种无终止的递归调用,只应出现有限次数的、有终止的递归调用,用 if语句来控制,只有在某一条件成立时才继续执行递归调用:否则就不再继续。如n=1;c=10,没有条件一直调用,有条件把递归调用变已知值,无调用函数,消失了。

例:有5个学生坐在一起,问第5个学生多少岁,他说比第4个学生大2岁,问第4个学生岁数,他说比第3个学生大2岁。问第3个学生,又说比第2个学生大2岁,问第2个学生,说比第1个学生大2岁。最后问第1个学生,他说是10岁。请问第5个学生多大。

每一个学生的年龄都比其前1个学生的年龄大2。说明共用一个函数关系。

可以用数学公式表述如下:

age ( n )=10 ( n =1)

age ( n )= age ( n -1)+2 ( n >1)

当 n > 1时,不断调用同一个函数,就是一个递归问题。

回溯将第5个学生的年龄表示直到第1个学生的年龄。

此时 age (1)已知等于10,没有可调用函数,出现已知值,不再出现调用。

从第1个学生的已知年龄推算出第2个学生的年龄(12岁),一直推算出第5个学生的年龄18岁为止。

如果要求递归过程不是无限制进行下去,必须具有一个结束递归过程的条件。就是要出现已知值,不再调用下去。

如: age (1)=10,就是使递归结束的条件,出现已知值,不再调用了,也就终止递归了。

编写程序:用一个函数来描述上述递归过程:

int age ( int n )

//求年龄的递归函数,内有调用自身的函数。

{int c;

if ( n ==1) c=10

∥这里c=age(1),并不等于age(5)

else

c=age (n-1)+2;

return(c );

// c 用作存放函数的返回值的变量

用一个主函数调用 age 函数,求得第5个学生的年龄。

程序如下:

# include < stdio.h>

int main () ∥函数名不用分号,因为函变量名与大括号实体内容是一体,不可分离,不用分号,内部也是连接的,不用逗号。

{int age ( int n ); //对 age 函数的声明,临时变量只用主函数对调用函数变量定义类型非void非任意类型

printf (\” No.5.age:% d \\n\”, age (5));

//输出第5个学生的年龄

return 0;}

int age ( int n ) //自定义递归函数,给调用时创造空间,调用结束时立即释放空间,依靠主函数定义的临时变量存放空间。

{ int c; if ( n ==1) c=10;

//如果 n 等于1,年龄为10,控制递归调用进度,当递归函数推进n=1时,停止。

else

//如果 n 不等于1

//年龄是前一个学生的年龄加2

c=age (n-1)+2;

return(c);}

main 函数中age 函数共被调用5次,即 age (5)、 age (4)、 age (3)、 age (2)、age(1)。其中 age (5)是 main 函数调用的,其余4次是在 age 函数中调用自己的,即递归调用4次。

在某一次调用 age 函数时并不是立即得到 age ( n )的确定值,而是一次又一次地进行递归调用,到 age (1)时才有确定的值,然后再递推出 age (2)、 age (3)、 age (4)、 age (5)。

age(1)=10作为递归的终止条件,使递归消失。

当 n 等于2时,应执行“ c= age ( n -1)+2;”,由于 n =2.它相当于“ c = age (1)+2;”。注意 age (1)的值,此时 n =1,应执行“ c =10=age(1)”,即不再递归调用 age 函数了,没有调用自身的函数了,递归调用也就结束。

依此类推,可以得到 age (5)值为18。

用递归方法求 n !。

求 n !可以用递推方法,递推法的特点是从一个已知的事实(如1!=1)出发,按一定规律推出下一个事实(如2!= 1!*2),再从这个新的已知的事实出发,再向下推出一个新的事实(3!=3*2!)。 n != n *( n -1)!

求 n !也可以用递归方法,即5!等于4!*5,而4=3!*4,…1!=1。可用下面的递归公式表示:

编写程序:

# include < stdio . h >

int main ()

{ int fac ( int n );

∥fac函数声明

int n;int y;

printf (\” input a integer number:\” ) ;

scanf (\” %d\”,&n);//这里n可改变,不固定值,将程序变为通用程序,调用参数n任意整数。

y = fac( n );调用n变量空间值

printf (\” %d!=%d\\n\”, n,y );//分取提取n值为n!,提取y值,变为y=f(n),执行调用,调用函数具有递归功能。

int fac(int n ) //自定义调用函数及参变量

int f;

if(n<0)

printf (\” n <0,data error \”);

else if (n==0 n ==1)

f=1;

else f=fac(n-1)*n;

return(f);}

每次调用 fac 函数后,其返回值应返回到调用 fac 函数处。例如当 n =2时,从函数体中可以看到“ f = fac (1)*2”,再调用 fac (1),返回值为1。这个1就取代了“ f = fac (1)x2”中的 fac (1).从而f=fac(2)=2。递归终止条件为 n =0或n=1。到n=2时能求出f(1),为什么还出现n=0,因为当调用f(1)直接等于1,n<0时还包括n=0,直接调用f(0)等于1。

c语言

C语言学习之—–(十)  函数的定义和调用

(十) 函数的定义和调用

本节介绍C程序的基本单元–函数。函数中包含了程序的可执行代码。每个C程序的入口和出口都位于函数main()之中。main()函数可以调用其他函数,这些函数执行完毕后程序的控制又返回到main()函数中,main()函数不能被别的函数所调用。通常我们把这些被调用的函数称为下层(lower-level)函数。函数调用发生时,立即执行被调用的函数,而调用者则进入等待状态,直到被调用函数执行完毕。函数可以有参数和返回值。

程序员一般把函数当作“黑箱”处理,并不关心它内部的实现细节。当然程序员也可以自己开发函数库。

说明一点,函数这一节很重要,可以说一个程序的优劣集中体现在函数上。如果函数使用的恰当,可以让程序看起来有条理,容易看懂。如果函数使用的乱七八糟,或者是没有使用函数,程序就会显得很乱,不仅让别人无法查看,就连自己也容易晕头转向。可以这样说,如果超过100行的程序中没有使用函数,那么这个程序一定很罗嗦(有些绝对,但也是事实)。

一、函数的定义

一个函数包括函数头和语句体两部分。

函数头由下列三部分组成:

函数返回值类型

函数名

参数表

一个完整的函数应该是这样的:

函数返回值类型 函数名(参数表)

{

语句体;

}

函数返回值类型可以是前面说到的某个数据类型、或者是某个数据类型的指针、指向结构的指针、指向数组的指针。指针概念到以后再介绍。

函数名在程序中必须是唯一的,它也遵循标识符命名规则。

参数表可以没有也可以有多个,在函数调用的时候,实际参数将被拷贝到这些变量中。语句体包括局部变量的声明和可执行代码。

我们在前面其实已经接触过函数了,如abs(),sqrt(),我们并不知道它的内部是什么,我们只要会使用它即可。

这一节主要讲解无参数无返回值的函数调用。

二、函数的声明和调用

为了调用一个函数,必须事先声明该函数的返回值类型和参数类型,这和使用变量的道理是一样的(有一种可以例外,就是函数的定义在调用之前,下面再讲述)。

看一个简单的例子:

void a(); /*函数声明*/

main()

{

a(); /*函数调用*/

}

void a() /*函数定义*/

{

int num;

scanf(%d,#);

printf(%d\\n,num);

}

在main()的前面声明了一个函数,函数类型是void型,函数名为a,无参数。然后在main()函数里面调用这个函数,该函数的作用很简单,就是输入一个整数然后再显示它。在调用函数之前声明了该函数其实它和下面这个程序的功能是一样的:

main()

{

int num;

scanf(%d,#);

printf(%d\\n,num);

}

可以看出,实际上就是把a()函数里面的所有内容直接搬到main()函数里面(注意,这句话不是绝对的。)

我们前面已经说了,当定义在调用之前时,可以不声明函数。所以上面的程序和下面这个也是等价的:

void a()

{

int num;

scanf(%d,#);

printf(%d\\n,num);

}

main()

{

a();

}

因为定义在调用之前,所以可以不声明函数,这是因为编译器在编译的时候,已经发现a是一个函数名,是无返回值类型无参数的函数了。

那么很多人也许就会想,那我们何必还要声明这一步呢?我们只要把所有的函数的定义都放在前面不就可以了吗?这种想法是不可取的,一个好的程序员总是在程序的开头声明所有用到的函数和变量,这是为了以后好检查。

前面说了,在调用之前,必须先声明函数,所以下面的做法也是正确的(但在这里我个人并不提倡)。

main()

{

void a();

a();

}

void a()

{

int num;

scanf(%d,#);

printf(%d\\n,num);

}

一般来说,比较好的程序书写顺序是,先声明函数,然后写主函数,然后再写那些自定义的函数。

既然main()函数可以调用别的函数,那么我们自己定义的函数能不能再调用其他函数呢?答案是可以的。看下面的例子:

void a();

void b();

main()

{

a();

}

void a()

{

b();

}

void b()

{

int num;

scanf(%d,#);

printf(%d\\n,num);

}

main()函数先调用a()函数,而a()函数又调用b()函数。在C语言里,对调用函数的层数没有严格的限制,我们可以往下调用100层、1000层,但是在这里我们并不提倡调用的层数太多(除非是递归),因为层数太多,对以后的检查有一些干扰,函数调过来调过去,容易让自己都晕头转向。

某些人可能就不明白了,看上面的例子,好像使用函数后,程序变的更长了,更不让人理解。当然,我举的这个例子的确没有必要用函数来实现,但是对于某些实际问题,如果不使用函数,会让程序变的很乱,这涉及到参数问题,我们下一节再说。

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。