易语言子程序高级应用:递归、回调与参数设计实战
本文介绍易语言子程序的高级应用,涵盖递归调用机制(含栈溢出风险规避)、参数传递形式(值传递、引用传递、数组及 UDT)、回调函数实现(事件驱动与指针驱动)以及多线程同步基础。通过斐波那契数列计算、员工薪资系统案例,展示了模块解耦与递归在实际开发中的具体实践方法。

本文介绍易语言子程序的高级应用,涵盖递归调用机制(含栈溢出风险规避)、参数传递形式(值传递、引用传递、数组及 UDT)、回调函数实现(事件驱动与指针驱动)以及多线程同步基础。通过斐波那契数列计算、员工薪资系统案例,展示了模块解耦与递归在实际开发中的具体实践方法。

💡学习目标:1. 深入理解易语言子程序的递归调用机制与实现条件;2. 掌握参数传递的高级形式(引用传递、数组参数、自定义数据类型参数);3. 学会使用回调函数实现程序模块间的解耦;4. 初步了解多线程环境下的子程序调用(线程同步基础);5. 通过真实案例(递归计算斐波那契数列、文件目录遍历、员工薪资计算系统的模块解耦)巩固所学知识。 ⚠️学习重点:递归的退出条件设置、栈溢出风险规避、引用传递与值传递的区别、数组/UDT 参数的类型声明、回调函数的指针实现方法、线程同步的临界区使用。
递归调用是指子程序直接或间接调用自身的过程,常用于解决具有重复子问题的问题(如计算阶乘、斐波那契数列、遍历文件目录、二叉树操作等)。
阶乘的数学公式为:n! = 1×2×3×…×n(n≥0),其中 0! = 1。
.版本 2 .子程序 计算阶乘_递归,整数型,公开,递归计算阶乘,参数 n≥0 .参数 n, 整数型,, 要计算阶乘的整数 .局部变量 结果,整数型 ' 终止条件 如果真 (n = 0 或 n = 1) 结果 = 1 返回 (结果) 如果真结束 ' 递归公式:n! = n × (n-1)! 结果 = n × 计算阶乘_递归 (n - 1) 返回 (结果)
✅测试结果:调试输出 (计算阶乘_递归 (5)),输出 120,符合预期。
斐波那契数列的数学公式为:F(n) = F(n-1) + F(n-2)(n≥2),其中 F(0)=0,F(1)=1。
.版本 2 .子程序 计算斐波那契_递归_非优化,整数型,公开,递归计算斐波那契数列(非优化版),参数 n≥0 .参数 n, 整数型,, 斐波那契数列的第 n 项 .局部变量 结果,整数型 ' 终止条件 如果真 (n = 0) 结果 = 0 返回 (结果) 如果真结束 如果真 (n = 1) 结果 = 1 返回 (结果) 如果真结束 ' 递归公式:F(n) = F(n-1) + F(n-2) 结果 = 计算斐波那契_递归_非优化 (n - 1) + 计算斐波那契_递归_非优化 (n - 2) 返回 (结果)
⚠️性能问题:当 n=40 时,计算时间约为 1 秒;当 n=50 时,计算时间约为 100 秒,因为存在大量重复计算。
通过创建一个全局或程序集变量的'记忆数组',存储已经计算过的斐波那契数列项,避免重复计算。
.版本 2 ' 程序集数据段定义的记忆数组 .程序集变量 斐波那契记忆数组,长整数型,数组,, 用于存储已经计算过的斐波那契数列项 .子程序 计算斐波那契_递归_记忆化,长整数型,公开,递归计算斐波那契数列(记忆化搜索优化版),参数 n≥0 .参数 n, 整数型,, 斐波那契数列的第 n 项 .局部变量 结果,长整数型 ' 预分配记忆数组的内存(根据 n 的大小动态调整) .如果 (取数组成员数 (斐波那契记忆数组) ≤ n) 重定义数组 (斐波那契记忆数组,真,n + 1) .如果结束 ' 检查记忆数组中是否已经存在该值 .如果 (斐波那契记忆数组 [n + 1] ≠ 0) ' 数组索引从 1 开始,初始值为 0 返回 (斐波那契记忆数组 [n + 1]) .如果结束 ' 终止条件 .如果 (n = 0) 结果 = 0 .否则 .如果 (n = 1) 结果 = 1 .否则 ' 递归公式:F(n) = F(n-1) + F(n-2) 结果 = 计算斐波那契_递归_记忆化 (n - 1) + 计算斐波那契_递归_记忆化 (n - 2) .如果结束 .如果结束 ' 将结果存入记忆数组中 斐波那契记忆数组 [n + 1] = 结果 返回 (结果)
✅性能对比:当 n=50 时,非优化版计算时间约为 100 秒,优化版计算时间小于 1 毫秒。
递归调用是基于函数栈(stack)实现的,每次调用都会在栈中压入一个'栈帧'(包含参数、局部变量、返回地址等信息)。当递归深度过大(如 n=10000)时,栈会溢出,导致程序崩溃。
.版本 2 .子程序 模拟栈溢出_递归,, 公开 .参数 递归深度,整数型,可空 .局部变量 临时变量,文本型,静态,"1000" ' 静态数组,每个栈帧占用较大内存 ' 模拟递归深度的增加 如果真 (递归深度 = 0 或 递归深度 = 空) 递归深度 = 1 如果真结束 ' 输出当前递归深度 调试输出 ('当前递归深度:', 递归深度) ' 继续递归调用 模拟栈溢出_递归 (递归深度 + 1)
⚠️运行结果:当递归深度约为 1000 时,程序会崩溃,提示'栈溢出'。
.版本 2 .子程序 计算阶乘_迭代,整数型,公开,迭代计算阶乘,参数 n≥0 .参数 n, 整数型,, 要计算阶乘的整数 .局部变量 结果,整数型 .局部变量 i, 整数型 ' 初始化结果 结果 = 1 ' 循环计算阶乘 计次循环首 (n, i) 结果 = 结果 × i 计次循环首结束 ' 处理 n=0 的情况 如果真 (n = 0) 结果 = 1 如果真结束 返回 (结果)
.版本 2 .子程序 计算斐波那契_迭代,长整数型,公开,迭代计算斐波那契数列,参数 n≥0 .参数 n, 整数型,, 斐波那契数列的第 n 项 .局部变量 结果,长整数型 .局部变量 i, 整数型 .局部变量 前一项,长整数型 .局部变量 前两项,长整数型 ' 处理 n=0 和 n=1 的情况 .如果 (n = 0) 结果 = 0 .否则 .如果 (n = 1) 结果 = 1 .否则 ' 初始化前两项 前两项 = 0 前一项 = 1 ' 循环计算第 n 项 i = 2 判断循环首 (i ≤ n) 结果 = 前一项 + 前两项 ' 更新前两项 前两项 = 前一项 前一项 = 结果 i = i + 1 判断循环尾 () .如果结束 .如果结束 返回 (结果)
值传递是易语言参数传递的默认方式,它会复制实参的值到形参中,形参的修改不会影响实参的值。
.版本 2 .子程序 交换两个整数_值传递,, 公开,尝试交换两个整数(值传递,无法实现交换) .参数 a, 整数型,, 要交换的第一个整数 .参数 b, 整数型,, 要交换的第二个整数 .局部变量 临时变量,整数型 ' 尝试交换 a 和 b 的值 临时变量 = a a = b b = 临时变量 ' 输出形参的值(此时 a 和 b 的值已交换) 调试输出 ('形参 a 的值:', a) 调试输出 ('形参 b 的值:', b)
.版本 2 .子程序 _测试值传递按钮_被单击 .局部变量 x, 整数型 .局部变量 y, 整数型 ' 初始化 x 和 y 的值 x = 10 y = 20 ' 输出实参的初始值 调试输出 ('实参 x 的初始值:', x) 调试输出 ('实参 y 的初始值:', y) ' 调用值传递的交换子程序 交换两个整数_值传递 (x, y) ' 输出实参的最终值(此时 x 和 y 的值未交换) 调试输出 ('实参 x 的最终值:', x) 调试输出 ('实参 y 的最终值:', y)
✅运行结果:形参 a 和 b 的值已交换,但实参 x 和 y 的值未交换。
引用传递需要在参数声明时使用参考关键字(英文为 ByRef),它会将实参的内存地址传递到形参中,形参的修改会直接影响实参的值。
.版本 2 .子程序 交换两个整数_引用传递,, 公开,交换两个整数(引用传递,能够实现交换) .参数 a, 整数型,参考,要交换的第一个整数(引用传递) .参数 b, 整数型,参考,要交换的第二个整数(引用传递) .局部变量 临时变量,整数型 ' 交换 a 和 b 的值(直接修改实参的内存地址) 临时变量 = a a = b b = 临时变量 ' 输出形参的值(此时 a 和 b 的值已交换) 调试输出 ('形参 a 的值:', a) 调试输出 ('形参 b 的值:', b)
.版本 2 .子程序 _测试引用传递按钮_被单击 .局部变量 x, 整数型 .局部变量 y, 整数型 ' 初始化 x 和 y 的值 x = 10 y = 20 ' 输出实参的初始值 调试输出 ('实参 x 的初始值:', x) 调试输出 ('实参 y 的初始值:', y) ' 调用引用传递的交换子程序 交换两个整数_引用传递 (x, y) ' 输出实参的最终值(此时 x 和 y 的值已交换) 调试输出 ('实参 x 的最终值:', y) ' 输出 20 调试输出 ('实参 y 的最终值:', x) ' 输出 10
✅运行结果:实参 x 和 y 的值已交换,形参的值也已交换。
在易语言中,数组作为参数传递时,默认也是值传递,但由于数组是一个指针类型的变量(存储数组的首地址),所以形参对数组元素的修改会影响实参对数组元素的访问。
.版本 2 .子程序 计算数组元素的和_一维,整数型,公开,计算一维数组所有元素的和 .参数 目标数组,整数型,数组,, 要计算和的一维数组 .局部变量 和,整数型 .局部变量 索引,整数型 ' 遍历数组并计算和 计次循环首 (取数组成员数 (目标数组), 索引) 和 = 和 + 目标数组 [索引] 计次循环首结束 返回 (和)
.版本 2 .子程序 _测试一维数组参数按钮_被单击 .局部变量 测试数组,整数型,数组 .局部变量 数组元素的和,整数型 ' 初始化测试数组 重定义数组 (测试数组,真,5) 测试数组 [1] = 1 测试数组 [2] = 2 测试数组 [3] = 3 测试数组 [4] = 4 测试数组 [5] = 5 ' 调用计算和的子程序 数组元素的和 = 计算数组元素的和_一维 (测试数组) ' 输出结果 调试输出 ('一维数组所有元素的和:', 数组元素的和)
✅运行结果:一维数组所有元素的和:15,符合预期。
.版本 2 .子程序 计算二维数组元素的和,整数型,公开,计算二维数组所有元素的和 .参数 目标二维数组,整数型,数组,, 要计算和的二维数组 .局部变量 和,整数型 .局部变量 行索引,整数型 .局部变量 列索引,整数型 ' 遍历二维数组并计算和 行索引 = 1 判断循环首 (行索引 ≤ 取数组成员数 (目标二维数组)) 列索引 = 1 判断循环首 (列索引 ≤ 取数组成员数 (目标二维数组 [行索引])) 和 = 和 + 目标二维数组 [行索引] [列索引] 列索引 = 列索引 + 1 判断循环尾 () 行索引 = 行索引 + 1 判断循环尾 () 返回 (和)
.版本 2 .子程序 _测试二维数组参数按钮_被单击 .局部变量 测试二维数组,整数型,数组 .局部变量 二维数组元素的和,整数型 ' 初始化测试二维数组 重定义数组 (测试二维数组,真,2, 3) 测试二维数组 [1] [1] = 1 测试二维数组 [1] [2] = 2 测试二维数组 [1] [3] = 3 测试二维数组 [2] [1] = 4 测试二维数组 [2] [2] = 5 测试二维数组 [2] [3] = 6 ' 调用计算和的子程序 二维数组元素的和 = 计算二维数组元素的和 (测试二维数组) ' 输出结果 调试输出 ('二维数组所有元素的和:', 二维数组元素的和)
✅运行结果:二维数组所有元素的和:21,符合预期。
自定义数据类型(UDT)作为参数传递时,默认也是值传递(会复制整个 UDT 变量的内存内容),如果 UDT 变量的成员非常多或包含大量数组,会导致参数传递效率低下。此时可以使用引用传递来提高效率。
.版本 2 .数据类型 员工信息类型 .成员 员工编号,文本型 .成员 员工姓名,文本型 .成员 员工年龄,整数型 .成员 员工部门,文本型 .成员 员工薪资,双精度小数型 .子程序 修改员工薪资_值传递,, 公开,尝试修改员工薪资(值传递,无法实现修改) .参数 员工信息,员工信息类型,, 要修改薪资的员工信息(值传递) .参数 调整金额,双精度小数型,, 薪资调整金额(正数为增加,负数为减少) ' 尝试修改员工薪资 员工信息。员工薪资 = 员工信息。员工薪资 + 调整金额 ' 输出形参的员工薪资 调试输出 ('形参的员工薪资:', 员工信息。员工薪资)
.版本 2 .子程序 _测试 UDT 值传递按钮_被单击 .局部变量 测试员工信息,员工信息类型 ' 初始化测试员工信息 测试员工信息。员工编号 = 'YG202X001' 测试员工信息。员工姓名 = '张三' 测试员工信息。员工年龄 = 25 测试员工信息。员工部门 = '技术部' 测试员工信息。员工薪资 = 5000.0 ' 输出实参的初始员工薪资 调试输出 ('实参的初始员工薪资:', 测试员工信息。员工薪资) ' 调用修改薪资的子程序(值传递) 修改员工薪资_值传递 (测试员工信息,500.0) ' 输出实参的最终员工薪资(此时薪资未修改) 调试输出 ('实参的最终员工薪资:', 测试员工信息。员工薪资)
✅运行结果:形参的员工薪资已修改为 5500.0,但实参的最终员工薪资仍为 5000.0。
.版本 2 .数据类型 员工信息类型 .成员 员工编号,文本型 .成员 员工姓名,文本型 .成员 员工年龄,整数型 .成员 员工部门,文本型 .成员 员工薪资,双精度小数型 .子程序 修改员工薪资_引用传递,, 公开,修改员工薪资(引用传递,能够实现修改) .参数 员工信息,员工信息类型,参考,要修改薪资的员工信息(引用传递) .参数 调整金额,双精度小数型,, 薪资调整金额(正数为增加,负数为减少) ' 修改员工薪资(直接修改实参的内存地址) 员工信息。员工薪资 = 员工信息。员工薪资 + 调整金额 ' 输出形参的员工薪资 调试输出 ('形参的员工薪资:', 员工信息。员工薪资)
.版本 2 .子程序 _测试 UDT 引用传递按钮_被单击 .局部变量 测试员工信息,员工信息类型 ' 初始化测试员工信息 测试员工信息。员工编号 = 'YG202X001' 测试员工信息。员工姓名 = '张三' 测试员工信息。员工年龄 = 25 测试员工信息。员工部门 = '技术部' 测试员工信息。员工薪资 = 5000.0 ' 输出实参的初始员工薪资 调试输出 ('实参的初始员工薪资:', 测试员工信息。员工薪资) ' 调用修改薪资的子程序(引用传递) 修改员工薪资_引用传递 (测试员工信息,500.0) ' 输出实参的最终员工薪资(此时薪资已修改) 调试输出 ('实参的最终员工薪资:', 测试员工信息。员工薪资)
✅运行结果:实参和形参的员工薪资都已修改为 5500.0。
回调函数是指被另一个函数(调用者)作为参数传递,并在适当的时候由调用者执行的函数,常用于实现程序模块间的解耦(如排序算法的比较函数、事件处理函数、数据处理的钩子函数等)。
易语言中没有直接的'函数指针'类型,但可以通过指针类型的变量(如整数型指针、字节集指针)或用户自定义事件来实现回调函数的功能。
.版本 2 .支持库 eGrid ' 程序集数据段定义的用户自定义事件 .程序集事件 数据处理完成事件_回调,, 公开,数据处理完成后触发的回调事件 .参数 处理结果,整数型,, 数据处理的结果 .程序集变量 数据处理完成事件句柄,整数型,, 用于存储用户自定义事件的句柄 .子程序 初始化数据处理完成事件,, 公开,初始化用户自定义事件 .局部变量 事件 ID, 整数型 ' 注册用户自定义事件 事件 ID = 注册用户自定义事件 ('数据处理完成事件_回调') 数据处理完成事件句柄 = 事件 ID ' 输出事件 ID 调试输出 ('数据处理完成事件_回调的事件 ID:', 事件 ID)
.版本 2 .支持库 eGrid .子程序 数据处理_调用者,, 公开,模拟数据处理的调用者,处理完成后触发回调事件 .参数 数据,整数型,, 要处理的数据 .局部变量 处理结果,整数型 ' 模拟数据处理过程(耗时 1 秒) 延迟 (1000) ' 计算处理结果 处理结果 = 数据 × 2 ' 触发回调事件 ' 第一个参数:控件句柄(如果是全局事件,可以传 0) ' 第二个参数:事件 ID ' 第三个参数及以后:事件的参数 发送用户消息 (0, 数据处理完成事件句柄,0, 处理结果)
.版本 2 .支持库 eGrid .子程序 _数据处理完成事件_回调,, 公开,数据处理完成事件_回调的处理函数 .参数 处理结果,整数型,, 数据处理的结果 ' 输出处理结果 调试输出 ('数据处理完成!处理结果:', 处理结果) ' 显示提示信息 信息框 ('数据处理完成!处理结果:' + 到文本 (处理结果), 0, '提示')
.版本 2 .支持库 eGrid .子程序 _测试事件驱动型回调按钮_被单击 .局部变量 测试数据,整数型 ' 初始化用户自定义事件 初始化数据处理完成事件 () ' 初始化测试数据 测试数据 = 10 ' 调用数据处理的调用者 数据处理_调用者 (测试数据)
✅运行结果:延迟 1 秒后,输出数据处理完成的结果为 20,并显示提示信息。
这种方法需要使用易语言的'指针操作'命令,较为复杂,但性能较高,常用于处理大量数据的场景。
.版本 2 .支持库 spec .支持库 EThread ' 程序集数据段定义的回调函数指针类型 .程序集类型 数据处理回调函数指针,子程序,公开,数据处理回调函数的指针类型 .参数 处理结果,整数型,, 数据处理的结果 .子程序 数据处理_指针驱动型,, 公开,模拟数据处理的调用者,使用指针驱动型回调 .参数 数据,整数型,, 要处理的数据 .参数 回调函数指针,整数型,, 回调函数的指针 .局部变量 处理结果,整数型 .局部变量 回调函数,数据处理回调函数指针,静态 ' 模拟数据处理过程(耗时 0.5 秒) 延迟 (500) ' 计算处理结果 处理结果 = 数据 × 3 ' 调用回调函数 ' 将回调函数指针转换为子程序变量 指针到子程序 (回调函数,回调函数指针) ' 调用回调函数 回调函数 (处理结果)
.版本 2 .支持库 spec .支持库 EThread .子程序 数据处理完成_指针驱动型回调,, 公开,指针驱动型回调的处理函数 .参数 处理结果,整数型,, 数据处理的结果 ' 输出处理结果 调试输出 ('指针驱动型数据处理完成!处理结果:', 处理结果) ' 显示提示信息 信息框 ('指针驱动型数据处理完成!处理结果:' + 到文本 (处理结果), 0, '提示')
.版本 2 .支持库 spec .支持库 EThread .子程序 _测试指针驱动型回调按钮_被单击 .局部变量 测试数据,整数型 .局部变量 回调函数地址,整数型 ' 初始化测试数据 测试数据 = 20 ' 获取回调函数的地址 回调函数地址 = 取子程序地址 (&数据处理完成_指针驱动型回调) ' 调用数据处理的调用者 数据处理_指针驱动型 (测试数据,回调函数地址)
✅运行结果:延迟 0.5 秒后,输出数据处理完成的结果为 60,并显示提示信息。
多线程是指在同一时间内,一个程序可以同时执行多个任务(线程),提高程序的运行效率。在易语言中,可以使用'多线程支持库'来实现多线程的功能。
.版本 2 .支持库 EThread .子程序 线程任务_1,, 公开,线程任务 1(输出 1-10) .局部变量 i, 整数型 ' 输出 1-10 计次循环首 (10, i) 调试输出 ('线程任务 1 输出:', i) 延迟 (200) ' 模拟任务的耗时 计次循环首结束
.版本 2 .支持库 EThread .子程序 线程任务_2,, 公开,线程任务 2(输出 11-20) .局部变量 i, 整数型 ' 输出 11-20 i = 11 判断循环首 (i ≤ 20) 调试输出 ('线程任务 2 输出:', i) 延迟 (300) ' 模拟任务的耗时 i = i + 1 判断循环尾 ()
.版本 2 .支持库 EThread .子程序 _测试多线程按钮_被单击 .局部变量 线程 ID1, 整数型 .局部变量 线程 ID2, 整数型 ' 创建线程任务 1 线程 ID1 = 启动线程 (&线程任务_1, , ) ' 创建线程任务 2 线程 ID2 = 启动线程 (&线程任务_2, , ) ' 输出线程 ID 调试输出 ('线程任务 1 的 ID:', 线程 ID1) 调试输出 ('线程任务 2 的 ID:', 线程 ID2)
✅运行结果:线程任务 1 和线程任务 2 会交替输出 1-20,体现了多线程的并发执行特性。
在多线程环境下,多个线程可能会同时访问共享资源(如全局变量、程序集变量、文件等),导致数据不一致的问题(即'线程安全问题')。为了解决这个问题,我们需要使用线程同步机制,如临界区、互斥锁、信号量等。
.版本 2 .支持库 EThread ' 程序集数据段定义的共享资源 .程序集变量 共享变量,整数型,, 共享变量,初始值为 0 .子程序 线程任务_3,, 公开,线程任务 3(对共享变量进行 +1 操作) .局部变量 i, 整数型 .局部变量 临时变量,整数型 ' 对共享变量进行 10000 次 +1 操作 计次循环首 (10000, i) ' 读取共享变量的值 临时变量 = 共享变量 ' 模拟任务的耗时 延迟 (1) ' 对临时变量进行 +1 操作 临时变量 = 临时变量 + 1 ' 将临时变量的值写回共享变量 共享变量 = 临时变量 计次循环首结束
.版本 2 .支持库 EThread .子程序 线程任务_4,, 公开,线程任务 4(对共享变量进行 +1 操作) .局部变量 i, 整数型 .局部变量 临时变量,整数型 ' 对共享变量进行 10000 次 +1 操作 计次循环首 (10000, i) ' 读取共享变量的值 临时变量 = 共享变量 ' 模拟任务的耗时 延迟 (1) ' 对临时变量进行 +1 操作 临时变量 = 临时变量 + 1 ' 将临时变量的值写回共享变量 共享变量 = 临时变量 计次循环首结束
.版本 2 .支持库 EThread .子程序 _测试线程安全问题按钮_被单击 .局部变量 线程 ID1, 整数型 .局部变量 线程 ID2, 整数型 ' 初始化共享变量 共享变量 = 0 ' 创建线程任务 1 线程 ID1 = 启动线程 (&线程任务_3, , ) ' 创建线程任务 2 线程 ID2 = 启动线程 (&线程任务_4, , ) ' 等待两个线程任务完成 ' 简化处理,等待 5 秒 延迟 (5000) ' 输出共享变量的最终值 调试输出 ('共享变量的最终值:', 共享变量) ' 预期值为 20000,但实际值约为 10000 左右
✅运行结果:共享变量的最终值约为 10000 左右,远小于预期值 20000,说明存在线程安全问题。
临界区是一种简单的线程同步机制,它允许同一时间内只有一个线程进入临界区,访问共享资源。
.版本 2 .支持库 EThread ' 程序集数据段定义的共享资源和临界区变量 .程序集变量 共享变量_临界区,整数型,, 共享变量,初始值为 0 .程序集变量 临界区对象,整数型,, 临界区对象的句柄 .子程序 初始化临界区,, 公开,初始化临界区对象 ' 创建临界区对象 临界区对象 = 创建临界区 ()
.版本 2 .支持库 EThread .子程序 线程任务_5,, 公开,线程任务 5(对共享变量进行 +1 操作,使用临界区) .局部变量 i, 整数型 .局部变量 临时变量,整数型 ' 对共享变量进行 10000 次 +1 操作 计次循环首 (10000, i) ' 进入临界区 进入临界区 (临界区对象) ' 读取共享变量的值 临时变量 = 共享变量_临界区 ' 模拟任务的耗时 延迟 (1) ' 对临时变量进行 +1 操作 临时变量 = 临时变量 + 1 ' 将临时变量的值写回共享变量 共享变量_临界区 = 临时变量 ' 离开临界区 离开临界区 (临界区对象) 计次循环首结束
.版本 2 .支持库 EThread .子程序 线程任务_6,, 公开,线程任务 6(对共享变量进行 +1 操作,使用临界区) .局部变量 i, 整数型 .局部变量 临时变量,整数型 ' 对共享变量进行 10000 次 +1 操作 计次循环首 (10000, i) ' 进入临界区 进入临界区 (临界区对象) ' 读取共享变量的值 临时变量 = 共享变量_临界区 ' 模拟任务的耗时 延迟 (1) ' 对临时变量进行 +1 操作 临时变量 = 临时变量 + 1 ' 将临时变量的值写回共享变量 共享变量_临界区 = 临时变量 ' 离开临界区 离开临界区 (临界区对象) 计次循环首结束
.版本 2 .支持库 EThread .子程序 _测试临界区按钮_被单击 .局部变量 线程 ID1, 整数型 .局部变量 线程 ID2, 整数型 ' 初始化临界区对象 初始化临界区 () ' 初始化共享变量 共享变量_临界区 = 0 ' 创建线程任务 1 线程 ID1 = 启动线程 (&线程任务_5, , ) ' 创建线程任务 2 线程 ID2 = 启动线程 (&线程任务_6, , ) ' 等待两个线程任务完成 ' 简化处理,等待 25 秒 延迟 (25000) ' 输出共享变量的最终值 调试输出 ('共享变量的最终值(使用临界区):', 共享变量_临界区) ' 预期值为 20000,实际值为 20000
✅运行结果:共享变量的最终值为 20000,符合预期,说明临界区成功解决了线程安全问题。
员工薪资计算系统的功能需求包括:
.版本 2 .数据类型 部门信息类型 .成员 部门编号,文本型 .成员 部门名称,文本型 .成员 直接下属部门数组,部门信息类型,数组,, 嵌套的 UDT 数组,用于存储直接下属部门的信息 .成员 部门员工信息数组,员工信息类型,数组,, 部门员工信息数组 .子程序 计算部门总薪资_递归,双精度小数型,公开,递归计算部门总薪资(包括直接下属部门和部门员工的薪资) .参数 目标部门信息,部门信息类型,参考,要计算总薪资的目标部门信息(引用传递,提高效率) .局部变量 总薪资,双精度小数型 .局部变量 下属部门索引,整数型 .局部变量 部门员工索引,整数型 ' 计算目标部门员工的总薪资 计次循环首 (取数组成员数 (目标部门信息。部门员工信息数组), 部门员工索引) 总薪资 = 总薪资 + 计算员工总薪资 (&计算基础薪资,&计算绩效薪资,&计算加班薪资,&计算社保公积金扣除,&计算个人所得税,目标部门信息。部门员工信息数组 [部门员工索引]) 计次循环首结束 ' 递归计算直接下属部门的总薪资 计次循环首 (取数组成员数 (目标部门信息。直接下属部门数组), 下属部门索引) 总薪资 = 总薪资 + 计算部门总薪资_递归 (目标部门信息。直接下属部门数组 [下属部门索引]) 计次循环首结束 ' 返回部门总薪资 返回 (总薪资)
将基础薪资、绩效薪资、加班薪资、社保公积金扣除、个人所得税计算的子程序作为回调函数传递到计算员工总薪资的子程序中,实现薪资计算方式的灵活替换。
.版本 2 .支持库 spec .支持库 EThread ' 程序集数据段定义的回调函数指针类型 .程序集类型 基础薪资计算回调函数指针,双精度小数型,公开,基础薪资计算回调函数的指针类型 .参数 员工信息,员工信息类型,参考,员工信息(引用传递) .程序集类型 绩效薪资计算回调函数指针,双精度小数型,公开,绩效薪资计算回调函数的指针类型 .参数 员工信息,员工信息类型,参考,员工信息(引用传递) .程序集类型 加班薪资计算回调函数指针,双精度小数型,公开,加班薪资计算回调函数的指针类型 .参数 员工信息,员工信息类型,参考,员工信息(引用传递) .程序集类型 社保公积金扣除计算回调函数指针,双精度小数型,公开,社保公积金扣除计算回调函数的指针类型 .参数 员工信息,员工信息类型,参考,员工信息(引用传递) .程序集类型 个人所得税计算回调函数指针,双精度小数型,公开,个人所得税计算回调函数的指针类型 .参数 应纳税所得额,双精度小数型,, 应纳税所得额 .子程序 计算基础薪资,双精度小数型,公开,计算基础薪资(示例:固定基础薪资) .参数 员工信息,员工信息类型,参考,员工信息(引用传递) .局部变量 基础薪资,双精度小数型 ' 示例:根据员工部门和职位计算基础薪资 .如果 (员工信息。员工部门 = '技术部') .如果 (员工信息。员工职位 = '高级工程师') 基础薪资 = 15000.0 .否则 .如果 (员工信息。员工职位 = '中级工程师') 基础薪资 = 10000.0 .否则 .如果 (员工信息。员工职位 = '初级工程师') 基础薪资 = 6000.0 .如果结束 .如果结束 .如果结束 .否则 .如果 (员工信息。员工部门 = '销售部') 基础薪资 = 5000.0 ' 销售部基础薪资较低,主要靠绩效 .否则 基础薪资 = 4000.0 ' 其他部门基础薪资统一为 4000 .如果结束 .如果结束 返回 (基础薪资)
.版本 2 .支持库 spec .支持库 EThread .子程序 计算员工总薪资,双精度小数型,公开,计算员工总薪资(使用回调函数实现模块解耦) .参数 基础薪资计算回调,整数型,, 基础薪资计算回调函数的指针 .参数 绩效薪资计算回调,整数型,, 绩效薪资计算回调函数的指针 .参数 加班薪资计算回调,整数型,, 加班薪资计算回调函数的指针 .参数 社保公积金扣除计算回调,整数型,, 社保公积金扣除计算回调函数的指针 .参数 个人所得税计算回调,整数型,, 个人所得税计算回调函数的指针 .参数 员工信息,员工信息类型,参考,员工信息(引用传递) .局部变量 基础薪资,双精度小数型 .局部变量 绩效薪资,双精度小数型 .局部变量 加班薪资,双精度小数型 .局部变量 社保公积金扣除,双精度小数型 .局部变量 应纳税所得额,双精度小数型 .局部变量 个人所得税,双精度小数型 .局部变量 总薪资,双精度小数型 .局部变量 基础薪资计算函数,基础薪资计算回调函数指针,静态 .局部变量 绩效薪资计算函数,绩效薪资计算回调函数指针,静态 .局部变量 加班薪资计算函数,加班薪资计算回调函数指针,静态 .局部变量 社保公积金扣除计算函数,社保公积金扣除计算回调函数指针,静态 .局部变量 个人所得税计算函数,个人所得税计算回调函数指针,静态 ' 将回调函数指针转换为子程序变量 指针到子程序 (基础薪资计算函数,基础薪资计算回调) 指针到子程序 (绩效薪资计算函数,绩效薪资计算回调) 指针到子程序 (加班薪资计算函数,加班薪资计算回调) 指针到子程序 (社保公积金扣除计算函数,社保公积金扣除计算回调) 指针到子程序 (个人所得税计算函数,个人所得税计算回调) ' 计算各部分薪资 基础薪资 = 基础薪资计算函数 (员工信息) 绩效薪资 = 绩效薪资计算函数 (员工信息) 加班薪资 = 加班薪资计算函数 (员工信息) 社保公积金扣除 = 社保公积金扣除计算函数 (员工信息) ' 计算应纳税所得额 应纳税所得额 = 基础薪资 + 绩效薪资 + 加班薪资 - 社保公积金扣除 - 5000.0 ' 5000.0 为起征点 ' 计算个人所得税(应纳税所得额≤0 时,个人所得税为 0) .如果 (应纳税所得额 ≤ 0) 个人所得税 = 0.0 .否则 个人所得税 = 个人所得税计算函数 (应纳税所得额) .如果结束 ' 计算总薪资 总薪资 = 基础薪资 + 绩效薪资 + 加班薪资 - 社保公积金扣除 - 个人所得税 ' 输出各部分薪资 调试输出 ('员工编号:', 员工信息。员工编号) 调试输出 ('基础薪资:', 基础薪资) 调试输出 ('绩效薪资:', 绩效薪资) 调试输出 ('加班薪资:', 加班薪资) 调试输出 ('社保公积金扣除:', 社保公积金扣除) 调试输出 ('应纳税所得额:', 应纳税所得额) 调试输出 ('个人所得税:', 个人所得税) 调试输出 ('总薪资:', 总薪资) ' 返回总薪资 返回 (总薪资)
💡本章总结:本章主要介绍了易语言子程序的高级应用,包括递归调用机制与实现(经典案例、栈溢出风险规避)、参数传递的高级形式(值传递与引用传递的区别、数组参数、UDT 参数)、回调函数的原理与易语言实现(事件驱动型、指针驱动型)、多线程环境下的子程序调用(线程同步基础),以及真实案例员工薪资计算系统的模块解耦与递归应用。 ⚠️注意事项:在使用递归时,必须设置合理的终止条件,避免栈溢出;在使用引用传递时,要注意对实参的修改会直接影响原始值;在使用指针类型的变量时,要确保指针操作的正确性,避免内存访问错误;在使用多线程时,要注意线程安全问题,使用适当的线程同步机制。 ✅学习成果:通过本章的学习,读者已经掌握了易语言子程序的高级应用,可以开发出功能更强大、结构更清晰、性能更高的易语言程序,并为后续学习面向对象编程(初步)和数据库操作奠定基础。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online