C语言---利用函数实现模块化程序设计

        在C语言中,所有的程序设计都必须含有一个主函数(main函数),对于简单问题的程序设计,我们可以将所有的语句、功能都写在主函数中,但是对于复杂问题,如果将所有的语句都写在主函数中,就会使主函数变得十分的复杂,这使得阅读与运行变得困难。因此,人们想到用组装的方式进行简化程序设计,将不同功能的函数分开表示,在使用时在进行调用,这样就可以将程序设计分模块进行。这也就是程序模块化的思路。

        函数就是功能,每一个函数用来实现一种特定的功能,函数的名字可以直接反应函数的功能。一个C程序可以由一个主函数和若干个其他函数构成,由主函数调用其他函数,其他函数之间也可以进行调用。

        函数分为:库函数与自定义函数;

        其中库函数为:c语言中自定义的函数;自定义函数为:用户根据自身要求所设计的函数;

目录

一、库函数

二、自定义函数

三、函数的调用

四、 函数调用时的数据传递

五、函数调用的练习

 六、函数的嵌套使用

 七、函数的声明

八、函数的递归

九、数组作为函数的参数


一、库函数

        常见的库函数包括:打印函数--printf、输入函数---scanf、求字符串长度函数---strlen、比较字符串函数---strcmp、以及复制字符串函数---strcpy等等;

举例:复制字符串函数---strcpy

#include <stdio.h>
int main()
{
    char arr1[]={"dhjas"};
    char arr2[20]={0};

    strcpy(arr2,arr1);
    printf("%s",arr2);
    return 0;
}

结果:

         库函数的使用,必须包含相应的头文件;具体的库函数使用,可以查阅下面的两个网站。

         学会查找工具的使用:

www.cplusplus.comhttp://www.cplusplus.com

www.cppreference.comhttp://www.cppreference.com

二、自定义函数

        自定义函数,即按照自己的要求来设置函数的功能,自定义函数和库函数一样,有函数名、返回值类型和函数参数;定义函数时应包括以下内容:

1、指定函数的名字,一般函数的名字就会体现出函数的功能,函数的名字用于后面对函数的调用。

2、指定函数的类型,即函数返回值类型,如果不需要返回值函数的类型定义为void。

3、函数的参数(名字及类型)以便函数进行调用时,对其进行数据的传递。

4、指定函数的功能,即定义这个函数是干什么的,这也是最重要的部分;

        函数的定义方法:

        自定义函数在进行调用前必须要进行定义

        1、定义无参函数

        函数中不含参数的传递:

        类型名  函数名(void)

        {

        函数体//函数体的内容即为函数的功能;

        }

        2、定义有参函数

        类型名  函数名(参数列表)

        {

        函数体//函数体的内容即为函数的功能;

        }

        3、定义空函数

类型名  函数名 ()

        函数体是空的,调用此函数时,什么工作也不做,没有实际作用。常常用于函数暂时还未写好,先用空函数占一个位置,等以后扩充程序时在进行编写。

三、函数的调用

         定义函数的目的就是为了调用函数,以得到预期的效果。函数调用形式为:

         函数名(实参列表)

        如果是调用无参函数,实参列表可以没有,但是括号不能省略,如果实参列表包含多个实参,则各个实参用逗号隔开。

例如:输入两个数,输出较大的一个

#include<stdio.h>
int get_max(int x,int y)//函数的定义
 {
    return (x>y?x:y);//函数的功能,返回较大的一个
 }
int main()
{
    int a,b;
    int c;
    printf("请输出两个整数\n");
    scanf("%d %d",&a,&b);
    c=get_max(a,b);//函数的调用
    printf("%d",c);
    return 0;
}

结果:

四、 函数调用时的数据传递

1、形式参数与实际参数

        在进行函数调用时,主调函数与被调函数之间有数据传递关系,在函数定义时,我们讲到函数名后面括号中的变量名称称为“形式参数”在主调函数中调用一个函数时,函数后面括号中的参数称为“实际参数”,实际参数可以是常量、变量以及表达式;

2、实参和形参之间的数据传递

        在函数调用过程中,系统会把实参的值传递给别调用函数的形参。这种数据之间的传递称为虚实结合。

