CAPL 编程系列教程 - 第十六期:函数的定义与调用 (进阶)

CAPL 编程系列教程的第十六期内容,继续深入讲解 CAPL 中函数的定义与调用,侧重于一些特殊情况和进阶用法。

CAPL 编程系列教程 - 第十六期:函数的定义与调用 (进阶)

一、 课程回顾与本期目标

  • 回顾: 上一期(第十五期)学习了函数定义的基本格式,包括返回值类型、函数名、形参列表、函数体和 return 语句,并通过计算 A 的 B 次方的例子进行了演示
  • 本期目标: 进一步细化函数定义的语法,特别是处理没有形参、没有返回值的情况,并讲解函数重载、数组作为参数以及函数无法直接返回数组的问题与解决方案

二、 函数的特殊定义形式

  1. 无返回值函数 (void):

    • 目的: 当函数只需要执行一系列操作(如打印、修改全局变量、控制硬件等)而不需要返回计算结果时使用
    • 定义: 返回值类型声明为 void
    • CAPL 特点: void 关键字可以省略。函数名前若无类型,则默认为 void
    • return 语句:
      • 可以在 void 函数中使用不带值的 return; 语句来提前结束函数的执行
      • 如果省略 return,函数会在执行完最后一条语句后自动结束
    • 示例 (打印三次 "I love you"):
      Code snippet
      // void sayLove() // void 可以省略
      sayLove() 
      {
          int i;
          for (i = 0; i < 3; i++) {
              write("I love you");
          }
          // return; // 可选
      }
      
      on key 'a' {
          sayLove(); // 调用无返回值的函数
      }
      
  2. 无参数函数:

    • 目的: 当函数执行的操作不需要外部传入数据时使用
    • 定义: 函数名后的小括号 () 必须保留,但括号内为空
    • 示例: 上述 sayLove() 函数就是一个无参数函数

三、 函数重载 (Function Overloading)

  • 概念: 允许在同一个作用域内定义多个同名函数,但它们的形式参数列表必须不同(参数个数、类型或顺序不同)
  • 返回值要求: 重载的函数必须具有相同的返回值类型 。仅返回值类型不同,不能构成重载,会导致编译错误
  • 目的: 为逻辑上相似但处理不同参数类型或个数的操作提供统一的函数名,方便调用者使用
  • 调用时区分: CAPL 编译器会根据调用函数时提供的实际参数 (实参)个数和类型来自动匹配并调用对应的重载函数版本
  • 示例 (求和函数 add):
    Code snippet
    // 重载版本 1: 计算两个整数的和
    int add(int a, int b) {
        return a + b;
    }
    
    // 重载版本 2: 计算一个整数数组所有元素的和
    int add(int numbers[]) { // 注意数组参数的写法
        int s = 0;
        int i;
        for (i = 0; i < elcount(numbers); i++) { // elcount 获取数组长度
            s += numbers[i];
        }
        return s;
    }
    
    on key 'a' {
        int nums1[3] = {93, 87, 92};
    
        write("add(3, 6) = %d", add(3, 6));             // 调用版本 1
        write("add(nums1) = %d", add(nums1));           // 调用版本 2
    }
    

四、 数组作为函数参数

  • 形参声明: 在函数定义时,数组类型的形参声明为 DataType ArrayName[]中括号 [] 内不写长度
  • 实参传递: 调用函数时,直接传递数组变量名即可
  • 工作机制 (隐式引用传递): 当数组作为参数传递给函数时,实际上传递的是数组的地址(或引用),而不是整个数组的副本。这意味着函数内部对形参数组元素的修改会直接影响到调用者传入的原始实参数组 。这可以用来间接实现“返回”多个值或修改外部数组。
  • 示例: 见上述 add(int numbers[]) 函数的定义和调用

五、 函数无法直接返回数组及解决方案

  • 限制: CAPL 函数(与 C 语言类似)不能直接将整个数组作为返回值类型来返回 。例如,int[] MyFunction() 这样的语法是不允许的
  • 解决方案 (通过参数“返回”数组):
    • 思路: 调用者先在自己的作用域内声明并准备好一个数组(可以为空或未初始化),然后将这个数组作为参数传递给需要“返回”数组的函数
    • 函数内部: 函数接收这个数组参数(形参),并在函数体内计算结果,将结果填充到这个传入的数组中
    • 函数结束后: 由于数组传递的是引用,调用者原始的数组内容已经被函数修改(填充)了,从而达到了“获取”一个数组结果的目的
  • 示例 (生成斐波那契数列):
    Code snippet
    // 函数定义:计算前 n 位斐波那契数,并填充到传入的 numbers 数组中
    // void 可以省略
    generateFibonacci(int n, int numbers[]) 
    {
        int i;
        int a = 0, b = 1, c; // 斐波那契计算用临时变量
    
        if (n <= 0 || elcount(numbers) < n) return; // 基本的错误检查
    
        // ... (省略了视频中详细的斐波那契数列填充逻辑) ...
        // 核心思想是在循环中计算斐波那契数,并赋值给 numbers[i]
        for (i = 0; i < n; i++) {
             if (i <= 1) {
                 numbers[i] = (i==0) ? a : b; // 处理前两项
             } else {
                 c = a + b;
                 numbers[i] = c;
                 a = b;
                 b = c;
             }
        }
    }
    
    on key 'a' {
        int fibSequence[8]; // 调用者准备一个足够大的数组
        int i;
    
        // 调用函数,将数组 fibSequence 传给函数去填充
        generateFibonacci(8, fibSequence); 
    
        // 调用后,fibSequence 数组中就包含了计算结果
        write("前 8 位斐波那契数列:");
        for (i = 0; i < elcount(fibSequence); i++) {
            write("%d", fibSequence[i]); 
        }
    }
    

六、 总结与后续

  • 本期深入探讨了 CAPL 函数的多种定义形式,包括无参数、无返回值 (void 可省略) 的情况
  • 学习了函数重载的概念和用法,以及数组作为函数参数的传递机制(引用传递)
  • 重点掌握了 CAPL 函数无法直接返回数组,以及通过传递数组参数作为“输出”的常用解决方案
  • 至此,CAPL 函数的基础内容讲解完毕,为后续编写更复杂的模块化代码打下基础