函数

函数是c语言模块化编程的最小单位,可以把每个函数看作一个“模块”(module)
把这些函数单独设计,测试,调试好,用时再拿来装配,总体调试,这些函数可以是自己设计制造/别人设计制造/现成的标准产品

函数的概念

函数是按给定的任务,把相关语句组织在一起的程序块,也称为例程或过程
可以将大的计算任务分解成若干较小的任务,并且可以基于函数进一步构造程序,而不需重新编写一些代码
设计得当的函数可以将程序中不需要了解的具体操作细节隐藏起来,从而使整个程序结构更加清晰,降低修改程序的难度
若干相关的函数可以合并成一个模块(源程序文件),一个c程序结构一般由一个或多个源程序文件组成
c在设计中考虑了函数的高效性和易用性两个因素,c程序一般都由许多小的函数组成,而不是少量较大的函数组成
函数之间通信可以通过参数,函数返回值以及外部变量进行,
函数在源文件中出现的次序可以是任意的,只要保证每个函数不被分离到多个文件,源程序就可以分为多个文件
通常在解决实际问题中,最好将程序划分成若干个与问题的自然划分相一致的函数,并通过主函数控制其他函数的执行

函数的分类

  • 标准库函数(ANSI/ISO C定义的标准库函数)符合标准的c编译器必须提供这些函数,函数的行为必须符合ANSI/ISO C的定义\
  • 第三方库函数
    由其他厂商自行开发的c语言函数库,不在标准范围内,能扩充c语言的功能(图形,网络,数据库等)\
  • 自定义函数
    自己定义使用的函数,也可以包装成函数库发布出来

函数的执行流程

c程序的执行从main()函数开始,一个函数可以调用其他函数,也可以被其他函数调用
调用其他函数后流程回到main()函数
在main函数中结束整个程序运行

模块化设计:“拿来主义”提高软件开发效率
函数体现了分治算法的思想,即求解问题采用逐步分解、分而治之的方法,将大问题分解为比较容易求解的小问题,小问题求解完后大问题就容易求解了
程序设计的模块化程序设计方法就是这种思路,采用函数求解
程序模块化,是程序设计简单直观,提高程序可读性和可维护性,提高代码复用性,减少编程和修改的工作量
采用自顶向下的程序设计思路,自下向上实现的模块化设计方法(先设计框架,然后从各个组成部分开始实现)
函数+函数构成程序
成熟软构件+修订=新软件
成熟软构件1+成熟软构件2+…=新软件系统

ANSI标准对c做的最明显的修改是函数声明和函数定义两方面

早期版本的c函数声明和定义的实现,例:
    int power();    函数声明中不包含参数列表,并且默认返回int类型的值,因此很多时候整个函数声明甚至可以完全省略
    power(base,n)   函数定义时参数名在圆括号内指定,
    int base,n;     参数类型在左花括号之前声明,如果没有声明某个参数的类型,则默认为int类型
    {…}             函数体与ANSI C中形式相同

相比于ANSI C,早期版本的方式很难检测出函数调用中参数数目和类型方面的错误
ANSI C仍然支持旧式的函数声明与定义(保持一个过渡阶段),但新式编译器中应一律使用ANSI C规定的新式函数原型声明方式(很多编译器已不支持)
ANSI C改编后的c函数声明、定义语法

函数声明

让编译器知道函数的存在,以及存在的形式,即使函数暂时没有定义,编译器也知道如何使用\

如:status ReadInfo(char *name, struct student stu[], int *num);
声明只是指出了函数的名字,并没有为其分配存储空间

基本格式为:函数数据类型 函数名(数据类型1 形参1,数据类型2 形参2…)
函数声明给出了函数名、返回值类型、参数列表等与该函数有关的其他信息,称为函数原型
函数声明的时候只要让编译器知道这个函数参数个数、参数类型即可,编译器并不会检查变量名

如:int func(int,int)和int func(int a,int b)对编译器是一样的,此时两个整型参数无法使用,称为占位参数
参数声明得当(一般尽量声明范围较大的参数类型),程序就可以自动进行适当的表达式强制类型转换
函数名称要满足标识符定义,同时与要实现的功能相关,函数名称后面跟一个小括号,表明不是普通变量名称而是函数名称

函数定义

定义表示创建或分配存储单元
将代码段封装成函数的过程叫做函数定义,基本格式如下:
函数返回值类型 函数名(数据类型1 形参1,数据类型2 形参2…){声明语句序列 可执行语句序列(函数体);}

例:计算整数n的阶乘
    int Fact(int n)
    {
        int i,result=1;             函数内部可以定义只供自己使用的变量,称内部变量
        for(i=1;i<=n;i++)
        {
            result*=i;
        }
        return result;              返回值作为函数调用表达式的值
    }
    int main()
    {
        printf("%d",Fact(10));      函数调用,实参传递
    }

其中函数体描述函数整体的功能,是函数的主体部分,即使只有一个语句,函数体也要由{}包围
如果有返回值,需要在函数体中使用return语句返回
函数不能嵌套定义,不能在一个函数中定义另外一个函数,函数的作用域默认是全局的,必须定义在函数以外(但可以在函数内部声明)
main()也是一个函数定义,因此也不能在main()函数内部定义新函数,main()函数要求必须返回int类型数据\

函数示例程序:从文件中读取数据(程序仅为大体的结构,无法运行)
status ReadInfo(char *name,struct student stu[],int *num);      status是自定义的类型,用以描述返回状态值,一种伪代码
int main()                                      开始执行;        typedef int Status;大致上是用来返回本函数是否执行成功
{
    char *name=“stuScores.txt”;struct student[40];int n;        实参
    if(ReadInfo(name,student,&n)==fail)       调用ReadInfo(); name为字符串常量(文件名)student为结构数组 n指向整型变量n
    {
        printf(“open file error\n”);
        return-1;
    }
}
status ReadInfo(char *name,struct student stu[],int *num)       形参
{
    status s=fail;                                               调用后开始执行
    int i=0;
    FILE *fp=fopen(name,“r”);
    if(fp==NULL)return s;
    while(!feof(fp))
    {
        fread(fp,&stu[i],sizeof(struct student),1);
        ++i;
    }
    *num=i;
    s=success;
    return s;                                                   回到main()函数,表达式有没有括号都正确
}

函数定义中的各构成部分有时候可以省略,如:dummy(){}
该函数不执行任何操作也不返回任何值,可以在程序开发期间用以保留位置(留待以后填充代码)
如果函数定义中省略返回值类型,则默认为int类型(注意,这些行为在大部分编译器中是不支持的)