C语言学习与总结---第七章:函数 [02]

函数 [02]

  • 7.6 函数递归
    • 7.6.1 递归函数与递归出口
    • 7.6.2 一些递归的例子
  • 7.7 数组作为函数参数
    • 7.7.1 数组元素作函数实参
    • 7.7.2 数组名作函数参数
    • 7.7.3 多维数组名作函数参数
  • 7.8 局部变量和全局变量
    • 7.8.1 局部变量
  • 7.9 内部函数和外部函数

7.6 函数递归

7.6.1 递归函数与递归出口

一个函数直接或间接地调用该函数本身,称为函数的递归调用。例如

int f(int x)  
{  
int y, z;  
z = f(y);       //函数f调用了自己本身  
return (2 * z); 
}

递归条件:
(1)递归出口:要存在一个递归结束的条件,满足该条件时不再继续;
(2)每次递归调用都是在逐渐接近递归出口;

7.6.2 一些递归的例子

(1)有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2个人大2岁。问第2个人,说比第1个人大2岁。最后问第1个人,他说是10岁。请问第5个人多大?
分析
age(5) = age(4) + 2
age(4) = age(3) + 2
age(3) = age(2) + 2
age(2) = age(1) + 2
age(1) = 10
可以用数学公式表述如下:
a g e ( n ) = { 10 , n = 1 a g e ( n − 1 ) , n > 1 age(n)=\begin{cases} 10,&n=1\\ age(n-1),&n>1 \end{cases} age(n)={10,age(n1),n=1n>1
C语言学习与总结---第七章:函数 [02]_第1张图片

#include  
int age(int n);  
int main()  
{  
    printf("第5个人的年龄是: %d\n", age(5));  
    return 0;  
}  
int age(int n)  
{  
    int a;  
    if (n == 1) a = 10;  
    else a = age(n - 1) + 2;  
    return a;  
} 

整个程序的运行过程:
在这里插入图片描述
(2)求阶乘n!

#include  
int factorial(int n);  
int main()  
{  
    int N;  
    scanf_s("%d", &N);  
    printf("%d", factorial(N));  
    return 0;  
}  
int factorial(int n)  
{  
    if (n == 0) return 1;  
    else  
        return n * factorial(n - 1);  
}

例如求5!
在这里插入图片描述
(3)汉诺塔(Tower of Hanoi)问题
传说印度的主神梵天做了一个梵塔,即汉诺塔,它是在黄铜板上插3根宝石针,其中一根针上从上到下按从小到大的顺序串上64个金片。梵天要求僧侣们轮流把金片在3根宝石针之间移来移去,规定每次只能移动一个金片,且不许将大金片压在小金片之上,并预言若这64个金片全部移到另一根宝石针上时,世界将在一生霹雳之中毁灭,而梵塔、庙宇和众生也都将同归于尽。要求编程序打印出移动的步骤。
C语言学习与总结---第七章:函数 [02]_第2张图片
C语言学习与总结---第七章:函数 [02]_第3张图片
C语言学习与总结---第七章:函数 [02]_第4张图片
C语言学习与总结---第七章:函数 [02]_第5张图片
C语言学习与总结---第七章:函数 [02]_第6张图片
将n个金片从1号针移动到3号针可以分为3步:
(1)将1号针上n-1个金片借助3号针先移到2号针上;
(2)把1号针上剩下的一个金片移到3号针上;
(3)将n-1个金片从2号针借助于1号针移到3号针上。

#include  
void move(int n, int star, int goal, int temp);  
int main()  
{  
    int n = 64, star = 1, goal = 3, temp = 2;  
    move(n, star, goal, temp);  
    return 0;  
}  
void move(int n, int star, int goal, int temp)  
{  
    if (n >= 1)  
    {  
        move(n - 1, star, temp, goal);  
        printf("move disk %d from %d to %d.\n", n, star, goal);  
        move(n - 1, temp, goal, star);  
    } 

运行结果
……
P.S.这个结果我等了10分钟也没等到,梵天预言这个结果出来世界将毁灭hhhhh

用clock()函数获得程序的运行时间:

#include  
#include  
void move(int n, int star, int goal, int temp);  
clock_t star, stop;   //clock_t是clock()函数返回的变量类型  
double duration;      //记录被测函数运行时间,以秒为单位  
int main()  
{/*不在测试范围内的语句写在clock()调用之前*/  
    int n = 15, star = 1, goal = 3, temp = 2;  
    star = clock();    //开始计时  
    move(n, star, goal, temp);   //把被测函数加在这里  
    stop = clock();    //结束计时  
    duration = ((double)(stop - star)) / CLK_TCK;  //计算运行时间  
    printf("运行时间: %8.4f", duration);  
    /*注意CLK_TCK是机器时钟每秒钟所走的时钟打点数*/  
    /*其他不在测试范围内的语句写在后面,如duration的值*/  
    return 0;  
}

当n=15时,部分运行结果及运行时间:
C语言学习与总结---第七章:函数 [02]_第7张图片
当n=20的时候运行时间就要好久好久……

7.7 数组作为函数参数

7.7.1 数组元素作函数实参

数组元素作为函数实参,其用法与变量相同
例,输入10个数,要求输出其中值最大的元素和该数是第几个数

#include  
int max(int x, int y);  
int main()  
{  
    int a[10], i, m, n;  
    printf("Please enter 10 integers: \n");  
    for (i = 0; i < 10; i++)  
        scanf_s("%d", &a[i]);  
    printf("\n");  
    for (i = 1, m = a[0], n = 0; i < 10; i++)  
    {  
        if (max(m, a[i]) > m)  
        {  
            m = a[i];  
            n = i;  
        }  
    }  
    printf("The largest number is %d.\nit is %dth number in array.", m, n);  
    return 0;  
}  
int max(int x, int y)  
{  
    return x > y ? x : y;  
} 

运行结果
C语言学习与总结---第七章:函数 [02]_第8张图片

7.7.2 数组名作函数参数

可以用数组名作函数实参,此时形参应当用数组名或用指针变量

例,有一个一维数组score,内放10个学生成绩,求平均成绩

#include  
float average(float s[10]);  
int main()  
{  
    float score[10], aver;  
    printf("Please enter 10 scores(0-150):\n");  
    for (int i = 0; i < 10; i++)  
        scanf_s("%f", &score[i]);  
    printf("\n");  
    printf("The average score is %5.2f.\n", average(score));  
    return 0;  
}  
float average(float s[10])  
{  
    float sum = 0.0, aver;  
    for (int i = 0; i < 10; i++)  
        sum += s[i];  
    aver = sum / 10;  
    return aver;  
}  

运行结果
C语言学习与总结---第七章:函数 [02]_第9张图片
注意
(1)用数组名作函数实参,应该在主调函数和被调函数中分别定义数组。
(2)实参数组和形参数组类型应该一致。
(3)形参数组可以不指定大小,因为C语言编译系统对形参数组大小不作检查。
(4)可以另设一个形参,传递数组的大小

#include  
int main()  
{  
    float average(float array[], int n);  //形参数组可以不指定数组长度  
    float score1[5] = { 98.5, 97, 91.5, 60, 55 };  
    float score2[10] = { 67.5, 89.5, 99, 69.5, 77, 89.5,76.5, 54, 60, 99.5 };  
    printf("the average of class A is % 6.2f\n",average(score1, 5));   //实参为数组名  
    printf("the average of class B is % 6.2f\n",average(score2, 10));  
    return 0;  
}  
float average(float array[], int n)  
{  
    int i;  
    float aver, sum = array[0];  
    for (i = 1; i < n; i++)  
        sum += array[i];  
    aver = sum / n;  
    return aver;  
}  

(5)数组名作函数实参时,不是把数组元素的值传递给形参数组,而是把实参数组的首地址传递给形参数组,这样两个数组共占同一段内存单元。形参数组中各元素的值如发生变化,会使实参数组的值同时发生变化。这与变量作函数参数的情况不同,务请注意

例,用选择法对数组中10个整数按由小到大排序

选择法是先将10个数中最小的数与a[0] 对换;再将a[1]到a[9]中最小的数与a[1]对换……。每比较一轮,找出一个未经排序的数中最小的一个,共比较9轮。

#include  
void sort(int a[], int n);  
int main()  
{  
    int a[10], i;  
    printf("Please enter array:\n");  
    for (i = 0; i < 10; i++)  
        scanf_s("%d", &a[i]);  
    printf("\nThe sorted array: \n");  
    sort(a, 10);  
    for (i = 0; i < 10; i++)  
        printf("%5d", a[i]);  
    printf("\n");  
    return 0;  
}  
void sort(int a[], int n)  
{  
    int i, j, k, t;  
    for (i = 0; i < n - 1; i++)  
    {  
        k = i;  
        for (j = i + 1; j < n; j++)  
            if (a[j] < a[k]) k = j;  
        t = a[k];  
        a[k] = a[i];  
        a[i] = t;  
    }  
}  

运行结果
C语言学习与总结---第七章:函数 [02]_第10张图片

7.7.3 多维数组名作函数参数

可以用多维数组名作为函数的实参和形参,定义形参数组时可以省略第一维的大小说明,但不能省略第二维以及其它高维的大小说明。
正确:int array[3][10]; int array[][10];
错误:int array[3][]; int array[][];

例,有一个3行4列的矩阵,求所有元素中的最大值

#include  
int max(int a[][4]);  
int main()  
{  
    int a[3][4] = { {3,5,2,6},{10,2,65,44},{21,1,34,11} };  
    printf("The maximun value is: %d.\n", max(a));  
    return 0;  
}  
int max(int a[][4])  
{  
    int m = a[0][0], i, j;  
    for (i = 0; i < 3; i++)  
        for (j = 0; j < 4; j++)  
            if (a[i][j] > m)m = a[i][j];  
    return m;  
} 

7.8 局部变量和全局变量

7.8.1 局部变量

局部变量:在一个函数内部定义的变量,只在本函数范围内有效,即只有在本函数内才能使用,在此函数以外是不能使用

全局变量:在函数之外定义的变量,从定义变量的位置开始到本源文件结束都有效,可以为本文件中的函数共用

例如,在汉诺塔问题的程序中,

#include  
#include  
void move(int n, int star, int goal, int temp);  
clock_t star, stop;  //star,stop是全局变量  
double duration;     //duration是全局变量  
int main()  
{  
    int n = 15, star = 1, goal = 3, temp = 2;     
    //n,star,goal,temp都是局部变量,只在main函数的范围内有效  
    star = clock();    
    move(n, star, goal, temp);    
    stop = clock();     
    duration = ((double)(stop - star)) / CLK_TCK;   
    printf("运行时间: %8.4f", duration);  
    return 0;  
}  
void move(int n, int star, int goal, int temp)  
{  
    if (n >= 1)  
    {  
        move(n - 1, star, temp, goal);  
        printf("move disk %d from %d to %d.\n", n, star, goal);  
        move(n - 1, temp, goal, star);  
    }  
} 

例,一维数组中存放了10个学生成绩,求出平均分、最高分和最低分

#include  
float average(float array[], int n);  
float Max = 0, Min = 0;   //全局变量   
int main()  
{   
    float ave, score[10];   //局部变量,在main函数内有效  
    for (int i = 0; i < 10; i++)  
        scanf_s("%f", &score[i]);  
    ave = average(score, 10);  
    printf("max = %6.2f\nmin = %6.2f\naverage = %6.2f\n", Max, Min, ave);  
    return 0;  
}  
float average(float array[], int n)  
{  
    float aver, sum = array[0];    //局部变量,在average函数内有效  
    Max = Min = array[0];  
    for (int i = 1; i < n; i++)  
    {  
        if (array[i] > Max)  Max = array[i];  
        else if (array[i] < Min)  Min = array[i];  
        sum = sum + array[i];  
    }  
    aver = sum / n;  
    return aver;  
} 

运行结果
C语言学习与总结---第七章:函数 [02]_第11张图片
再看一个例子

#include   
int n = 10;      //全局变量  
void func1()   
{  
    int n = 20;  //局部变量  
    printf("input1 n: %d\n", n);  
}  
void func2(int n)  
{  
    printf("input2 n: %d\n", n);  
}  
void func3()   
{  
    printf("input3 n: %d\n", n);  
}  
int main()  
{  
    int n = 30;  //局部变量  
    func1();  
    func2(n);  
    func3();  
    //代码块由{}包围  
    {  
        int n = 40;  //局部变量,作用域是距离它最近的{}  
        printf("block n: %d\n", n);  
    }  
    printf("main n: %d\n", n);  
    return 0;  
}  

运行结果
C语言学习与总结---第七章:函数 [02]_第12张图片
代码中虽然定义了多个同名变量 n,但它们的作用域不同,在内存中的位置(地址)也不同,所以是相互独立的变量,互不影响,不会产生重复定义错误。

(1)全局变量全部存放在静态存储区,在程序开始执行时给全局变量分配存储区,程序行完毕就释放。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放;

(2)局部变量,如不专门声明为static存储类型,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间;

(3)当全局变量和局部变量同名时,在局部范围内全局变量被“屏蔽”,不再起作用。或者说,变量的使用遵循就近原则,如果在当前作用域中存在同名变量,就不会向更大的作用域中去寻找变量。对于 func1(),输出结果为 20,显然使用的是函数内部的 n,而不是外部的 n;func2() 也是相同的情况;

(4)func3() 输出 10,使用的是全局变量,因为在 func3() 函数中不存在局部变量 n,所以编译器只能到函数外部,也就是全局作用域中去寻找变量 n;

(5)由{}包围的代码块也拥有独立的作用域,printf() 使用它自己内部的变量 n,输加粗样式出 40;

(6)C语言规定,只能从小的作用域向大的作用域中去寻找变量,而不能反过来使用更小的作用域中的变量。对于 main() 函数,即使代码块中的 n 离输出语句更近,但它仍然会使用 main() 函数开头定义的 n,所以输出结果是 30。

7.9 内部函数和外部函数

根据函数能否被其他源文件调用,将函数区分为内部函数外部函数

内部函数:只能被本文件中其他函数所调用。定义内部函数的一般形式:
static 类型标识符 函数名(形参表列)
例如:

static int fun(int a, int b)

外部函数:可供本文件和其它文件的函数调用定义外部函数的一般形式:
extern 类型标识符 函数名(形参表列)
extern可以缺省
例如:

extern int fun(int a, int b)

例,有一个字符串,输入一个字符,要求在字符串中删去该字符。用外部函数实现

/*file1.c*/  
#include  
int main()  
{  
    extern void enter_string(char str[]);     //外部函数  
    extern void delete_string(char str[], char ch);   //外部函数  
    extern void print_string(char str[]);     //外部函数  
    char c, str[6];  
    enter_string(str);  
    scanf_s("%c", &c);  
    delete_string(str, c);  
    print_string(str);  
    return 0;  
}  
/*file2.c*/  
#include   
void enter_string(char str[5])       //定义外部函数   
{  
    gets_s(str,6);                       // 向字符数组输入字符串   
}  
  
/*file3.c*/  
void delete_string(char str[], char ch)  //定义外部函数   
{  
    int i, j;  
    for (i = j = 0; str[i] != '\0'; i++)  
        if (str[i] != ch)  
            str[j++] = str[i];  
    str[j] = '\0a';  
}  
/*file4.c*/  
#include   
void print_string(char str[])         //定义外部函数   
{  
    printf("%s\n", str);               //输出字符串   
} 

运行结果
C语言学习与总结---第七章:函数 [02]_第13张图片
说明
在文件2中定义了函数enter_string()用来输入一个字符串;
在文件3定义了函数delete_string()作用是把一个字符串中的某个字符删除;
文件4定义了函数print_string()输出一个字符串;
文件1调用了文件2,3, 4中定义的外部函数.

你可能感兴趣的:(C语言,函数与递归)