板凳——————————————————(老树)C Prime Plus( 第9章 函数)

第9章 函数

9.1 复习函数

函数(function)是完成特定任务的独立代码单元。

函数让程序更加模块化,提高代码可读性,方便修改。

9.1.1 创建并使用简单函数

函数原型指明了函数的返回值类型和函数接受的参数类型。这些信息称为该函数的签名(signature)

9.1.3 函数参数

9.1.4 定义带形式参数的函数

ANSI C风格的函数头:

void show_n_char(char ch, int num)

改行告知编译器本函数具有两个参数ch和num,类型分别是char和int。这两个参数被称为形式参数(formal parameter),简称形参。形式参数是局部变量。

注意每个变量前都要声明其类型,不能写成这样:void dibs(int x, y, z)

9.1.5 声明带形式参数函数的原型

函数原型一般是下面这种形式:

void show_n_char(char ch, int num)

也可以省略变量名:

void show_n_char(char, num)

9.1.6 调用带实际参数的函数

函数调用中,实际参数(actual argument,简称实参)提供了ch和num的值。

show_n_char(SPACE,12)

调用时实参的值(SPACE,12)赋值给函数中相应的形式参数(ch,num)。

9.1.7 黑盒视角

9.1.8 使用return从函数中返回值

9.1.9 函数类型

声明函数时必须声明函数的类型,带返回值的函数类型和其返回值类型相同,

不带返回值的函数应该声明为void类型。

9.2 ANSI C函数原型

9.2.1 问题所在

9.2.2ANSI的解决方案

针对参数不匹配的问题,ANSI C要求函数声明时还要声明变量的类型,即 使用函数原型来声明函数的返回类型、参数的数量和每个参数的类型。有了这些信息,编译器可以检查函数调用是否和函数原型匹配。

9.2.3 无参数和未指定参数

为了表明函数没有参数,应该在圆括号内使用void

void print_name(void);

一些函数接受许多参数,ANSI C允许使用部分原型:

int printf(const char *, …)

表明第一个参数是字符串,可能还有其他参数。

9.2.4 函数原型的优点

让编译器在第1次执行到该函数前就知道如何使用它。

将整个函数定义放在第1次调用该函数之前,也有同样的效果。

此时,函数定义也相当于函数原型。

9.3 递归
关于递归的概念,我们都不陌生。简单的来说递归就是一个函数直接或间接地调用自身,是为直接或间接递归。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。用递归需要注意以下两点:(1) 递归就是在过程或函数里调用自身。(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
递归一般用于解决三类问题:
   (1)数据的定义是按递归定义的。(Fibonacci函数,n的阶乘)
   (2)问题解法按递归实现。(回溯)
   (3)数据的结构形式是按递归定义的。(二叉树的遍历,图的搜索)
递归的缺点:
  递归解题相对常用的算法如普通循环等,运行效率较低。因此,应该尽量避免使用递归,除非没有更好的算法或者某种特定情况,递归更为适合的时候。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,因此递归次数过多容易造成栈溢出。
  用线性递归实现Fibonacci函数,程序如下所示:
int FibonacciRecursive(int n)
{
if( n < 2)
return n;
return (FibonacciRecursive(n-1)+FibonacciRecursive(n-2));
}

//用普通的递归计算Fibonacci数列:

#include “stdio.h”
#include “math.h”

int factorial(int n);

int main(void)
{
int i, n, rs;
printf(“请输入斐波那契数n:”);
scanf("%d",&n);

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

return 0;

}

// 递归
int factorial(int n){
if(n <= 2) {
return 1;
}
else {
return factorial(n-1) + factorial(n-2);
}
}
/*http://www.nowamagic.net/librarys/veda/detail/2317
wannian07@wannian07-PC:~/Desktop/Beginning C 5TH$ ./maopao
请输入斐波那契数n:20
6765
*/
一个典型的递归例子是对已排序数组的二分查找算法。
现在有一个已经排序好的数组,要在这个数组中查找一个元素,以确定它是否在这个数组中,很一般的想法是顺序检查每个元素,看它是否与待查找元素相同。这个方法很容易想到,但它的效率不能让人满意,它的复杂度是O(n)的。现在我们来看看递归在这里能不能更有效。
还是考虑上面的两个条件:
第一:这个问题是否可以分解为形式相同但规模更小的问题?
第二:如果存在这样一种分解,那么这种分解是否存在一种简单情境?

#include “stdio.h”
#include “stdlib.h”

void selectionSort(int data[], int count);
int binary_search(int *a, int n, int key);

int main()
{
int i, key, rs;
int arr[10];
int count;

printf("排序前数组为:");
srand((int)time(0));
for(i=0; i < 10; i++)
{
    arr[i] = rand()%100;
    printf("%d ",arr[i]);
}

count = sizeof(arr)/sizeof(arr[0]);
selectionSort(arr, count);

printf("\n排序后数组为:");
for(i=0; i < 10; i++)
{
    printf("%d ", arr[i]);
}

printf("\n请输入要查找的数字:");
scanf("%d",&key);

rs = binary_search(arr, 10, key);
printf("%d ", rs);

}

void selectionSort(int data[], int count)
{
int i, j, min, temp;
for(i = 0; i < count; i ++) {
/find the minimum/
min = i;
for(j = i + 1; j < count; j ++)
if(data[j] < data[min])
min = j;
temp = data[i];
data[i] = data[min];
data[min] = temp;
}
}

int binary_search(int *data, int n, int key)
{
int mid;
if(n == 1){
return (data[0] == key);
}else{
mid = n/2;
printf(“mid=%d\n”, data[mid]);
if(data[mid-1] == key)
return 1;
else if(data[mid-1] > key)
{
printf(“key %d 比 data[mid-1] %d 小,取前半段 \n”, key, data[mid-1]);
return binary_search(&data[0], mid, key);
}
else
{
printf(“key %d 比 data[mid-1] %d 大,取后半段 \n”, key, data[mid-1]);
return binary_search(&data[mid], n - mid, key);
}
}
}
/*http://www.nowamagic.net/librarys/veda/detail/2317
wannian07@wannian07-PC:~/Desktop/Beginning C 5TH$ ./maopao
排序前数组为:71 64 88 51 77 78 75 54 9 51
排序后数组为:9 51 51 54 64 71 75 77 78 88
请输入要查找的数字:20
mid=71
key 20 比 data[mid-1] 64 小,取前半段
mid=51
key 20 比 data[mid-1] 51 小,取前半段
mid=51
key 20 比 data[mid-1] 9 大,取后半段
http://www.nowamagic.net/librarys/veda/detail/2317
*/
9.3.3 尾递归

尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去。尾递归就是把当前的运算结果(或路径)放在参数里传给下层函数,深层函数所面对的不是越来越简单的问题,而是越来越复杂的问题,因为参数里带有前面若干步的运算路径。
  尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。比如f(n, sum) = f(n-1) + value(n) + sum; 会保存n个函数调用堆栈ÿ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值