说明:1、在定义函数中指定的形参,在未进行函数调用时,他们并不占内存单元,只有在发生函数调用时形参才被临时分配地址;

2、将实参的值一一对应传给形参;形参与实参的名称可以相同,也可以不同;

3、通过return语句将函数值待会给主调函数;

4、函数被调完后,形参单元被释放;

5、实参可以向形参传数据,而形参不能向实参传数据。函数之间数据的传递是单向的;

例如:输入两个数字,交换两个数字

#include <stdio.h>
void wap(int x,int y)//定义交换函数,形式参量x,y
{
    int z;
    z=x;
    x=y;
    y=z;
}
int main()
{
    int a,b;
    printf("请输出两个数:");
    scanf("%d %d",&a,&b);
    printf("%d,%d\n",a,b);//交换前a,b值
    wap(a,b);//调用交换函数
    printf("%d,%d\n",a,b);//交换后a,b值
    return 0;
}

结果:

        结果并没有实现交换;a和b为实际参数;x和y为形式参量;当实参传给形参的时候,形参是实参的一份临时拷贝,对形参的修改,不会影响实参;也就是说,由于“单向传递”的“值传递”方式,形参值的改变不能使实参值的改变。 

        对上述代码实现修改,利用传递地址的方法实现两个数之间的交换。

#include <stdio.h>
void wap (int* px,int* py)//定义交换函数,形式参量为指针地址
{
    int z = *px; //指针所指向的地址内元素发生转变
    *px=*py;
    *py=z;
}
int main()
{
    int a,b;
    printf("请输出两个数:");
    scanf("%d %d",&a,&b);
    printf("%d,%d\n",a,b);
    wap(&a,&b);//函数的调用,实际参数为a,b的地址
    printf("%d,%d\n",a,b);
    return 0;
}

结果:

         那么什么地方传参数时需要传地址?什么地方传参不需要传地址?

        如果函数不改变a和b的值,只需要用到a和b的值,就不需要传递地址。

        函数的调用:

        1、传值调用

        函数的形参和实参分别占用不同内存块,对形参的修改不会影响实参;

        2、传址调用

        传址调用把地址传给函数,函数和函数外部的变量之间可能会建立联系。函数内部可以直接操作函数外部。

五、函数调用的练习

练习1、输入一个数字,判断其是否为素数

#include <stdio.h>
#include <math.h>
int wap(int x)//定义判断素数函数
    {
    int i;
    for(i=2;i<=sqrt(x);i++)
    {
        if(x%i==0)
         return 0;
    }
    return 1;
    }
int main()
{
    int a;
    printf("请输入一个数字:");
    scanf("%d",&a);
    if(wap(a))//调用函数
    {
        printf("%d是素数\n",a);
    }
    else
        printf("%d不是素数\n",a);
    return 0;

}

结果:

练习2:输入一个年份,判断其是否为闰年

#include <stdio.h>
int is_leap(int x)//定义判断闰年的函数
{
  if((x%4!=0)||(x%100==0&&x%400!=0))//判断闰年的条件,条件写法不唯一
        return 0;
  else
        return 1;
}

int main()
{
    int year;
    printf("请输入一个年份:");
    scanf("%d",&year);
    if(is_leap(year))//调用函数
        printf("%d是闰年",year);
    else
        printf("%d不是闰年",year);
    return 0;
}

 结果:

练习3: 写一个函数实现一个整形数组的二分查找;

#include<stdio.h>
int rearch(int arr[],int x,int y)//定义查找函数,形参包括数组名,输入的数字以及数组的长度
    {
     int left=0;//定义左下标
    int right=y-1;//定义右下标
    while(left<=right)//判断左下标与右下标的大小
    {
    int mid=(left+right)/2;//确定中标
    if(x>arr[mid])//判断左下标与中标的大小
        left=mid+1;
    else if(x<arr[mid])//判断左下标与中标的大小
        right=mid-1;
    else//如果两个相等,则可以确定元素在数组中的位置,并返回其位置
      return mid;
    }
    if(left>right)//如果左下标大于右下标,则说明输入数字在数组中无法找到
        return -1;
    }

