09:函数、递归和递推

来源:互联网 发布:阿里云怎么备案域名 编辑:程序博客网 时间:2024/05/01 08:42
【练习】
模拟 windows 系统下的扫雷游戏,在 10 x 10 的地图中随机布雷 10 个;
若是雷的地方用 X 表示;不是雷的地方用 O 表示;
在周围有雷的格子里标记周围雷的数量。
<思想>
二维数组的组下标和组内下标分别表示坐标的 x 和 y ,随机数方式循环布雷
找到雷,在周围八个格子里进行依次 +1 操作
/*代码*/
#include <stdio.h>#include <stdlib.h>#include <time.h>int main(){    srand(time(0));    int map[10][10] = {0};    int delta[][2] = {-1, -1, -1, 0, -1, 1, 0, -1, 0, 1, 1, -1, 1, 0, 1, 1};    int row = 0, col = 0, cnt = 0, num = 0;    //布雷    do{        row = rand() % 10;        col = rand() % 10;        if(map[row][col] != -1){            map[row][col] = -1;            cnt++;        }    }while(cnt < 10);    //标记周围雷数量    for(row = 0; row < 10; row++){        for(col = 0; col < 10; col++){            if(map[row][col] != -1){                continue;            }            for(num = 0; num < 8; num++){                int tmp_row = row + delta[num][0];                int tmp_col = col + delta[num][1];                if(tmp_row < 0 || tmp_row > 9 || tmp_col < 0 || tmp_col > 9){                    continue;                }                if(map[tmp_row][tmp_col] == -1){                    continue;                }                map[tmp_row][tmp_col]++;            }        }    }    //打印地图    for(row = 0; row < 10; row++){        for(col = 0; col < 10; col++){            if(map[row][col] == -1)                printf("X ");            else if(map[row][col] > 0)                printf("%d ", map[row][col]);            else                printf("O ");        }        printf("\n");    }    return 0;}

【函数】
C语言里可以采用分组的方式管理语句,每个语句分组叫做一个/* 函数 */
多函数程序执行的模式:
1. 整个程序的执行时间被分成几段,不同时间段被分配给不同的函数使用
2. 所有时间段之间既不能重叠也必须连续
3. 如果函数A把一段时间分配给函数B使用,则函数B在完成所有工作之后必须把后面的时间还给函数A
4. 如果函数A在工作过程中把一段时间分配给了函数B使用,则它们之间存在函数调用关系
在这个关系中函数A叫做调用函数,函数B叫做被调用函数
函数之间的调用关系只在一段时间范围内有效,这段时间就是被调用函数工作的时间范围
函数调用语句可以在两个函数之间产生函数调用关系
不可以跨函数使用变量
不同函数里的变量可以重名
如果函数多次运行则每次运行的时候它里面的变量对应的存储区都可能不同

volatile int num; //volatile:可变的,不稳定的

如果一个变量被多个程序共用,需要加 volatile 关键字,可以保证每次使用存储区的数据都是最新的数据。/*嵌入式会用到*/

即:如果变量对应的存储区被多个不同的程序共用就必须使用这个关键字声明变量。

【函数返回值】
函数调用过程中通常伴随着两个函数之间的数据传递
数据传递存在两个完全相反的方向,既可以从调用函数向被调用函数传递数据,也可以从被调用函数向调用函数传递数据
永远使用被调用函数分配的存储区记录要传递的数据
从被调用函数只能向调用函数传递 1 个数据
这个数据只能在被调用函数结束的时候才能传递数据
这个数据叫做被调用函数的/* 返回值 */
这个返回值必须记录在被调用函数分配的存储区里,应该把这个存储区的类型名称写在被调用函数的名称前面
return 关键字可以把一个数字作为返回值放在专门的存储区里
函数调用语句可以当作数字使用,这个数字就是函数的返回值
如果被调用函数没有使用 return 关键字设置返回值,则调用函数得到的就是一个随机数
调用函数得到返回值以后,或者立刻使用,或者转存到其他存储区
如果函数没有返回值就必须在函数名称前写 void
如果函数名称前面什么都没写,在C89规范里表示函数表示一个整数类型的存储区存放返回值,在C99规范中不允许这样
被调用函数不可以使用数组记录返回值

【函数参数】
为了从调用函数向被调用函数传递数据,就需要使用被调用函数提供的一组存储区,这些存储区的个数和类型任意
在函数名称后面的小括号里写一组变量声明语句,这些变量就用来代表用来传递数据的存储区
这些变量叫做/* 形式参数 */
小括号里的素有内容叫做 形势参数列表
每个形式参数的类型名称都不可以省略
相邻的形式参数声明之间用 , 隔开
在函数调用语句的小括号里需要为每个形式参数提供一个对应的数字,计算机会把这些数字记录到对应的形式参数存储区里
这些数字叫做/* 实际参数 */
能当作数字使用的内容都可以作为实际参数使用
如果函数没有形式参数,应该在函数名称后面的小括号里面写 
void
如果函数名称后面的小括号里什么都没写,就表示函数可以提供任意多个任意类型的形式参数

【练习1】
1 X 9 = 9
2 X 8 = 16
3 X 7 = 21
4 X 6 = 24
5 X 5 = 25
/*代码*/

#include <stdio.h>void cheng(void){    int i;    for(i = 1; i <= 5; i++){        printf("%d X %d = %d\n", i, 10-i, i*(10-i));    }   }int main(){    cheng();    return 0;}


