zoukankan      html  css  js  c++  java
  • (C/C++学习笔记) 十. 函数

    十. 函数

    ● 基本概念

    函数

    函数定义 function definition:

    return_type function_name ( parameter list )

    { Body of function; }        //函数体内先是变量的声明, 然后是语句; 这就样平时看到的主函数一样, 先有变量, 再有语句

     

    函数调用 function call:

    function_name ( arguments list );

     

    函数原型: 函数原型也叫函数声明,还叫引用说明, 函数声明由函数返回类型、函数名和形参列表组成。形参列表必须包括形参类型,但是不必对形参命名。

     

    函数声明 function declaration

    return_type function_name ( parameter list );

    ※ 如果函数定义在先,调用在后,调用前可以不必声明;
    如果函数定义在后,调用在先,调用前必须声明。

    #include <iostream>

    using namespace std;

    void show_message();    //声明一个返回值类型为空(不返回函数值), 没有形参, 函数体只有一条输出语句的子函数,

    void show_age();        //声明一个返回值类型为空(不返回函数值), 没有形参的子函数, 函数体有变量定义和输出语句的子函数

    void out(int x, int y);    //声明一个返回值类型为空(不返回函数值), 但有两个形参的子函数(一些返回值类型为空的子函数不需要没有形参); 形参列表可以省略形参, int dec(int, int)

    int dec(int x, int y);    //声明一个返回值为int, 函数体只包含一条返回语句的子函数; 两个不同子函数的形参的标识符可以一样

    int func(int n);        //声明一个返回值类型为空, 有一个形参, 函数体为一个if...else if...else...语句的子函数

     

    int main()

    {

        show_message();        //函数调用, 不需要实参

        show_age();

        out(10, 5);            //不能写成cout<<out(10, 5)<<endl, 因为子函数out()的没有返回值, 无法传值给cout对象

        cout<<dec(10, 5)<<endl;    //调用子函数dec()以后, 返回值返回到 主函数main()调用dec()函数的位置

        int a=10, b=5;

        cout<<dec(10, a+b)<<endl;    // 实参可以是常量、变量或表达式,如这里的dec(10, a+b),但要求ab有确定的值; 形参不能是表达式

        //不直接给出fun()实参的值, 而是靠用户输入

        int n;

        cout<<"input n: "<<endl; //如果没有这个提示, 就直接输入n的值

        cin>>n;

        cout<<" the result: "<<func(n)<<endl;

        return 0;

    }

     

    //子函数show_message()

    void show_message()

    {

        cout<<"Hello World!"<<endl;        //就算只有一条语句, 也必须要用一对花括号

    }

     

    //子函数show_age()

    void show_age()

    {

        int age=23;

        cout<<"age is: "<<age<<endl;

    }

     

    //子函数out()

    void out(int x,int y)

    {

        cout<<x+y<<endl;

        cout<<x-y<<endl;

    }

     

    //子函数dec()

    int dec(int x, int y)

    {

        return(x+y);

    }

     

    //子函数dec()

    int func(int n)

    {

        if(n>0)

            return 1;        //n>0成立

        else if (n==0)

            return 0;        //n=0成立

        else

            return -1;    //n=-1成立

    }

    #include <iostream>

    using namespace std;

    int add(int x)

    {

        return x;

    }

    int main()

    {

        int a=add(0);

        cout<<a<<endl;

        int b=add(a+10);

        cout<<b<<endl;

        return 0;

    }

    //函数的实参可以是表达式:

     

     

    ● 栈帧

    栈帧(Stack Frame)是栈上的内存片段,也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。其主要作用有:为局部变量和临时变量分配空间,传递参数,保存返回地址。

    每个函数在栈上拥有独立的帧,这些帧是在函数调用时分配的。在任一时刻,寄存器ebp(帧指针)都指向帧的起始处,寄存器esp(栈指针)指向帧的结尾处,它们之间的部分是当前函数的栈帧。我们可以这样认为,ebp是局部的,作用于当前帧,而esp是全局的,作用于整个栈。

    (1ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。

    (2EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

    ebp是寄存器,前面加%表示取寄存器的值, 以此类推.

    假设函数P(caller)调用函数Q(callee),一般来说在P中会执行如下操作:

    1. push %ebp; move %esp, %ebp; 保存上一帧的ebp,以便恢复,并将ebp指向帧头
    2. sub $NUM, %esp; 为局部变量和临时变量分配空间,其中NUM为字节数,在编译时已经确定
    3. 将参数从右到左依次压栈
    4. 将返回地址压栈
    5. 跳转到函数Q的第一条语句
    6. 将返回值(如果有)保存在eax
    7. 释放空间,恢复信息(ebp)
    8. 弹出返回地址并返回

    举个例子:

    1. int caller(int m, int n)
    2. {
    3. return m + n + callee(m, n);
    4. }
    5.  
    6. int callee(int x, int y)
    7. {
    8. return x + y;
    9. }

    由gcc编译之后的汇编代码如下:

    1. caller:
    2. pushl %ebp //将上一帧的帧指针压栈
    3. movl %esp, %ebp //ebp指向帧头
    4. pushl %ebx
    5. subl $20, %esp //为何要分配20个字节
    6. movl 12(%ebp), %eax //获取参数n
    7. movl 8(%ebp), %edx //获取参数m
    8. leal (%edx,%eax), %ebx //leal指令,相当于ebx = edx + eax = m + n
    9. movl 12(%ebp), %eax
    10. movl %eax, 4(%esp) //参数n压栈,注意n先于m压栈
    11. movl 8(%ebp), %eax
    12. movl %eax, (%esp) //参数m压栈
    13. call callee //返回地址压栈,并跳转到callee
    14. leal (%ebx,%eax), %eax //此时ebx的值为m+neax(前者)的值为callee的返回值
    15. addl $20, %esp //释放空间
    16. popl %ebx //恢复信息
    17. popl %ebp
    18. ret //弹出返回地址并返回
    19.  
    20. callee:
    21. pushl %ebp
    22. movl %esp, %ebp
    23. movl 12(%ebp), %eax
    24. movl 8(%ebp), %edx
    25. leal (%edx,%eax), %eax
    26. popl %ebp
    27. ret

    注意到一个细节,为了保存参数mncaller函数在栈上分配了20个字节,但实际只需要8个字节,这是什么原因呢?
    -----
    | ebp |
    | ebx |
    | . |
    | . |
    | . |
    | n |
    | m |
    | ra |
    -----

    这是因为在IA32
    架构中,栈帧的大小总是16的倍数。通过研究栈帧的内容,可以发现寄存器ebpebx再加上返回地址ra总共是12个字节,因此栈帧的总大小为32

     

    ● 函数的调用

    函数的调用的执行过程:

    第一步:函数调用;
    ① 将函数调用语句下一条语句的地址保存在一种称为
    ""的内存空间中,
    以便函数调用完后返回。将数据放到栈空间中的过程称为压栈

    ② 对实参表从后向前,依次计算出实参表达式的值,并将值压栈。

    ③ 转跳到函数体处。

       

    第二步:函数体执行,即逐条运行函数体中语句的过程。

    ④ 如果函数中还定义了变量,将变量压栈。

    ⑤ 将每一个形参以栈中对应的实参值取代,执行函数体。

    ⑥ 将函数体中的变量、保存在栈中的实参值,依次从栈中取出,以释放栈
    空间。从栈中取出数据称为出栈
    x出栈用pop(x)表示。

    第三步:返回,即返回到函数调用表达式的位置。

    ⑦ 返回过程执行的是函数体中的return语句。

     

    例如:

    int max(int x,int y)

    {

    int z;

    z=(x>y)?x:y;

    return z ;

    }

    函数max()的详细过程是:

     

    //几个概念

    调用函数时,要先把寄存器的值入栈,这个过程叫做函数调用时的现场保护.

    函数执行完成后要出栈,叫做现场还原.

    返回地址一般是紧邻函数调用语句的下一条语句的地址, 因为函数调用结束后程序要继续执行, 所以先把这个地址压入堆栈, 等函数调用结束以后, 把这个地址从堆栈里面弹出来, 然后执行.

     

    ● 默认参数(default parameter)

    //sample1.cpp

    #include <iostream>

    using namespace std;

    void OutputInfo(const char* pchData = "Hello!") //括号里的参数还可以写成(char* pchData = "Hello!"), 差别是?

    {

        cout << pchData << endl;    //输出信息

    }

    void main()

    {

        OutputInfo();            //利用默认值作为函数实际参数

        OutputInfo("World!");    //直接传递实际参数

    }

    //sample2.cpp

    #include <iostream>

    using namespace std;

    int add(int x=5, int y=6)

    {

        return x+y;        

    }

     

    int main()

    {

        cout<<add(10,20)<<endl;

        cout<<add(10)<<endl;

        cout<<add()<<endl;

        return 0;

    }

     

     

    /*注意①默认参数不能出现在非默认参数的左边:

    int func(int x, int y=10, int z)    //非法

    int func(int x, int y, int z=10)    //合法

    ②形参的默认值可以是: 全局常量, 全局变量, 表达式, 函数调用, 但不能是局部变量, :

    void func1()

    {

        int k;

        void func2(int x=k); //k为局部变量, 不能作为形参的默认值

    }

    */

     

    ● 函数和宏的区别

    //带参函数的定义和调用

    #include <iostream.h>

    int SQ(int);

     

    void main()

    {

        int i=1;    //声明变量并初始化

        while(i<=5)

            cout<<SQ(i++)<<" ";

        cout<<endl;

    }

     

    int SQ(int x)

    {

        return ((x)*(x))

    }

    //带参宏

    #define SQ(x) ((x)*(x))

    #include <iostream.h>

     

    void main()

    {

        int i=1;

        while(i<=5)

            cout<<SQ(i++)<<" "; //主函数运行前, SQ(i++)被替换为((i++)*(i++)), 每次循环后,i的值会增加2, 因此只会做三次循环

        cout<<endl;

    }

     

    C/C++参数传递的3种方式

    传值调用----数据传送是单向的----不影响原始的实参值;

    地址调用----数据传送是双向的----修改原始的实参的值(在被调用函数中修改调用函数环境中的参数变量);

    引用调用---数据传送是双向的----同上

    #include <iostream.h>

    void swap1(int x, int y);        //单向值传递

    void swap2(int *x, int *y);    //(双向)地址传递, 优先在C语言中使用

    void swap3(int &x,int &y);                // (双向)引用传递, C语言中使用(因为引用是C++的概念)

     

    int main()

    {

        int a=5,b=10;

        swap1(a,b);

        cout<<"a="<<a<<endl;

        cout<<"b="<<b<<endl;

        swap2(&a,&b);

        cout<<"a="<<a<<endl;

        cout<<"b="<<b<<endl;

        swap3(a,b);

        cout<<"a="<<a<<endl;

        cout<<"b="<<b<<endl;

        return 0;

    }

     

    void swap1(int x, int y)    //如下三个子函数, 也可以定义为int, 并返回0

    {

        int tmp;

        tmp=x;

        x=y;

        y=tmp;

        cout<<"swap1: x="<<x<<endl;

        cout<<"swap1: y="<<y<<endl;

    }

     

    void swap2(int *x, int *y)

    {

        int tmp;

        tmp=*x;

        *x=*y;

        *y=tmp;

        cout<<"swap2: x="<<*x<<endl;

        cout<<"swap2: y="<<*y<<endl;

    }

     

    void swap3(int &x,int &y)

    {

        int tmp = x;

        x = y;

        y = tmp;

        cout<<"swap3: x="<<x<<endl;

        cout<<"swap3: y="<<y<<endl;

    }

    //把swap3独立出来, 那么swap2swap3的效果就是一样的:

    #include <iostream.h>

    void swap3(int &x,int &y);                // (双向)引用传递, C++语言中使用(因为引用是C++的概念)

     

    int main()

    {

        int a=5,b=10;

        swap3(a,b);

        cout<<"a="<<a<<endl;

        cout<<"b="<<b<<endl;

        return 0;

    }

     

    void swap3(int &x,int &y)

    {

        int tmp = x;

        x = y;

        y = tmp;

        cout<<"swap3: x="<<x<<endl;

        cout<<"swap3: y="<<y<<endl;

    }

     

     

    ● 空类型函数

    空类型函数: 凡不要求返回值的函数都应定义为空类型 & auto, 静态局部变量

    #include<iostream.h>

     

    void AddOne()        //形式参数列表为空, 这样就定义了不需要参数的函数,并且, 这个函数不要求返回值, 所以应定义为空类型

    {

        auto int a=1;

        a+=1;

        cout<<a<<endl;

    }

    int main()

    {

        cout<<"第一次调用:"<<endl;

        AddOne();

        cout<<"第二次调用:"<<endl;

        AddOne();

        return 0;

    }

     

    //上面的空类型函数也可以写成:

    int AddOne()

    {

        auto int a=1;

        a+=1;

        cout<<a<<endl;

        return 0;

    }

    //但为了降低程序出错的几率,凡不要求返回值的函数都应该定义为空类型,所以最好不要这样定义

    #include<iostream.h>

    void AddOne()    //函数声明/函数原型, 如果有形参,要写形参类型和形参(形参可省略)

     

    void AddOne()

    {

        static int a=1;

        a+=1;

        cout<<a<<endl;

    }

    int main()

    {

        cout<<"第一次调用:"<<endl;        //第一次调用后,虽然变量a的作用域和上例中的auto变量a一样,即都是在AddOne函数体类,但此时a保留了此次调用后留下的值

        AddOne();

        cout<<"第二次调用:"<<endl;    

        AddOne();    

        return 0;

    }

     

     

    //在上面的程序中, 子函数的定义在主函数上面, 如果被定义在主函数下面, 那么在主函数上面要就要有函数声明(function declaration)/函数原型(function prototype), :

    #include<iostream.h>

     

    int main()

    {

        cout<<"第一次调用:"<<endl;        //第一次调用后,虽然变量a的作用域和上例中的auto变量a一样,即都是在AddOne函数体类,但此时a保留了此次调用后留下的值

        AddOne();

        cout<<"第二次调用:"<<endl;    

        AddOne();    

        return 0;

    }

     

    void AddOne()

    {

        static int a=1;

        a+=1;

        cout<<a<<endl;

    }

     

    ● 返回值为空(void)类型的函数不能进行赋值和传值

    返回值为空(void)类型的函数不能进行赋值和传值

    //有如下空函数:

    void show_index()

    {

        int index=10;

        cout<<"Index is:"<<index<<end;

    }

     

    //下面的操作时不允许的

    i=show_index();    //,非法语句, 返回值为空的函数不能进行赋值

    set_index(show_index);    //非法语句,返回值为空的函数不能进行值传递

     

    ● 空函数

    空函数: 没有参数和返回值, 函数的作用域也为空

    void set_work_space() {}    //作用是在编程的开始阶段, 在将来准备扩展功能的地方写上一个空函数, 先占一个位置

     

    int main()

        {

        set_work_space()

        return 0;

        }

     

    ● 自定义并调用可变参数函数

    #include <iostream>

    #include <STDARG.H> //需要包含该头文件

    using namespace std;

    void OutputInfo(int num,...)                        //定义一个省略号参数的函数

    {

        va_list arguments;                            //定义va_list类型变量

        va_start(arguments,num);

        while(num--)                                //读取所有参数的数据

        {

            char* pchData = va_arg(arguments,char*);    //获取字符串数据

            int iData = va_arg(arguments,int);            //获取整型数据

            cout<< pchData << endl;                    //输出字符串

            cout << iData << endl;                        //输出整数

        }

        va_end(arguments);

    }

     

    void main()

    {

        OutputInfo(2,"Beijing",2008,"Olympic Games",2008);    //调用OutputInfo函数

    }

     

    ● 三种函数调用

    一般函数调用, 函数的嵌套调用, 函数的递归调用

     

    ● 嵌套调用

    嵌套调用: 在自定义函数中调用其它自定义函数

    注意: C/C++语言中, 函数的定义都是互相平行、独立的,也就是说,即在定义函数时,一个函数体内不能定义另一函数;即嵌套定义是不允许的,但嵌套调用是允许的,即在一个函数体内可以调用另一函数,如我们常见的在main()函数内调用另一函数。

    //函数嵌套调用的简单案例

    #include <iostream.h>

     

    void show_message()

    {

        cout<<"The show_message function"<<endl;

    }

     

    void display()

    {

        show_message();    //嵌套调用, 即在子函数display()中调用另一个子函数show_message()

    }

    int main()

    {

        display();    //主函数调用子函数display(), 子函数display()调用另一子函数show_message()

        return 0;

    }

    一般函数调用:

    函数的嵌套调用:

    //函数嵌套调用的复杂案例

    #include <stdio.h>

    int dif(int x,int y,int z);

    int max(int x,int y,int z);

    int min(int x,int y,int z);

     

    void main()

    {

        int a,b,c,d;

        scanf("%d%d%d",&a,&b,&c);

        d=dif(a,b,c);

        printf("Max-Min=%d ",d);

    }

     

    int dif(int x,int y,int z)

    {

        return max(x,y,z)-min(x,y,z);

    }

     

    int max(int x,int y,int z)

    {

        int r;

        r=x>y?x:y;

        return(r>z?r:z);

    }

     

    int min(int x,int y,int z)

    {

        int r;

        r=x<y?x:y;

        return(r<z?r:z);

    }

     

    ● 递归调用

    函数直接或间接的调用自身叫函数的递归调用, 即递归可以分为直接递归调用和间接递归调用。

    1. 直接递归调用:是在调用函数的过程中又调用该函数本身;

    1. 间接递归调用:是在调用f1()函数的过程中调用f2()函数,而f2()中又需要调用f1()

     

    注意:

    ① 递归一定要有出口。

    ② 递归次数不宜过多,否则可能引起堆栈溢出。

    ③ 无限循环不一定会崩溃, 但如果函数的递归没有终止条件, 系统会崩溃.

    #include <stdio.h>

     

    void func(int n)

    {

        if (n > 0)

        {

            func(n - 1); // 一语句是func()函数运行的第一步,当一次调用的函数运行结束时,系统将释放这次调用时所占用的存储单元,程序的执行流程返回到上一层的调用点,同时取用当初进入该层时函数中的变量(在函数体里面)和形参所占用的存储单元中的数据.

    // 递推过程(剥卷心菜):func(10)→func(9)……func(1)

    // 回归过程(合卷心菜):1→2→……10

            printf("n=%d ", n);

        }

        else

            printf("n is less than or equal to 0. ");

    }

     

     

    void main()

    {

        func(10);

    }

     

    #include <stdio.h>

     

    void func(int n)

    {

        if (n > 0)

        {

            printf("n=%d ", n);

            func(n - 1);

        }

        else

            printf("n is less than or equal to 0. ");

    }

     

    int main(void)

    {

        func(-10);

        func(0);

        func(10);

        return 0;

    }

     

    ● 分析问题的两种方法:递推(迭代)和递归

    分析问题的两种方法:递推(迭代)和递归

    There are two approaches to writing repetitive algorithms: recursion and iteration (迭代和递归).

    ①    递归是重复调用函数自身实现循环。

    ②    而迭代与普通循环的区别是:循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值。

    版本一:

    ● 递推:从一个已知的事实出发,按一定的规律推出下一个事实。

    通常利用迭代公式法,通过循环结构实现,循环的终值是问题的结果。

    ● 递归:从要求的结果出发,归纳出后一个结果和前一个结果存在的关系,直到一个已知值(初值)为止(即反推法)。

    这样我们就能设计一个函数(递归函数),使它不断使用下一级值调用自身,直到一个已知值(递归出口)。

     

    版本二:

    递归是首先自顶向下逐步拓展计算需求,最后自底向上计算, 即由f(n)拓展到f(1),再由f(1)逐步算回f(n)

    迭代是直接自下向顶运算,由f(1)算到f(n)

     

    版本三:

    递归(recursion): Any procedure(A) which, while being executed, either calls itself or calls another procedure (B),which in turn calls procedure(A).过程(A)执行时, 或调用其自身,或调用另一过程(B),(B)又调用过程(A), 这种进程(process)称为递归。

    经典案例: 阶乘, 汉诺塔, Fibonacci数列

     

    迭代(iteration): a. The process of repeating a set of instructions a specified number of times or until a specific result is achieved.

    重复一套指令的执行过程,执行特定的次数或直到得到一个特定的结果

    b. One cycle of a set of instructions to be repeated: After ten iterations, the program exited the loop. 一个会被反复执行一套指令的循环.

    经典案例: 1加到100

    int v=1;

    for(i=2;i<=100;i++)

    {

    v=v+i;

    }

     

    ● 函数递归典型案例1: n的阶乘

    递推法

    #include<stdio.h>

     

    main()

    {

        int m=1,i,n;

        scanf("%d",&n);

        for(i=1;i<=n;i++)

        m=m*i;

        printf("the result is %10d ",m);

    }

    递归法

    递归函数设计的一般形式是:

    //形式一

    递归函数名f(参数n,……)

    {

        if(n==初值)

            return…;

        else

            returnf(n-1)的表达式;

    }

    #include <stdio.h>

    int fac(int n)

    {

    if(n==0||n==1)

            return 1;/*递归出口*/

    else 

            return fac(n-1)*n; /*递归公式*/

    }

    main()

    {

        int n, s;

        scanf("%d",&n);

        s=fac(n);

        printf("%d! =%15d ",n,s);

    }

    //形式二

    递归函数名f(参数n,……)

    {

        定义一个保存结果的变量t;

        if(n==初值)

            结果(t)=…;

        else

            结果(t)=f(n-1)的表达式;

        return(返回结果(t));

    }

    #include <stdio.h>

    int fac(int n)

    {

        int t;    //定义一个保存结果的变量t;

        if(n==0||n==1)

            t=1;/*递归出口*/

        else

            t=fac(n-1)*n; /*递归公式*/

    return(t);

    }

    main()

    { int n, s;

    scanf("%d",&n);

    s=fac(n);

    printf("%d! =%15d",n,s);

    }

    递归程序分两个阶段执行:

    调用:欲求fac(n)→先求fac(n-1)fac(n-2) … → fac(0)

    若fac(0)已知,回推结束。

             欲求fac(3) fac(2) fac(1) fac(0)

             fac(1) 已知等于1,回推结束。

    回推:已知fac(0)→可求出fac(1)fac(2)… → (n) 

    已知fac(0)→可求出fac(1)fac(2) fac(3)

     

     

    ● fac函数在回推阶段被调用了4次,递归调用了3(考虑n=0的情况),到终止条件才有确定的值,然后再递推出每一次调用的值,最后得到所求的结果。

     

    • 另外一个图示(不考虑n=0的情况):

    int fact(int n)

    {

        if(n<=1)

            return 1;

        else

            return n*fact(n-1);

    }

     

    ● 函数递归典型案例2: Fibonacci数列问题(求前12项之和)

    斐波那契数列指的是这样一个数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233377610987159725844181676510946177112865746368...

    特别指出:第0项是0,第1项是第一个1。这个数列从第2项开始,每一项都等于前两项之和。

    在数学上,斐波纳契数列以如下被以递归的方法定义:

     

    //方法1: 用循环结构来实现

    #include <stdio.h>

    void main()

    {

        long int f1,f2;

        int i;

        f1=1;

        f2=1;

        for(i=1;i<=20;i++)

        {

            printf("%12ld %12ld",f1,f2);

            if(i%2==0)

                printf(" ");

            f1=f1+f2;

            f2=f2+f1; //每次循环打印两个数字

        }

    }

     

    //方法2: 递归法

    #include<stdio.h>

    int fun(int n)

    {

        if(n==1||n==2)

            return 1;

        if(n>2)

            return (fun(n-1)+fun(n-2));

    }

    void main()

    {

        int i;

        for(i=1;i<=20;i++)

        {

            printf("%8d",fun(i));

        if(i%5==0) //这两句是用来换行

            printf(" ");

        }

    }

     

     

    ● 函数递归典型案例3: 反向输出一个整数(非数值问题)

    //方法1:用循环结构来实现

    #include<stdio.h>

    main()

    {

        int n;

        scanf("%d",&n);

        if(n<0)

        {

            n=-n;

            printf("-");    //如果n>=0, 这个符号就不会打印出来

        }

        while(n!=0)

        {

            printf("%d", n%10);

            n=n/10;

        }

        printf(" ");

    }

    //方法2:用递归函数来实现

    /* 将反向输出一个正整数的算法归纳为:

    ifn为一位整数)

    输出n

    else

    {

    输出n的个位数字;

    对剩余数字组成的新

    整数重复"反向输出"

    操作;

    } */

    #include<stdio.h>

    void main()

    {

        void printn(int x);

        int n;

        printf("Input n=");

        scanf("%d",&n);

        if(n<0)

        {

            n=-n;

            putchar('-');

        }

        printn(n);

        printf(" ");

    }

     

    void printn(int x) /*反向输出整数x*/

    {

        if(x>=0 && x<=9) /*若x为一位整数*/

         printf("%d",x); /*则输出整数x*/

        else /*否则*/

        {

            printf("%d",x%10); /*输出x的个位数字*/

            printn(x/10);

    /*x中的个位数字去掉,形成新的x后,继续递归操作*/

        }

    }

     

    ● 函数递归典型案例4: 汉诺塔(河内塔)问题

    假设: An个盘子

    已知条件:若只有一个盘子,则是直接从A移到C(递归出口)

    算法设计如下:

    • 第一步:把n-1个盘子依照题目中的规则从塔A(源塔)借助于塔C(中间塔)搬到塔B(目标塔)
    • 第二步:将剩下的一只盘(也就是最大的一只)直接从塔A(源塔)搬到那个仍然空着的塔C(目标塔)
    • 第三步:再次将B(源塔)上的n-1个盘子借助于塔A(中间塔) 搬到塔C(目标塔)。这一步是没有问题的,因为C塔上仅有一只最大的盘。

     

    函数hanoi( int n, int a, int b, int c)设计:

    1. 如果(n==1) AC;

    2. 否则执行3

        (1) 调用函数hanoi(n-1,a,c,b);

        (2) a→c;

        (3) 调用函数hanoi(n-1,b,a,c);

    #include<stdio.h>

    void hanoi(int n,char a,char b,char c)

    {

        if(n==1) printf("%c--->%c ",a,c);

        else

        {

            hanoi(n-1,a,c,b);

            printf("%c--->%c ",a,c);

            hanoi(n-1,b,a,c);

        }

    }

     

    main()

    {

        int m;

        printf("Input the number of disks:");

        scanf("%d",&m);

        printf("The steps to moving%3d disks: ",m);

        hanoi(m,'A','B','C');

    }

     

    ● 重载函数

    重载函数: 多个函数的函数名相同, 但参数类型或参数个数不同(不是返回值类型不同)

    #include <iostream>

    using namespace std;

    int Add(int x ,int y)                //定义第一个重载函数

    {

        cout << "int add" << endl;    //输出信息

        return x + y;                //设置函数返回值

    }

    double Add(double x,double y)        //定义第二个重载函数

    {

        cout << "double add" << endl;    //输出信息

        return x + y;                //设置函数返回值

    }

    int main()

    {

        int a = Add(5,2);            //调用第一个Add函数

        float b = Add(10.5,11.4);    //调用第二个Add函数

        cout<<a<<endl;

        cout<<b<<endl;

        return 0;

    }

     

    ● 内联函数

    内联函数(inline function): 定义了一个内联函数后, 编译器会在调用该内联函数的地方展开这个函数的副本(copy), 这样可以减少函数调用带来的开销(在程序所在文件内移动指针寻找被调用函数地址带来的开销)

    #include<iostream.h>

    inline int integer_add (int x,int y);    //声明该函数为内联函数

     

    void main()

    {

        int result=integer_add(5, 10);

        cout<<result<<endl;

    }

     

    int integer_add(int x,int y)    //也可以写成inline int integer_add(int x,int y),表明定义了一个内联函数

    {

        return x+y;

    }

     

  • 相关阅读:
    Mysql外键和表关系
    列类型-字符类型
    列类型-日期时间型
    列类型
    破解MySQL的root密码
    数据库简单使用
    数据库简介
    socketserver
    python网络编程-粘包问题的解决
    python-网络编程,简单模型
  • 原文地址:https://www.cnblogs.com/ArrozZhu/p/8377852.html
Copyright © 2011-2022 走看看