int main()
{
    int arr1[]={1,2,3,4,5,6,7,8,33,45};//定义一个数组
    int i;
    printf("请输入一个数字:");
    scanf("%d",&i);
    int sz=sizeof(arr1)/sizeof(arr1[0]);//确定数字长度
    int ret=rearch(arr1,i,sz);//调用函数,实参包括数组名,输入的数字以及数组的长度
    if(ret==-1)
        printf("未找到。\n");
    else
        printf("找到了,下标为 %d",ret);
    return 0;
}

结果:

练习4:写一个函数,每调用一次这个函数,将Num的值加一;

#include<stdio.h>
int add (int x)//定义加值函数
{
    ++x;
    return x;
}
int main()
{
    int num=0;
while(num!=101)
{
   num= add(num);//调用加值函数
    printf("%d ",num);
}
    return 0;
}

结果:

 六、函数的嵌套使用

        C语言中函数的定义是互相平行的,独立的,也就是说在定义函数时,不能在一个函数内部在定义另一个函数,即函数不能嵌套定义。但是可以嵌套调用函数,即在调用一个函数过程中,又调用另外一个函数。

举例:

#include <stdio.h>
void dayin()//定义第一个调用函数
    {
    printf("haha\n");
    }
void three_dayin()//定义第二个调用函数
    {
        int i;
    for(i=0;i<3;i++)
        dayin();//调用第一个函数
    }
int main()
    {
    dayin();//调用第一个函数
    three_dayin();//调用第二个函数
    return 0;
    }

结果:

错误示范:函数的嵌套定义

#include <stdio.h>
int add(int x,int y)//对第一个函数进行定义
{
   int sub(int x,int y)//对第二个函数进行定义,但是位置在第一个函数内部
    {
    return x-y;
    }
    return x+y;
}

int main()
{
    return 0;
}

        这样的代码是没有办法通过编译的,不能在函数内部再次进行函数的定义。

        链式访问:是指依赖的是函数返回值;链式访问的前提是函数具有返回值;

例如:

#include <stdio.h>
int main()
{
    printf("%d",printf("%d",printf("%d",43)));//将最里层的printf的返回值作为第二层的打印值
                                              //将第二层的返回值作为最外层的打印值
                                              //printf的返回值取决于打印的位数
     return 0;
}

 结果:

 七、函数的声明

        在一个函数中调用另外一个函数时,具有以下要求:1、被调函数是库函数或者已经被用户自定义;2、使用库函数,应该在文件的开头包含库函数的头文件;3、如果使用自己定义的函数,二定义函数的位置在调用函数位置的后面,就应该在主调函数中对被调函数作出声明。

       函数声明作用:1、告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但具体是否存在,函数声明决定不了。2、函数声明一般出现在函数的使用前面,要满足先声明后使用。3、函数的声明一般放在头文件中;

举例1:在同一个文件中进行函数的声明与使用

        函数声明的形式:

        函数类型 函数名(参数类型1 参数名1,···参数类型n  参数名n)

        其中参数名可有可无;

#include <stdio.h>
int main()
{
    void pr();//对被调用函数的声明
    pr();//调用函数
    return 0;
}
void pr()//定义pr函数
{
    printf("xi'an");
}

结果:

举例2:在不同文件中进行函数的声明和使用

        如果程序的功能很多时,就会有多个函数模块,此时,我们习惯将函数分文件进行编写,然后将所有函数的声明都放在一个头文件中,在主函数中对头文件进行包含即可;包含头文件的意义在于,将头文件的内容copy出来。

举例:

1、main.c代码

#include <stdio.h>
#include "main.h"//包含头文件
int main()
{
    int a,b;
    printf("请输入两个数字:");
    scanf("%d%d",&a,&b);
    int sum1 = add(a,b);//调用加法函数
    printf("%d\n",sum1);
    int sum2 = sub(a,b);//调用减法函数
    printf("%d\n",sum2);
    return 0;
}

2、main.h代码

#ifndef ADD_H_INCLUDED
#define ADD_H_INCLUDED
#endif // ADD_H_INCLUDED
int add(int,int);//对加法函数的声明
int sub(int,int);//对减法函数的声明

3、add.c代码

 int add(int x,int y)
    {
        return x+y;
    }

