第七章 用函数实现模块化程序设计

函数、递推、递归

1、需要输出以下的结果,用函数调用实现:

***********************

How do you do!

***********************

#include<stdio.h>
int main()
{
    void print_star();                // 声明函数
    void print_message();            // 声明函数
    print_star();                    // 调用函数
    print_message();
    print_star();                    // 调用函数
    return 0;
}
void print_star()                    // 定义函数
{
    printf("***********************\n");
}
void print_message()
{
    printf("How do you do !\n");
}

2、输入两个整数,求输出二者中的大者。要求在主函数中输入两个整数,用一个函数max求出其中的大者,并在主函数中输出此值。

#include<stdio.h>
int main()
{
    int max(int x,int y);
    int a,b,c;
    printf("Please enter two numbers:\n");
    scanf("%d,%d",&a,&b);
    c=max(a,b);
    printf("The Max number is:%d\n",c);
}
int max(int x,int y)
{
    int z;
    if(x>y)
    {
        z=x;
    }else
    {
        z=y;
    }return(z);
}

  max函数也可以写成这样

int max(int x,int y)
{
  if(x>y) return(x);
  else return (y);
}

3、输入4个正整数,找出其中最大的数。用一个函数来实现。

#include<stdio.h>
int main()
{
    int max_4(int,int,int,int);                    // 对比较4个数的函数进行声明
    int a,b,c,d,m;
    printf("Please enter two numbers:\n");
    scanf("%d,%d,%d,%d",&a,&b,&c,&d);
    m=max_4(a,b,c,d);                            // 调用比较4个数的函数
    printf("The Max number is:%d\n",m);
}
    
int max_4(int a,int b,int c,int d)                // 定义比较4个的函数
{
    int max(int ,int );                            // 声明比较2个数的函数
    int m;
    m=max(a,b);                                    // 调用比较2个数的函数                            
    m=max(m,c);                                    // 第3(c)个值和上一次比较出来的结果再进行比较
    m=max(m,d);                                    // 第4(d)个值和上一次比较出来的结果再进行比较
    return m;
}

int max(int x,int y)                            // 定义比较2个的函数
{
    if(x>y) return x;
    else 
        return y;
}

 4、有一个学生坐在一起,问第5个学生多少岁?他说比第4个学生大2岁。问第4个学生岁数,他说比第3个学生大2岁。问第3个学生岁数,他说比第2个学生大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);

数学公式 :age(n) = 10      (n=1)

          = age(n-1)+2  (n>1)

#include<stdio.h>
int age(int n)
{
    if(n==1)
    {
        return 10;
    }
    else
    {
        return age(n-1)+2;
    }
}

int main()
{    
    printf("%d\n",age(5));
}

5、分别用递推、递归方法实现n!

(1)递推方法

#include<stdio.h>
int main()
{  
    int n;
    int f(int n);
    printf("Please input a integer number: ");
    scanf("%d",&n);                // 用户输入n
    long sum=0;
    sum=f(n);                    // sum用来存储n!值
    printf("%d!=%ld\n",n,sum);
    return 0;
}
int f(int n)
{   int i;
    long sum=1;
    for(i=1;i<=n;i++)            // 必须从1开始,for循环的长度为阶乘数 n
    {
        sum=sum*i;                // 思想是 前几项的积再乘以后面的项  如:1*2=2*3
    }
    return sum;
}

(2)递归方法

递归公式 :n! = 1        (n=1)

        = n.(n-1) !  (n>1)

#include<stdio.h>
int main()
{  
    int n;
    int f(int n);
    printf("Please input a integer number: ");
    scanf("%d",&n);                // 用户输入n
    long sum=0;                    // 长类型接受超过6位的数值
    sum=f(n);                    // sum用来存储n!值
    printf("%d!=%ld\n",n,sum);
    return 0;
}
int f(int n)
{
    if(n==1)
        return 1;
    else
        return f(n-1)*n;        // 思路是 n是一个固定的值,分别往回乘,n-1又继续调用自己,每次减n每次1,直到n=1
}

递归注意点:  (1)递归必须有递归关系式

        (2)递归必有递归出口

递归两个过程:(1)回溯;         

       (2)递推。

什么样的问题可以用递归呢?

(1)问题是否能转化为同一种方法解决的子问题;

(2)子问题的规模比原问题的规模要小;

(3)必有递归结束条件。

数组元素、数组名作为函数参数

1、用数组元素作为函数实参

  1)假设A、B队各有10个人,计算A队高于、低于、等于B队成绩的人数

