【20180904】【C/C++基础知识】递归函数,函数变量的作用域和存储类型

 

请问:函数可以被其他函数直接或间接的调用,那它可不可以被自己调用?

答:可以!这样的函数叫做“递归函数”。

 

递归函数

递归函数(Recursive Function):即自调用函数,即在函数体内有直接或间接的自己调用自己的语句。

递归函数很特殊,它可能因为自己直接或间接的调用自己,很容易造成死循环。因此我们使用递归函数前,要先写出递归调用的结束条件!递归函数会一直递归调用到结束条件,才开始进行真正的计算,返回回去才得到最终结果。

例1:求n的阶乘。

long fact(int n)
{
    if(n==0)
        return 1;   // 递归结束条件
    else
        return(n*fact(n-1));
}

有递归性质的问题,我们可以用递归函数进行描述,函数简洁易读性强。程序调用时是通过参数和返回值,进行函数递归调用的数据传递的,就涉及到栈的使用、内存的分配和释放、数据的拷贝等,因此效率很低,时间空间消耗较大,因此我们常用非递归函数代替。大多数递归函数都能用非递归函数代替。

例2:求两个整数a, b的最大公约数。

算法:

(a, b)的最大公约数也是(b, a%b)的最大公约数。

证明:

我们将a表示为a=kb+r,那么r=a%b,即r是a除以b的余数。

(1)若D是a和b的公约数,那么D可以整除a和b,而r=a-kb,因此D也可以整除r,所以D也是r的公约数,所以D也是(b, r)的公约数,即D是(b, a%b)的公约数。

(2)若D是(b, a%b)的公约数,则D%b=D%r=0,因为a=kb+r,所以D%a=0,所以D也是(a, b)的公约数。

综上,(a, b)的公约数和(b, a%b)的公约数一样,最大公约数也一样。

递推关系的结束条件:a%b=0。

/* 求a和b的最大公约数 */
long gcd1(int a, int b)   // 递归程序
{
    if(a%b==0)
        return b;
    else
        return gcd1(b,a%b);  // 尾递归
}
long gcd2(int a,int b)   // 非递归程序
{
    int temp;
    while(b!=0)      // 递归结束条件
    {
        temp=a%b;   // a存储原来的b的值,b存放a%b并且进行判断。
        a=b;
        b=temp;
    }
    return a;
}

递归在语句的最后,这种递归叫做“尾递归”,尾递归可以轻松的转换为非递归函数(通过循环语句实现)。只要没有达到结束条件,就会一直执行。

疑问:若a<b???

注意:

(1)使用递归函数的目的是简化程序设计,提高程序的可读性,但增加了系统的开销。

(2)自调用过程在函数内必须设置某些条件(结束条件),当条件成立时终止自调用过程,并使程序控制逐步从函数中返回。

(3)递归调用机制是通过栈数据结构实现的。

(4)函数值间由参数传递和返回值相联系。

例3:变量的作用域与存储类型

/* 变量的作用域 */
#include <stdio.h>
#include<stdlib.h>
void func()
{
    int a=5;
}
void main()   
{
    int a=10;
    func();
    printf("a=%d\n",a);
    system("pause");
    //return 0;
}

请问:为什么都是变量a,为什么func函数的变量不会对main函数中的a造成影响?

答:变量有生存期和作用域。

 

变量的作用域与存储类型

  • 变量的生存期:是从变量分配到内存开始到从内存中删除变量空间结束这段时间,即变量的存在时间。
  • 变量的作用域:也称为可见性,指变量能够被访问的范围。
  • 各种变量的生存期和作用域:

(1)全局变量:生存期和程序的生存期一致。也就是程序运行就为全局变量分配空间,程序结束才会释放全局变量的空间。全局变量的作用域从定义的地方开始到所在文件的结束都可见。

(2)局部变量:通常指动态局部变量(如函数的形参或者函数内的内部变量),生存期和函数一致,函数调用就为变量分配空间,函数结束就释放变量内存空间。作用域是从定义的位置开始,到func的右大括号结束。

/* 变量的作用域 */
#include <stdio.h>
#include<stdlib.h>
int a=-1;  // 全局变量
void func()
{
    int a=5;  // 局部变量
}
void main()   
{
    int a=10;  // 局部变量
    func();    // 在main函数中调用func函数时,在这个func中是看不到main中的a的
             // func调用结束之后,它的变量a的空间被释放,不可见,因此看到的是main中的a
    printf("a=%d\n",a);
    system("pause");
    //return 0;
}