4、sub.c代码

int sub(int i,int j)
    {
        return i-j;
    }

运行结果:

 注意:所有文件必须在同一个工程之中。

八、函数的递归

        什么是递归?在调用一个函数的过程中又直接或者间接地调用该函数本身,称为函数的递归调用。

        程序调用自身的编程技巧。递归的主要思考方式:把大事化小。

递归的两个必要条件

        1、存在限制条件,当满足这个限制条件,递归便不在继续;

        2、每次递归调用之后越来越接近这个限制条件。

        一个递归问题分为“回溯”与“递推”两个过程;

举例1:输入一个数字,将这个数字分开输出,如:输入一个12345,依次输出1 2 3 4 5

#include<stdio.h>
void pr(n)
{
    if(n>9)
    {
        pr(n/10);
    }
    printf("%d  ",n%10);
}
int main()
{
    int sum ;
    printf("请输入一个数字:");
    scanf("%d",&sum);
    pr(sum);
    return 0;
}

结果:

 举例2:求n的阶乘

#include <stdio.h>
int jc(int n)
{
    if(n>1)
    return n*jc(n-1);
    else
    return 1;

}
int main()
{
    int i;
    printf("请输入一个数字:");
    scanf("%d",&i);
    int sum=jc(i);
    printf("%d",sum);
    return 0;

}

结果:

        此处对于函数的递归作以简单的介绍,具体递归的应用还需要多加练习,此处只希望读者弄清楚递归的概念, 并且能够区分递归与嵌套的区别(一个是调用自己,一个是调用别的函数)。

九、数组作为函数的参数

        数组作为函数的参数时,分为数组元素作为函数的参数以及数组名作为函数的参数;

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

        数组元素可以作为函数的实参,不能作为函数的形参,因为形参是在函数被调用时临时分配的存储单元,不可能为单独的一个数组元素分配地址。在数组元素作为实参时,把实参的值(数组元素的值)传给形参,是“值传递”的方式。

举例:输入10个数,要求输出其最大的元素以及该元素是第几个数。

#include <stdio.h>
int max(int x,int y )//定义比较函数
   {
      return (x>y?x:y);//返回较大值
   }
int main()
{
    int arr[10];//定义输入数字的数组
    int i,n,m;
    printf("请输入是十个数:");
    for(i=0;i<10;i++)//输入十个数字
    {
        scanf("%d",&arr[i]);
    }

    for(i=1,m=arr[0],n=0;i<10;i++)//是个数组元素进行两两比较
    {
        if(max(m,arr[i])>m)
        {
            m=max(m,arr[i]);//将最大值存放在m中
            n=i;//把最大值的下标记录下来
        }
    }
    printf("最大的数为%d,其下标为%d",m,n);

    return 0;
}

结果:

        2、数组名作为函数参数

        除了利用数组元素作为函数的参数,数组名也可以做为函数的参数(形参和实参);

        用数组元素作为函数的实参时,向形参传递的是数组元素的值;而用数组名作为函数的参数时,传递的是数组首元素的地址;因为数组名就是数组首元素的地址;

举例:有一个一维数组,内存放了10个学生的成绩,求其平均值;

#include <stdio.h>
int main()
{
    float average(float arr2[]);//求平均值函数的声明
    float arr[10],aver;//定义数组,变量
    int i;
    printf("请输入十个学生的成绩:");//输入十个学生的成绩
    for(i=0;i<10;i++)
    scanf("%f",&arr[i]);
    aver=average(arr);//调用average函数,数组名作为实参
    printf("十个学生的平均成绩为:%f",aver);//打印平均成绩
    return 0;
}
float average(float arr2[])//对average函数的定义
{                          //arr1的首元素地址传递给arr2数组,两个数组具有同一地址,共同占用同一单元,arr[]指向同一单元;
    int i;
    float n ,sum;
    for(i=0;i<10;i++)
    sum+=arr2[i];
    n=sum/10;
    return n;
}

结果:

         总结,本节主要讲述了利用函数实现模块化程序设计,内容较多,但是并不难,读者在了解完相关的概念后需要对其中提到的练习进行熟练的掌握,这样才能更好的运用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值