#include<stdio.h>
int main()
{  
    int higher(int,int);
    int a[10]={71,81,86,75,80,56,50,64,65,90};
    int b[10]={70,80,85,75,85,56,50,60,60,80};
    int i,j,g=0,d=0,e=0;
    printf("A    B\n");
    for(i=0;i<10;i++)
    {
        if(higher(a[i],b[i])==1)
        {
            g++;
        }
        else if(higher(a[i],b[i])==-1)
        {
            d++;
        }
        else
        {
            e++;
        }
        printf("%d\t",a[i]);
        printf("%d\t",b[i]);
        printf("\n");
    }
    printf("\n");
    printf("A组有 %d 学生的成绩高于B组\nA组有 %d 学生的成绩低于B组\nA组有 %d 学生的成绩等于B组\n",g,d,e);    
    if(g>=6)
    {
        printf("\nA Team win!\n\n");
    }
    return 0;
}

higher(int x,int y)
{
    int flag;
    if(x>y)
        flag=1;
    else if(x<y)
        flag=-1;
    else 
        flag=0;
    return flag;
}

 2、用数组名作为函数实参

   1)有10个学生成绩,用一个函数求全体学生的平均成绩

#include<stdio.h>
int main()
{  
    float average(float array[]);
    float score[10],aver;
    int i;
    printf("input 10 scores:\n");
    for(i=0;i<10;i++)
    {
        scanf("%f",&score[i]);
    }
    aver=average(score);                    // 用数组名调用函数
    printf("average score is %5.2f\n",aver);
    return 0;
}
float average(float array[])                
{
    int i;
    float aver,sum=array[0];                
    for(i=1;i<10;i++)                    // 遍历数组元素
    {
        sum=sum+array[i];                // 每次循环累加数组的元素
    }
    aver=sum/10;                        // 最后将总累计数除10
    return (aver);                      // 将aver带回主函数
}   

  注意几点:  (1)数组名作为函数参数,在调用函数时并不开辟存放形参数组的空间;

         (2)数组名代表的数组的首元素的地址;

         (3)上例子中,形参数组首元素array[0]和实参数组首元素score[0]具有同一地址,共占同一存储空间;

         (4)定义函数时数组大小的不做检查,即可以不定义长度。

  2)有两个班,学生数不同,编写一函数,用来分别求各班的平均成绩。

#include<stdio.h>
int main()
{  
    float average(float array[],int );
    float score_1[5]={80,80,70,70,70};                        // 有5个学生的成绩
    float score_2[10]={80,80,70,70,70,90,80,80,80,70};        // 有10个学生的成绩
    printf("The average of score_1 is %5.2f\n",average(score_1,5));
    printf("The average of score_2 is %5.2f\n",average(score_2,10));
    return 0;
}
float average(float array[],int n)                        // n来传递数组的长度            
{
    int i;
    float aver,sum=array[0];            // 累加是从第一个元素开始的        
    for(i=1;i<n;i++)                    // 遍历数组元素
    {
        sum=sum+array[i];                // 每次循环累加数组的元素
    }
    aver=sum/n;                            // 最后将总累计数除n
    return (aver);                        // 将aver带回主函数
}    

3、用一个函数实现用 选择法 对10个整数按升序排列

 

#include<stdio.h>
int main()
{  
    void sort(int array[],int n);            // 对sort函数声明    因为只是排序不需要返回值
    int a[10]={13,43,21,45,32,16,47,12,48,65};
    int i,j;
    printf("The sorted array:\n");
    sort(a,10);
    for(i=0;i<10;i++)
    {
        printf("%d\n",a[i]);
    }
    return 0;
}

void sort(int array[],int n)
{
    int i,j,k,t;                                    // t存放临时
    for(i=0;i<n-1;i++)                              // n-1是因为有10元素,所以比较9次
    {
        k=i;                                        // k用来存放当前“最小”的元素的序号
        for(j=i+1;j<n;j++)                          // i+1是表示后一项,每次都会后移一位
        {                                            
            if(array[j]<array[k])                   // 如果前一项k比后一项j大,即后面项的比前面的项要小
            {
                k=j;                                // 那么把小项(即后面)的下标j存储在k中
            }
        }
        t=array[k];array[k]=array[i];array[i]=t;    // t存储下标k的值(即小的值),k的位置(后面的位置)用比较大的来代替,i的位置(即前面的位置)存储小值t
    }
} 

 

