(C/C++学习笔记) 十. 函数

时间:2023-09-12 22:32:02

十. 函数

● 基本概念

函数

函数定义 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),但要求a和b有确定的值; 形参不能是表达式

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

int n;

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

cin>>n;

cout<<"\nthe 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成立

}

(C/C++学习笔记) 十. 函数

#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;

}

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

(C/C++学习笔记) 十. 函数

● 栈帧

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

(C/C++学习笔记) 十. 函数

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

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

(2)EBP:基址指针寄存器(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. int callee(int x, int y)
  6. {
  7. return x + y;
  8. }

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

  1. caller:
  2. pushl %ebp //将上一帧的帧指针压栈
  3. movl %esp, %ebp //令ebp指向帧头
  4. pushl %ebx
  5. 个字节
  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+n,eax(前者)的值为callee的返回值
  15. addl $20, %esp //释放空间
  16. popl %ebx //恢复信息
  17. popl %ebp
  18. ret //弹出返回地址并返回
  19. callee:
  20. pushl %ebp
  21. movl %esp, %ebp
  22. movl 12(%ebp), %eax
  23. movl 8(%ebp), %edx
  24. leal (%edx,%eax), %eax
  25. popl %ebp
  26. ret

个字节,但实际只需要8个字节,这是什么原因呢?
-----
| ebp |
| ebx |
| . |
| . |
| . |
| n |
| m |
| ra |
-----
这是因为在IA32架构中,栈帧的大小总是16的倍数。通过研究栈帧的内容,可以发现寄存器ebp、ebx再加上返回地址ra总共是12个字节,因此栈帧的总大小为32。

● 函数的调用

函数的调用的执行过程:

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

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

③ 转跳到函数体处。

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

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

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

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

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

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

例如:

int max(int x,int y)

{

int z;

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

return z ;

}

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

(C/C++学习笔记) 十. 函数

//几个概念

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

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

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

● 默认参数(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!");    //直接传递实际参数

}

(C/C++学习笔记) 十. 函数

//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;

}

(C/C++学习笔记) 十. 函数

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

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++)<<"\t";

cout<<endl;

}

int SQ(int x)

{

return ((x)*(x))

}

(C/C++学习笔记) 十. 函数

//带参宏

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

#include <iostream.h>

void main()

{

int i=1;

while(i<=5)

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

cout<<endl;

}

(C/C++学习笔记) 十. 函数

● 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;

}

(C/C++学习笔记) 十. 函数

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

#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;

}

(C/C++学习笔记) 十. 函数

● 空类型函数

空类型函数: 凡不要求返回值的函数都应定义为空类型 & 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;

}

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

(C/C++学习笔记) 十. 函数

#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;

}

(C/C++学习笔记) 十. 函数

//在上面的程序中, 子函数的定义在主函数上面, 如果被定义在主函数下面, 那么在主函数上面要就要有函数声明(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++学习笔记) 十. 函数

● 三种函数调用

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

● 嵌套调用

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

注意: 在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;

}

(C/C++学习笔记) 十. 函数

一般函数调用:

(C/C++学习笔记) 十. 函数

函数的嵌套调用:

(C/C++学习笔记) 十. 函数

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

#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\n",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);

}

(C/C++学习笔记) 十. 函数

(C/C++学习笔记) 十. 函数

● 递归调用

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

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

(C/C++学习笔记) 十. 函数
(C/C++学习笔记) 十. 函数

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

(C/C++学习笔记) 十. 函数
(C/C++学习笔记) 十. 函数

注意:

① 递归一定要有出口。

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

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

#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", n);

}

else

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

}

void main()

{

func(10);

}

(C/C++学习笔记) 十. 函数

#include <stdio.h>

void func(int n)

{

if (n > 0)

{

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

func(n - 1);

}

else

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

}

int main(void)

{

func(-10);

func(0);

func(10);

return 0;

}

(C/C++学习笔记) 十. 函数

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

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

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. 一个会被反复执行一套指令的循环.

加到100

int v=1;

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

{

v=v+i;

}

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

递推法

(C/C++学习笔记) 十. 函数

#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\n",m);

}

(C/C++学习笔记) 十. 函数

递归法

(C/C++学习笔记) 十. 函数

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

//形式一

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

{

if(n==初值)

return…;

else

return含f(n-1)的表达式;

}

(C/C++学习笔记) 十. 函数

#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",n,s);

}

(C/C++学习笔记) 十. 函数

//形式二

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

{

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

if(n==初值)

结果(t)=…;

else

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

return(返回结果(t));

}

(C/C++学习笔记) 十. 函数

#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);

}

(C/C++学习笔记) 十. 函数

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

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

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

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

,回推结束。

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

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

(C/C++学习笔记) 十. 函数

(C/C++学习笔记) 十. 函数

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

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

int fact(int n)

{

if(n<=1)

return 1;

else

return n*fact(n-1);

}

(C/C++学习笔记) 十. 函数

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

,610,987,1597,2584,4181,6765,10946,17711,28657,46368...

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

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

(C/C++学习笔记) 十. 函数

//方法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("\n");

f1=f1+f2;

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

}

}

(C/C++学习笔记) 十. 函数

//方法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("\n");

}

}

(C/C++学习笔记) 十. 函数

● 函数递归典型案例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("\n");

}

(C/C++学习笔记) 十. 函数

:用递归函数来实现

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

if(n为一位整数)

输出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("\n");

}

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后,继续递归操作*/

}

}

(C/C++学习笔记) 十. 函数

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

(C/C++学习笔记) 十. 函数

假设: 塔A有n个盘子

已知条件:若只有一个盘子,则是直接从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) 则A→C;

(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\n",a,c);

else

{

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

printf("%c--->%c\n",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:\n",m);

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

}

(C/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;

}

(C/C++学习笔记) 十. 函数

● 内联函数

内联函数(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;

}

(C/C++学习笔记) 十. 函数