声明:该读书笔记摘抄自《C和指针》——Kenneth A.Reek (著) 徐波(译)。为了克服自己走马观花,提高阅读和学习效率,决定将自己在读书过程中遇到的一些知识点加以摘抄和总结备忘,在此感谢原书作者和翻译。
一、递归的两个特性
1、存在限制条件
2、每次递归之后越来越接近限制条件
二、将二进制数字转换为可打印字符的递归实现
void binary_to_ascii(unsigned int value)
{
unsigned int quotient ;
quotient = value / 10 ;
if (0 != quotient) {
binary_to_ascii(quotient) ;
}
putchar(value % 10 + '0') ;
}
追踪递归函数的执行过程的关键是理解函数中所声明的变量是如何存储的,当函数被调用时,它的变量空间时是声明在运行时堆栈上的。以前调用的函数的变量仍保留在堆栈上,但它们被新函数所声明的变量所覆盖,不能访问。递归函数调用自身时,每调用一次都将在堆栈上创建一批新的变量,它们将覆盖前一次调用在堆栈上创建的变量。最后一次递归调用的函数的变量位于栈顶。这样,当递归完成时,最后一次的调用将开始首先返回,并且该函数的变量位于栈顶,首先出栈;第一次被调用的递归函数最后返回,它的变量最后出栈。因此,可以总结出,递归函数展开时完成入栈操作,回归时完成出栈操作。
三、递归与迭代
1、计算n的阶乘
1)递归实现n的阶乘
long factorial(int n)
{
if (0 >= n) {
return 1;
} else {
return n*factorial(n-1); //尾部递归可以方便得转换为迭代实现
}
}
2)、迭代实现n的阶乘
long factorial(int n)
{
int result = 1;
while (1 < n) {
result *= n;
n -= 1;
}
return result;
}
对比:在计算n的阶乘的两种方法中,递归并没有体现出它的优势,因为递归函数调用将涉及到运行时开销,比如参数入栈,为局部变量分配内存空间,保存寄存器的值,在函数返回时,参数出栈,销毁局部变量,寄存器恢复。这样的话,用迭代效率更高一些。
2、计算斐波那契数。斐波那契数其实是一个数列,它的每个数的数值是前两个数之和,第一个和第二个数为1。
1)斐波那契数的递归实现,计算第n个斐波那契数
long fibonacci(int n)
{
if (2 >= n) {
return 1;
} else {
return (fibonacci(n-1) + fibonacci(n-2)) //尾部递归可以用迭代实现
}
}
2)斐波那契数的迭代实现,计算第n个斐波那契数
long fibonacci(int n)
{
long result;
long prev; //前面的那个数
long next; // 前面的第二个数
result = 1;
prev = 1;
while (2 < n) {
next = prev;
prev = result;
result = prev + next;
n--;
}
return result;
}
对比:在递归版本中存在冗余计算,效率相当低;应采用第二种方法。
四、可变参数
1、头文件 <stdarg.h>
2、 可变参数列表省略号(三个点): 用于提示此处可以传递数量和类型未定的参数,声明函数原型时也必须这样声明。
va_list :变量类型,用于访问参数列表中的未确定的部分
va_start : 是一个宏函数,用于初始化va_list定义的变量。它接受两个参数,第一个是var_list定义的变量,第二个参数是参数列表中省略号前面最后一个有名字的参数。该初始化过程将var_list定义的变量指向了可变参数部分的第一个参数。
va_arg: 是一个宏函数,用于依次访问可变参数变量列表。它也接受两个参数,第一个是var_list定义的变量,第二个是参数列表中下一个参数的类型(在某些函数中可能需要根据前面获得的数据来判断下一个参数的类型)。该宏函数返回该这个参数的值,并且使va_list定义的变量指向下一个可变参数。
va_end:是一个宏函数,当访问完最后一个可变参数后,需要调用该宏函数。
3、可变参数的限制
1)可变参数列表必须从第一个可变参数开始逐个访问,可以不全部访问,不能跳序访问;
2)所有参数列表的变量没有原型说明,因此传递的值都将进行缺省参数类型提升;例如char、short提升为int,float提升为double
3)参数列表中至少要有一个命名参数,否则无法使用va_start查找参数列表的可变部分;
以上是第七章的摘抄和总结,未完待续。。。。。