注意点:  (1)数组名作为函数参数时传递的是首地址,即地址传递方式;

      (2)按“值”传递方式需要开辟存储单元,而按“地址”传递方式需要开辟内存单元,即形参和实参共占同一段内存单元,地址是共享的;

      (3)由于地址共享,可以通过改变形参数组的值来改变实参数组的值。

 

4、有4个学生,5门课的成绩,设计一个函数,用来求出其中最高的成绩

#include <stdio.h>
int main()
{
    float highest_score(float array[][5]);        // 一维大小可以省略
    float score[4][5]={{61,65,76,77,89},{81,70,80,78,60},{81,75,88,78,66},{82,71,85,79,66}};
    printf("The highest score is %5.2f\n",highest_score(score));
    return 0;
}

float highest_score(float array[4][5])
{
    int i,j;
    float max;
    max=array[0][0];
    for(i=0;i<4;i++)
    {
        for(j=0;j<5;j++)
        {
            if(array[i][j]>max)
            {
            max=array[i][j];
            }
        }
    }
    return max;
}


变量的的作用域和生存期

1、局部变量和全局变量

  1)局部变量

    ① 在一个函数内部生效,如主函数中的 int i,j,k;

    ② 不同函数的变量名是可以相同的,互不干扰,互不影响;

    ③ 形参也是局部变量;

    ④ 复合语句中,可以定义变量,只在本复合语句中生效;

  2)全局变量

    ① 函数之外定义的变量,称为“全局变量”;

    ② 作用范围是本源文件开始到结束;

    ③ 如果同一个源文件中,全局变量和局部变量同名,局部变量优先有效。

  例1:有4个学生,5门课的成绩,输出其最高成绩以及属于第几个学生、第几门课程;

#include <stdio.h>
int Row,Column;                                    // 定义全局变量Row和Column
int main()
{
    float highest_score(float array[4][5]);        // 一维大小可以省略
    float score[4][5]={{61,65,76,77,89},{81,70,80,78,90},{81,75,88,78,66},{82,71,85,79,66}};
    printf("The highest score is %5.2f\nRow:%d\nColumn:%d\n",highest_score(score),Row,Column);
    return 0;
}

float highest_score(float array[4][5])
{
    int i,j;
    float max;
    max=array[0][0];
    for(i=0;i<4;i++)
    {
        for(j=0;j<5;j++)
        {
            if(array[i][j]>max)
            {
            max=array[i][j];
            Row=i;
            Column=j;
            }
        }
    }
    return max;
}
View Code

 

注意几点:  1)一个函数可以改变一个全局变量,该文件的其他函数也可以使用这个以改变的变量;

       2)全局变量相当于是函数之间的联系通道;

       3)全局变量一般用首个字母大写来定义;

       4)全局变量整个过程都占用存储单元。

 

2、变量的存储方式和生存期

  变量两个属性:作用域、生存期

  变量存储方式:静态存储方式、动态存储方式。静态存储方式由静态存储区分配存储空间,动态存储方式由动态存储区分配存储空间。

  区别:  静态存储方式,执行过程中占据固定存储单元,执行完释放,例如:全局变量;

       函数中定义的变量是分配动态存储空间的,执行完释放,如:局部变量。

  变量和函数两个属性:数据类型、数据存储类别:

    ① auto——自动变量

      函数调用时、复合语句中都是此类别,执行完自动释放空间,int和绝大多数都是默认是“自动存储类别”。

    ② static——静态变量

      函数调用后,不释放存储单元,继续保持原值,下次调用函数,改值还是上次结束后的值。

#include <stdio.h>
int main()
{
    int fac(int n);
    int i;
    for(i=1;i<=5;i++)                    // 先后调用5次fac函数
    {
        printf("%d!=%d\n",i,fac(i));    // 输出每次i!的值
    }
    return 0;
}

