函数

    • 函数是完成某项功能的程序模块(代码);
    • C程序主要是由函数组成的,函数也是C程序实现模块化设计的主要途径。
    • 函数的分类:
      • 用户角度:
        • 标准库函数
        • 用户自定义函数 **
      • 函数形式
        • 有参函数
        • 无参函数
      • 主调函数:主动调用某函数的函数;
      • 被调函数:被调用的函数;
    • 函数的优点:
      • 可以减少重复性代码;
      • 实现C程序的模块化设计;
    • 怎么定义函数?
      • 格式:返回值类型 函数名(参数列表);
          • 函数体代码;

        • 例子: int min(int a,int b)
        • { return a
        • 注意:如果省略函数的类型标识符,则默认为int 型
      • 形式参数是用来接收主调函数传输过来的数据。
    • 定义空函数
      • 类型标识符 函数名()
      • {}
        • 调用此函数时,什么都不做。常用来在准备扩充功能的地方先写一个空函数,占位。
    • 定义函数的注意事项:
      • 函数类型标识符变量类型说明符相同,它表示返回的函数值的类型。
      • 在C语言中还可以定义无类型(即void类型)的函数,这种函数不返回函数值,只是完成某种功能。
      • 如果省略函数的类型标识符,则默认为是int型。
      • 函数中返回语句的形式为 return(表达式);或 return 表达式;其作用是将表达式的值作为函数值返回给调用函数。其中表达式的类型应与函数返回值类型一致。
      • 如果形参表中有多个形参,即使它们的类型是相同的,在形参表中也只能逐个进行说明。
      • 如果”形参表列”中有多个形式参数,则它们之间要用”,”分隔。
      • 一个完整C程序中的所有函数可以放在一个文件中,也可以放在 多个文件中。
    • 函数调用的一般形式:
      • 函数名(实参表列)
        • C语言函数(2021.5.26) - 图1
      • 函数调用方式
        1. - ![](https://api2.mubu.com/v3/document_image/38888b02-3988-4ce6-91ae-c6ebc9a4c2b1-12077076.jpg#align=left&display=inline&height=254&margin=%5Bobject%20Object%5D&originHeight=380&originWidth=599&status=done&style=none&width=400)
    • 函数参数和函数的值:
      • 定义函数时,函数名后括号中的变量称为形式参数,即形参。
      • 在主函数中调动函数时,函数名后括号中的表达式称为实际参数,简称实参。
        • 函数调用时才给形参分配存储空间,且调用结束后所占空间立即释放。
        • 形式参数(形参):函数定义时指定的参数。
        • 实际参数(实参):函数调用时指定的参数。
          • 形参在函数未被调用前是不开辟内存空间的,只有当函数被调用时,系统才会为形参开辟内存空间,用于接收实际的数据。
          • 值传递|单向传递:在C语言中,实参向形参的数据传递是“值传递”,是单向传递,即只由实参传给形参,而不能由形参传回来给实参。在内存中,实参、形参占不同的存储单元,因此形参值的改变不会影响实参值。
    • 函数原型声明的两种形式:
      • 1.函数类型 函数名(形参类型1 形参名1,形参类型2 形参名2,…);
        • 各“形参名”可以是任意的,可以与被调用函数中的形参名一样,也可以不一样。但声明中的函数类型以及各形参类型、形参个数必须要与被调用函数定义中的一致。
      • 2.函数类型 函数名(形参类型1 ,形参类型2 ,…)
        • 可以只写形参类型而不写形参的名字,因为编译系统并不检查参数名字,只检查参数类型。
        • 意义:便于在编译源程序时对调用函数的合法性进行全面检查。当发现不匹配的函数调用(如函数类型不匹配,参数个数不一致,参数类型不匹配等)时,就会在屏幕上显示出错信息,用户可据此发现并改正函数调用中的错误。
      • 什么情况下需要声明函数?
        • 函数定义出现在函数调用之后,需要在主调函数内或主调函数前声明;
        • 函数定义是在另一个源文件中;
    • 嵌套调用:
      • C语言不允许嵌套定义函数,但可以嵌套调用函数。
        • C语言函数(2021.5.26) - 图2
      • 嵌套调用(被调函数做主调函数,再次调用其他函数);
    • 函数的递归调用:
      • 函数调用原理:
        • 1.函数调用函数是要压栈(栈空间)的;
        • 2.函数调用结束后,要出栈。
      • 递归:函数体内直接或间接的调用自身。
      • 本质:借助函数自身调用实现的循环结构。
      • 实现原理:函数的调用原理实现回推。
    • 实现函数递归的注意事项:
        1. 函数内部必须要有函数返回的条件;(必须要有出口)
        1. 对函数返回的判断必须写在函数开始处;(函数开始就要有出口的判断)
        1. 开始递归;
        1. 递归调用时,必须要向出口逼近
      • 用递归来求阶乘
    • 数组做函数参数:
      • 1.数组元素做参数
        • 和变量做参数是一样的,是单向的“值传递”。
        1. 数组名做参数,传递的第一个元素的地址:是“地址传递”,对形参的操作会影响到实参。
      • 被调函数形参为数组时,计算该数组大小为固定值,32位为4字节,64位为8字节。
        • 所以在自定义函数形参用数组时,多加一个参数为数组长度。防止自定义函数内对数组的操作产生越界行为。
        • 如果是字符数组,存放字符块,不用计算字符数组大小,字符数组中有’\0’结束符。
    • 衍生知识点:
      • const 关键字 : const 关键字用于修饰变量,用在数组时候表明只能读取数据,不能修改数据;
      • const 关键字使用 :
        • const int a = 9;
        • 用const 修饰的变量,也被称为 只读变量。
    • 变量的作用域(作用范围)
      • 根据变量的作用范围不同,将变量分为局部变量和全局变量
      • 1.局部变量(内部变量)
        • 函数内定义的变量
        • 作用域:仅限于函数内;
        • 形式参数也是局部变量:
        1. 全部变量(外部变量)
          • 函数外部定义的变量
          • 作用域:从变量定义到本文件末。
      • 全局变量:
        • 1、设置全局变量增加了函数间数据的联系渠道。
        • 2、若在同一源程序文件中外部变量与局部变量同名,则在局部变量的作用域范围内,外部变量被屏蔽,即不起作用。
        • 3、建议不在必要时不要使用全局变量,因为:
          • ① 全局变量在程序的全部执行过程中都占用存储单元,而不是仅在需要时才开辟单元;
          • ② 使用全局变量过多,会降低程序的清晰性。在各个函数执行时都可能改变外部变量的值,程序容易出错。因此,要限制使用全局变量;
          • ③ 降低了函数的通用性。因为函数在执行时要依赖于其所在的外部变量。如果将一个函数移到另一个文件中,还要将有关的外部变量及其值一起移过去。但若该外部变量与其它文件的变量同名时,就会出现问题,降低了程序的可靠性和通用性。一般要求把C程序中的函数做成一个封闭体,除了可以通过“实参——形参”的渠道与外界发生联系外,没有其它渠道。
    • 变量的存储方式和生存期/生命周期
      • 1、变量的生命周期:变量在运行时存在的时间;
        • 局部变量:当局部变量定义时产生,函数结束后消亡。
        • 全局变量:当全局变量定义时产生,程序结束后消亡。
      • 2、变量的存储方式:变量存放在哪里;
        • 变量存储类型:
          • 2.1 auto (自动类型)#默认 修饰局部变量,变量存放在栈(段)空间;系统自动回收,用户无法操作;
          • 2.2 static(静态类型)
            • static 修饰局部变量:变量的作用域不变,生命周期延长;
            • static 修饰全局变量:变量的生命周期不变,作用域减少;只限定本文件内使用。
          • 2.3 extern(外部类型)
            • 用extern 修饰的变量,表明该变量可能是在其他文件中,或者是在本条语句后定义的。
            • extern 修饰全局变量:变量的生命周期不变,作用域扩充
          • 2.4 register(寄存器类型)
              1. register只能修饰局部变量,不更改变量的作用域;
                • 一般情况下,变量往往是存储在内存中的,用register修饰的变量存储在CUP的寄存器中。
                • 场景:循环变量!
          • 变量完整定义格式:
            • [存储类型] 数据类型 变量名;
      • 3、变量的存储方式决定了变量的生命周期。
      • C语言函数(2021.5.26) - 图3
      • C语言函数(2021.5.26) - 图4