心得:

如果在函数内部有同名变量,同名的全局变量就会被函数内部同名的局部变量覆盖,其作用域也被覆盖,出了被同名变量覆盖的地方这个全局变量才又可见。

复合语句中的动态局部变量,生存期与作用域都是在复合运行,变量定义开始,到复合语句右大括号结束。

  • 变量的作用域:指在源程序中定义变量的位置及其能被读写访问的范围。分为:局部变量和全局变量。

1. 局部变量:在语句块内定义的变量(形参也是局部变量)

特点:

(1)生存期是该语句块,进入语句块时获得内存,仅能由语句块内语句访问,退出语句块时释放内存,不再有效。

(2)定义时不会自动初始化,除非程序员指定初值。

(3)并列语句块各自定义的同名变量互不干扰。(形参和实参可以同名)

疑问:为什么形参和实参同名会出错???

2. 全局变量:在所有函数之外定义的变量。生存期是整个程序,从程序运行起占据内存,程序运行过程中可随时访问,程序退出时释放内存。有效范围是从定义变量的位置开始到本程序结束。

  • 变量的存储类型:指数据在内存中存储的方式。即编译器为变量分配内存的方式,它决定变量的生存期。
  • 调用形式:存储类型 数据类型 变量名

  • C程序的存储类别(Storage Class):

auto型(自动变量)

static型(静态变量)

extern型(外部变量)

register型(寄存器变量)

 

静态和动态数据都存储在RAM存储区,寄存器是在CPU中的。

 

静态存储区的变量:和程序共存亡。

动态存储区的变量:和所在的函数共存亡。

寄存器中的变量:同动态存储区,和所在函数共存亡。

 

1. 自动变量定义:auto 数据类型 变量名(就是在数据类型前面加上auto关键字)

(1) 进入语句块时自动申请内存,退出时自动释放内存。

(2)动态局部变量,缺省的存储类型。(如果没有指明变量类型,那么就是默认缺省的auto)

2. 静态变量定义:static 数据类型 变量名

(1)存储期同动态局部变量

(2)生存期为整个程序运行期间

(3)作用域同动态局部变量,是在函数内部可见。

例4:利用静态变量计算整数n的阶乘n!

/* 利用静态变量求n! */
#include <stdio.h>
#include<stdlib.h>
long func(int n);
int main()   
{
    int i,n;
    printf("Please input n:\n");
    scanf_s("%d",&n);
    for(i=1;i<=n;i++)
    {
        printf("%d!=%ld\n",i,func(i));
    }
    system("pause");
    return 0;
}
long func(int n)
{
    static long p=1;   // 定义静态变量
    p=p*n;
    return p;
}

/* 修改为动态变量 */
#include <stdio.h>
#include<stdlib.h>
long func(int n);
int main()   
{
    int i,n;
    printf("Please input n:\n");
    scanf_s("%d",&n);
    for(i=1;i<=n;i++)
    {
        printf("%d!=%ld\n",i,func(i));
    }
    system("pause");
    return 0;
}
long func(int n)
{
    auto long p=1;   // 定义静态变量
    p=p*n;
    return p;
}

结果错误!

心得:若静态局部变量和全局变量自动初始化为0,自动变量不初始化时,值是随机值,因此我们必须明确初始化。静态变量仅初始化一次,变量的值可保存到下次进入函数,使函数具有记忆功能。而自动变量是每次调用函数时都会初始化(每次进来都重新分配空间),没有记忆功能。虽然静态局部变量作用域是函数,即只在函数内部可见,但它的生存期是整个程序。

  • 寄存器:是CPU内部容量有限,但速度极快的存储器。

3. 寄存器变量定义:register 类型名 变量名

注意:

(1)使用频率比较高的变量声明为register,可使程序更小,执行速度更快。

(2)现在编译器有能力自动把普通变量优化为寄存器变量,并且可以忽略用户的指定。

(3)所以一般无需特变声明变量为register。

小结:

(1)全局变量:生存期是从出生到结束,作用域是整个程序。

(2)局部变量:其中静态局部变量生存期是整个程序,作用域是函数内。其它的局部变量,如自动变量、寄存器变量的生存期和作用域都是函数内(即函数内可见)。

(3)递归函数要先写出结束条件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Satisfying

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值