int fac(int n)
{
    static int f=1;                        // 保留上次执行结束后的值
    f=f*n;                                // 在上面基础上继续乘n
    return f;                            // 返回的是n!的值
}
View Code

 

      静态局部变量注意点:  1)静态局部变量只赋一次初值,再次执行函数的时候,初值是上次执行函数结束的值。自动变量每次执行都重新赋初值。

                  2)静态局部变量不赋初值,编译时自动赋初值“0”(对数值型变量)或者空字符(对字符变量);

                  3)静态局部变量,不能给其他函数引用;

 

      静态外部变量:定义外部变量前加一个 static 声明,只作用于本文件中,不能给其他文件引用。

    ③ register——寄存器变量

       相对于不太频繁,但是又要经常用到的数据,可以使用寄存器变量,来提高执行效率。现在多数系统都自动将这些变量存放在寄存器中。

       形式:register int f ;

    ④ extern——外部变量

      外部变量的作用是当前文件中的变量的作用域扩展到其他文件中,例如:在A文件中定义了NUM变量,那B文件中要使用A文件中的NUM就要在B文件的头部 加上extern NUM,这样才使扩展了NUM可以在B中的使用。

 

  小结:对数据的定义有个属性分别是 数据类型存储类别

  

 

 

 

内部函数和外部函数

1、内部函数

  定义:只能被本文件中的其他函数所调用的函数,称为内部函数,也称为静态函数,形式如下:

  satatic 类型标识符 函数名(形参表);

static int fun(int a,int b);

2、外部函数

  定义:函数首部的最左端加关键字extern,此类函数可以被其他文件调用,如果定义时没写extern,则隐含为外部函数:

extern int fun(int a,int b);

  例 1:有一个字符串,内有若干个字符,现输入一个字符,如果字符串中包含此字符,则把它删除。用外部函数实现。

// file1.c (文件1)
#include <stdio.h>
int main()
{                // 声明需要调用其他文件的函数
    extern void enter_string(char str[]);            
    extern void delete_string(char str[],char ch);
    extern void print_string(char str[]);
    char c;
    char str[80];
    printf("Please enter a string\n");
    enter_string(str);                            // 调用输入函数,用户输入一个字符串
    printf("Please enter a char which you want to delete:\n");
    scanf("%c",&c);                                // 用户输入一个需要删除的字符
    delete_string(str,c);                        // 调用删除函数,给delet函数传入两个实参
    print_string(str);                            // 调用打印函数,输入删除后的字符串
    return 0;
}

// file2.c(文件2)
#include <stdio.h>
void enter_string(char str[80])
{
    gets(str);                                    // 向字符数组输入字符串
}

// file3.c(文件3)
#include <stdio.h>
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]='\0';
}


// file4.c(文件4)
#include <stdio.h>
void print_string(char str[])
{
    printf("After delete string:\n");
    printf("%s\n",str);
}
View Code

  分析:从str[0]开始逐个检查数组元素是否与指定要删除的字符相同。若不同,就留在数组中;若相同就不保留。可以看到,str[0]不等于要删除的字符,应保留,所以使str[0]赋给str[0],即保留原值,同理,把str[1]赋给str[1],str[2]赋给str[2],str[3] 赋给str[3],str[4]不保留,把str[5]赋给str[4]......即用原来的str[5]的值取代了str[4]的原值。用变量i作为循环变量,检查str数组中各个元素,用 j 来累计未被删除的字符。当然可以设两个数组,把不删除的字符一 一赋给新数组。但本程序只用一个数组,只把不被删除的字符保留在原数组中。由于 i 总是大于或等于 j,因此最后保留下来的字符不会覆盖未被检测处理的字符。最后将 结束符 \0 复制到被保留的字符后面。

 

提高部分

1、递归的典型例子——Hanoi 汉诺塔问题

  1)课本代码实现:

 

#include <stdio.h>
int main()
{
    void hanoi(int n,char x,char y,char z);            // 对函数hanoi声明

    int m;
    printf("Please input the number of dishes:");
    scanf("%d",&m);                                    // 输入需要移动盘的数量
    printf("The step to moving %d dishes:\n",m);    
    hanoi(m,'A','B','C');                            // 执行移动盘子,ABC为三条柱子
    return 0;
}

void hanoi(int n,char x,char y,char z)
{    
    void move(char a,char b);                        // 定义move函数
    if(n==1)
    {
        move(x,z);                        
    }else
    {
        hanoi(n-1,x,z,y);                            // 递归调用下一个和尚移动的n-1盘子
        move(x,z);                                    // 自己移动一个盘子
        hanoi(n-1,y,x,z);                            // 递归调用下一个和尚移动的n-1盘子
    }
}

void move(char a,char b)
{
    printf("%c--->%c\n",a,b);
}

 

  2)简化实现:

#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);        // A、C的值是不停更新的
        hanoi(n-1,B,A,C);
    }
}

int main()
{
    hanoi(3,'A','B','C');
    return 0;
}

 

posted @ 2021-03-09 14:54  一个特立独行的猪  阅读(633)  评论(0)    收藏  举报