【练习2】
编写程序从键盘得到一个 0 - 100 之间的整数,把这个整数到100之间所有和7无关的数字都显示在屏幕上
要求:在被调用函数中进行对7的判断
<思想>
包含 7 以及能被 7 整除的数字
/*代码*/
#include <stdio.h>void count(int i); //显示声明int main(){    int num = 0;    for(num = 0; num <= 100; num++){        count(num);    }       printf("\n");    return 0;}void count(int i){     if(!i)        printf("%d ", i);     if(i%10 != 7 && i/10 != 7 && i%7 != 0)        printf("%d ", i); }


【函数参数】
数组可以做为形式参数使用
和数组形式参数配合使用的实际参数应该是一个存储区的地址,通常用数组名称
数组做形式参数的时候只是把形式参数写成数组的样子,真正的形式参数并不是数组,而是一个可以当作数组使用的变量
数组形式参数里包含的存储区都不是被调用函数提供的
可以省略数组形式参数声明里中括号中间的整数
数组做形式参数的时候需要另外准备一个整数类型的形式参数用来表示数组形式参数里包含的存储区个数
调用函数可以任意使用数组形式参数里包含的存储区,所以可以利用数组形式参数实现双向数据传递,这种参数叫做/* 输入输出参数 */

/* 双向数据传递演示代码 */

#include <stdio.h>//单项数据传递:主函数传给被调用函数void print(int arr[5], int size){    int num = 0;    for(num = 0; num < size; num++){        printf("%d ", arr[num]);    }       printf("\n");}//双向数据传递void neg(int arr[], int size){    int num = 0;    for(num = 0; num < size; num++){        arr[num] = 0 - arr[num];    }   }int main(){    int arr[] = {1, 2, 3, 4, 5};     print(arr, 5);     neg(arr, 5);     print(arr, 5);     return 0;}

【练习】
编写函数生成一张彩票,彩票里包含多个 1 - 36 之间的随机数,把彩票里所有的数字传递给调用函数并显示
/* 代码 */
#include <stdio.h>#include <stdlib.h>#include <time.h>void lottery(int arr[], int size);int main(){    srand(time(0));    int num = 0, i = 0;    printf("请输入想要生成的彩票数的数量:");    scanf("%d", &num);    int lot[num];    lottery(lot, num);    for(i = 0; i < num; i++){        printf("%d ", lot[i]);    }       printf("\n");    return 0;}void lottery(int arr[], int size){    int i = 0;    for(i = 0; i < size; i++){        arr[i] = rand() % 36 + 1;    }   }

C语言里函数参数的个数可以不固定,这种参数叫做/* 变长参数 */
变长参数不可以在编写函数的时候进行命名,在被调用函数里需要使用特殊方法才能得到没有命名的参数内容
如果编译器在编译的时候首先遇到函数调用语句,就猜测函数的格式,猜测结果里函数有一个整数类型的返回值,函数可以有任意多个任意类型的形式参数
这个猜测结果叫做函数的/* 隐式声明 */
函数隐式声明里参数类型只能是 int 或者 double 
如果函数的实际格式和隐式声明的格式不一样,就会编译出错
函数大括号前面部分可以单独写成1条语句,这种语句叫做/* 函数声明语句 */
函数声明语句里可以省略形式参数名称
可以把函数声明语句单独写在文件开头,这叫做函数的/* 显式声明 */
函数显式声明可以避免隐式声明
一个程序里除了主函数以外的所有函数都应该进行显式声明
exit 标准函数可以立刻结束整个程序的执行
    exit(0); //结束整个程序
为了使用这个标准函数需要包含 stdlib.h 头文件
这个函数使用的时候需要一个整数类型的实际参数,这个实际参数的作用和主函数的返回值一样

【递归和递推】
C语言函数可以调用自己
这种函数叫做/* 递归函数 */
如果一个问题可以分解成几个小问题,至少一个小问题和原来的问题本质上一样,只不过稍微简单一点,这种问题就可以采用递归函数解决
编写递归函数的步骤:
1. 编写语句解决分组后的每个小问题(假设递归函数已经可以使用)
2. 在递归函数的开头编写分支处理不可分解的情况(这个分支必须可以让函数结束 return;)
使用递归函数解决问题的思路叫做/* 递归 */
使用循环解决同样问题的思路叫做/* 递推 */

【练习1】
计算从1开始到某个正整数为止,中间所有整数的和
<思想>
递归函数解决
/*代码*/
#include <stdio.h>int add(int i){     if(i == 1){         return 1;    }       return add(i-1) + i;}int main(){    int num = 0;    printf("请输入一个整数,自动求和:");    scanf("%d", &num);    int sum = add(num);    printf("求和结果为:%d\n", sum);    return 0;}

【练习2】
1 1 2 3 5 8 13 21 ...
0 1 2 3 4 5 6  7  ...
编写函数根据编号计算出对应的数字并传递给调用函数(菲波那切数列)
<思想>
递归函数解决
/*代码*/        //效率低
#include <stdio.h>int fei(int num){    if(num <= 1){        return 1;    }       return fei(num-1) + fei(num-2);}int main(){    int num = 0;    printf("请输入编号:");    scanf("%d", &num);    printf("结果为:%d\n", fei(num));    return 0;}

/*代码*/        //效率高,数组+三种变量声明的方法提高效率
#include <stdio.h>int arr[50] = {0};  //方法2int fei(int num){ //int fei(int arr[], int size, int num) 方法3    //static int arr[50] = {0};  方法1    if(num <= 1){         return 1;    }       if(!arr[num - 2] || !arr[num - 1]){        arr[num - 2] = fei(num - 2);         arr[num - 1] = fei(num - 1);     }       return arr[num-2] + arr[num-1];}int main(){    //int arr[50] = {0}; //方法3    int num = 0;    printf("请输入编号:");    scanf("%d", &num);    printf("结果为:%d\n", fei(num)); //fei(arr, 50, num)    return 0;}

0 1
原创粉丝点击