当前位置 博文首页 > CW_qian的博客:7月22日C语言基础函数

    CW_qian的博客:7月22日C语言基础函数

    作者:[db:作者] 时间:2021-08-25 21:46

    函数入门

    ????????在C语言中,函数意味着功能模块。一个典型的C语言程序,就是由一个个的功能模块拼接起来的整体。也因为如此,C语言被称为模块化语言。

    ????????对于函数的使用者,可以简单地将函数理解为一个黑箱,使用者只管按照规定给黑箱一些输入,就会得到一些输出,而不必理会黑箱内部的运行细节。


    黑箱的输入和输出

    ????????日常使用的电视机可以被理解为一个典型的黑箱,它有一些公开的接口提供给使用者操作,比如开关、音量、频道等,使用者不需要理会其内部电路,更不需要管电视机的工作原理,只需按照规定的接口操作接口得到结果。

    ????????对于函数的设计者,最重要的工作是封装,封装意味着对外提供服务并隐藏细节。对于一个封装良好的函数而言,其对外提供服务的接口应当是简洁的,内部功能应当是明确的。

    函数的定义

    • 函数头:函数对外的公开接口
      1. 函数名称:命名规则与跟变量一致,一般取与函数实际功能相符合的、顾名思义的名称。
      2. 参数列表:即黑箱的输入数据列表,一个函数可有一个或多个参数,也可以不需要参数。
      3. 返回类型:即黑箱的输出数据类型,一个函数可不返回数据,但最多只能返回一个数据。
    • 函数体:函数功能的内部实现
    • 语法说明:

    返回类型 函数名称(参数1, 参数2, ……)

    {

    ????函数体

    }

    • 函数示例1:求两个给定整数的最大值
    int?max(int?x, int?y) // 该函数接收两个整型参数,并返回一个整型数据
    
    {
    
    ????int?z;
    
    ????z = x>y ? x : y;
    
    ????return?z;
    
    }
    • 语法汇总:
      1. 当函数的参数列表为 void 时,表示该函数不需要任何参数。
      2. 当函数的返回类型为 void 时,表示该函数不返回任何数据。
      3. 关键字 return 表示退出函数。若函数头中规定有返回数据类型,则 return 需携带一个类型与之匹配的数据;若函数头中规定返回类型为 void,则 return 不需携带参数。

    实参与形参

    • 概念:
      • 函数调用中的参数,被称为实参,即 arguments
      • 函数定义中的参数,被称为形参,即 parameters
    • 实参与形参的关系:
      • 实参于形参的类型和个数必须一一对应。
      • 形参的值由实参初始化。
      • 形参与实参位于不同的内存区域,彼此独立。
    • 示例:

    // 函数定义中,x、y都属于形参,位于函数 max 的栈内存中

    // 它们的值由实参一一对应初始化

    int?max(int?x, int?y)
    
    {
    
    ????int?z;
    
    ????z = x>y ? x : y;
    
    ????return?z;
    
    }
    
    
    
    int?main(void)
    
    {
    
    ????int?a = 1;
    
    ????int?b = 2;
    
    ????int?m;? ? ? ?
    
    ????// 函数调用中,a、b都属于实参,存储于主函数 main 的栈内存中
    
    ????m = max(a, b); ???
    
    }

    函数调用的流程

    函数调用时,进程的上下文会切换到被调函数,当被调函数执行完毕之后再切换回去。


    函数调用时代码的执行流程

    静态函数

    • 背景知识:普通函数都是跨文件可见的,即在文件 a.c 中定义的函数可以在 b.c 中使用。
    • 静态函数:只能在定义的文件内可见的函数,称为静态函数。
    • 语法:

    staitc void?f(void) // 在函数头前面增加关键字 static ,使之成为静态函数

    {

    ???? // 函数体

    }

    • 要点:
      • 静态函数主要是为了缩小函数的可见范围,减少与其他文件中重名函数冲突的概率。
      • 静态函数一般被定义在头文件中,然后被各个源文件包含。

    递归函数

    • 递归概念:如果一个函数内部,包含了对自身的调用,则该函数称为递归函数。
    • 递归问题:
      • 阶乘。n!=(n-1)!*n
      • 幂运算。a^n = a^(n-1)*a
      • 字符串翻转。
    • 要点:
      • 只有能被表达为递归的问题,才能用递归函数解决。
      • 递归函数必须有一个可直接退出的条件,否则会进入无限递归。
      • 递归函数包含两个过程,一个逐渐递进的过程,和一个逐渐回归的过程。
    • 示例:依次输出 n 个自然数。
    • 思路:先输出前面的 n-1 个自然数,再输出最后一个自然数 n 。而要输出前面的 n-1 个自然数,递归调用自身即可。

    // 该函数的功能:依次输出 n 个自然数

    // 该函数的功能:依次输出 n 个自然数
    
    void?f(int?n)
    
    {
    
    ????if(n < 0) ?????????// 1,当满足此条件时,不再进行递归。
    
    ????????return;
    
    ????????
    
    ????f(n-1); ???????????// 2,递归调用自己,输出前 n-1 个数
    
    ????printf("%d\n", n); // 3,输出最后一个自然数 n
    
    }

    • 递归调用时,函数的栈内存的变化如下图所示。可见,随着递归函数的层层深入,栈空间逐渐往下增长,如果递归的层次太深,很容易把栈内存耗光。
    • 层层递进时,问题的规模会随之减小,减小到可直接退出的条件时,函数开始层层回归。


    递归调用时栈内存的变化

    回调函数(钩子函数)

    • 概念:函数实现方不调用该函数,而由函数接口提供方间接调用的函数,称为回调函数。
    • 示例:系统中的信号处理,是一个典型的利用回调函数的情形。

    ?
    信号处理

    • 要点:
      • 示例中函数 sighandler 是回调函数。
      • signal() 将函数回调函数传递给内核,使得内核可以在恰当的时机回调 sighandler。
      • 应用开发者和内核开发者只要约定好回调函数的接口,即可各自开发,进度互不影响。

    cs
    下一篇:没有了