VB 程 式 设 计 内 功 讲 座 (叁) - 王 国 荣
Bug 是 什 麽 ? 有 一 句 名 言 最 能 诠 释 bug 的 意 义 :「电 脑 是 按 照 指 令 行 事 , 而 不 是 人 的 意 志」, 当 人 将 其 意 志 转 化 成 指 令 下 给 电 脑 之 後 , 而 电 脑 执 行 的 结 果 与 人 的 意 志 不 符 时 , 便 是 bug。
怎 样 下 指 令 给 电 脑 呢 ? 若 是 一 般 的 使 用 者 , 使 用 的 是 作 业 系 统 所 提 供 的 指 令 (例 如 copy 指 令 )或 应 用 程 式 所 提 供 的 操 作 介 面 (当 使 用 者 操 作 应 用 程 式 时 , 应 用 程 式 即 已 代 替 使 用 者 下 指 令 给 电 脑 了 )。
然 而 对 程 式 设 计 人 员 而 言 , 下 指 令 给 电 脑 的 过 程 就 比 较 复 杂 了 , 首 先 必 须 选 取 适 当 的 程 式 语 言 , 然 後 将 自 己 期 望 电 脑 做 的 事 情 表 示 成 程 式 语 言 , 接 着 再 利 用 程 式 开 发 工 具 编 译 成 执 行 档 , 最 後 才 经 由 作 业 系 统 所 提 供 的 指 令 载 入 执 行 档 , 由 电 脑 开 始 执 行 , 理 论 上 , 以 上 的 每 一 个 过 程 都 可 能 发 生 错 误 , 例 如 程 式 语 言 选 错 了 、 程 式 写 错 了 、 编 译 器 有 bug 而 编 译 出 错 误 的 执 行 档 、 作 业 系 统 有 bug… , 想 一 想 还 真 可 怕 , 到 处 都 可 能 有 bug, 这 也 莫 怪 有 人 要 说「软 体 是 高 风 险 的 事 业」。
不 过 本 文 所 讨 论 的 范 围 仅 局 限 於「程 式 写 错」的 部 分 , 首 先 我 们 只 能 假 设 编 译 器 是 没 有 错 的 , 作 业 系 统 是 没 有 错 的 , 这 就 好 像 我 们 搭 飞 机 以 前 必 须 假 设 飞 机 是 不 会 出 事 的 一 样 , 如 果 您 十 分 在 乎 飞 机 、 编 译 器 、 作 业 系 统 的 极 少 数 出 错 情 况 , 那 麽 笔 者 只 能 说 , 别 搭 飞 机 , 也 别 当 个 程 式 设 计 人 员 。
有 句 话 说 :「人 非 圣 贤 , 孰 能 无 过」, 而 笔 者 想 说 的 是「程 式 设 计 无 圣 贤」, 从 开 始 写 第 一 行 程 式 就 不 会 出 错 , 笔 者 未 曾 听 过 啊 ! 知 错 能 改 , 乃 程 式 设 计 之 根 本 大 法 也 。 记 得 以 前 在 学 校 的 时 候 , 老 师 总 喜 欢 讲「知 错 能 改 , 善 莫 大 焉」, 当 老 师 者 焉 知 数 学 题 作 错 了 , 就 是 不 懂 啊 ! 不 懂 者 又 如 何 知 错 , 又 如 何 能 改 呢 ? 对 程 式 设 计 来 说 , 道 理 也 是 相 同 的 ,「知 错 能 改」说 得 简 单 , 做 起 来 却 十 分 困 难 , 笔 者 写 了 十 几 年 的 程 式 , 还 是 必 须 面 临 bug 上 身 的 问 题 , 不 过 也 正 因 为 如 此 , 本 期 要 与 您 谈 一 谈 除 错 的 方 法 !
肉 眼 除 错 指 的 是 将 程 式 印 出 来 或 是 盯 着 萤 幕 一 行 一 行 检 查 , 这 听 起 来 有 点 不 太 入 流 , 主 要 的 缺 点 是 这 个 方 法 的 除 错 效 率 不 高 , 尤 其 对 於 自 己 写 的 程 式 , 总 认 为 程 式「应 该」按 照 自 己 的 意 思 执 行 , 而 没 有 看 出 程 式「实 际 上」是 怎 麽 执 行 的 。
肉 眼 所 观 察 出 来 的 错 误 , 通 常 都 还 不 是 十 分 肯 定 , 因 此 接 下 来 是 修 改 程 式 、 编 译 、 联 结 、 测 试 , 然 後 才 能 确 定 是 否 找 到 了 错 误 , 如 果 肉 眼 除 错 一 直 无 法 找 出 错 误 , 那 麽「侦 错 、 修 改 程 式 、 编 译 、 联 结 、 测 试」等 动 作 就 得 一 再 重 来 , 非 常 浪 费 时 间 。
肉 眼 除 错 与 程 式 设 计 人 员 对 程 式 语 言 的 了 解 也 有 极 大 的 关 系 , 笔 者 并 不 全 然 反 对 这 个 方 法 , 因 为 在 侦 错 的 过 程 中 , 程 式 员 通 常 会 去 思 索 程 式 为 什 麽 错 了 , 这 对 程 式 语 言 的 了 解 颇 有 帮 助 , 而 且 也 可 以 训 练 程 式 员 对 於 错 误 的 敏 锐 性 , 不 过 就 像 前 面 所 说 的 , 这 个 方 法 的 效 率 不 高 , 除 了 给 自 己 这 方 面 的 训 练 之 外 , 别 忘 了「时 程 控 制」在 程 式 开 发 中 也 是 非 常 重 要 的 一 件 事 , 因 此 请 再 参 考 以 下 更 有 效 率 的 除 错 技 巧 。
VB 所 提 供 的 侦 错 功 能 相 当 丰 富 , 值 得 一 试 。
程 式 错 了 , 侦 测 的 对 象 不 外 乎 程 式 码 或 资 料 (变 数 及 物 件 ), 而 这 两 者 的 关 系 是 : 程 式 码 是「因」、 资 料 是「果」, 虽 然 说 我 们 要 找 出 错 误 的 因 , 但 是 在 侦 测 的 过 程 中 , 却 必 须 从 果 来 着 手 , 因 为 果 比 因 来 得 容 易 观 察 , 直 到 发 现 某 一 资 料 的 结 果 错 误 时 , 再 逐 步 清 查 错 误 原 因 。
在 检 视「果」(资 料 )的 功 能 中 , VB 所 提 供 的 有 :
◇ 即 时 运 算 视 窗 : 即 时 运 算 视 窗 是 一 个 BASIC 语 言 的 编 译 器 , 我 们 在 里 面 所 输 入 的 指 令 将 会 立 刻 被 执 行 , 因 此 当 我 们 怀 疑 某 个 变 数 可 能 有 错 时 , 只 要 利 用「? 变 数 名 称」即 可 加 以 检 验 , 此 外 , 它 也 可 以 用 来 执 行 副 程 式 、 函 数 、 及 合 法 的 VB 叙 述 。
◇ 区 域 变 数 视 窗 : 当 我 们 将 程 式 中 断 於 某 一 个 程 序 (包 含 副 程 式 、 函 数 、 及 事 件 程 序 )时 , 区 域 变 数 视 窗 就 会 显 示 该 程 序 的 所 有 区 域 变 数 , 如 此 一 来 , 不 必 在 即 时 运 算 视 窗 输 入「? 变 数 名 称」即 可 以 看 到 区 域 变 数 的 内 容 。 区 域 变 数 视 窗 除 了 会 显 示 某 一 程 序 的 区 域 变 数 之 外 , 也 会 显 示 该 程 序 所 在 模 组 的 全 域 变 数 。
◇ 监 看 视 窗 : 区 域 变 数 视 窗 会 显 示 所 有 区 域 变 数 的 内 容 , 但 如 果 变 数 过 多 , 便 不 容 易 观 察 , 监 看 视 窗 则 只 显 示 特 别 观 察 的 变 数 。
◇ 程 式 视 窗 : 程 式 视 窗 是 检 测 程 式 码 执 行 过 程 的 视 窗 , 不 过 VB5 特 别 增 加 了 一 个 方 便 的 功 能 : 在 中 断 模 式 底 下 , 只 要 将 滑 鼠 游 标 移 到 变 数 的 上 面 , 程 式 视 窗 即 会 以 黄 色 条 块 显 示 该 变 数 的 内 容 。
以 上 的 功 能 座 落 在 功 能 表 的「检 视」栏 底 下 。
在 检 测「因」(程 式 码 )方 面 , VB 所 提 供 的 功 能 有 :
◇ 设 定 中 断 点 : 可 将 某 一 行 叙 述 设 定 成 中 断 点 , 则 当 程 式 执 行 到 此 一 叙 述 时 , 程 式 即 会 进 入 中 断 模 式 , 而 只 有 在 中 断 模 式 底 下 , 我 们 才 能 够 利 用 即 时 运 算 视 窗 、 区 域 变 数 视 窗 、 监 看 视 窗 、 及 程 式 视 窗 检 视 变 数 的 内 容 。
◇ 逐 行 执 行 : 每 执 行 一 行 叙 述 程 式 即 进 入 中 断 模 式 , 此 一 功 能 可 让 我 们 检 视 每 一 行 叙 述 执 行 前 後 的 结 果 。
◇ 逐 程 序 执 行 : 与 逐 行 执 行 类 似 , 但 遇 到 程 序 的 呼 叫 时 , 会 执 行 过 被 呼 叫 程 序 的 所 有 叙 述 , 才 中 断 於 呼 叫 程 序 端 的 下 一 行 叙 述 。
◇ 跳 出 程 序 : 当 程 式 中 断 於 某 一 个 程 序 , 而 我 们 不 想 再 逐 行 执 行 时 , 此 时 可 以 使 用 此 一 功 能 一 路 执 行 完 此 一 程 序 , 而 让 中 断 点 回 到 呼 叫 端 的 下 一 个 叙 述 。 (请 注 意 此 一 功 能 并 非 脱 离 程 序 不 执 行 )
◇ 执 行 至 游 标 处 : 在 某 一 程 序 中 , 既 不 想 逐 行 执 行 , 也 不 想 跳 出 程 序 , 可 以 先 将 输 入 游 标 设 定 於 某 一 叙 述 (在 叙 述 上 面 按 下 滑 鼠 ), 然 後 选 取 此 一 功 能 , 则 程 式 会 执 行 到 输 入 游 标 处 , 方 才 中 断 。
◇ 设 定 下 个 执 行 点 : 此 一 功 能 有 点 像 是 VB 的 Goto 叙 述 , 将 会 跳 过 (不 执 行 )中 间 的 所 有 叙 述 。
◇ 呼 叫 堆 叠 : 可 显 示 程 序 呼 叫 的 所 有 历 程 。
以 上 的 功 能 除 了「呼 叫 堆 叠」位 於 功 能 表 的「检 视」栏 底 下 之 外 , 其 他 功 能 则 座 落 在 功 能 表 的「侦 错」栏 底 下 。
程 式 执 行 时 是 无 法 侦 错 的 , 我 们 一 定 要 先 让 程 式 停 下 来 , 才 能 检 测 各 个 变 数 的 内 容 , 所 以 使 用 VB 侦 错 功 能 的 第 一 步 是 让 VB 程 式 能 够 停 下 来 , 而 以 下 是 中 断 程 式 方 法 :
开 启 程 式 视 窗 , 在 我 们 希 望 中 断 的 叙 述 上 面 按 下 F9, 则 将 来 程 式 执 行 到 此 一 叙 述 时 , 即 会 进 入 中 断 模 式 , 此 时 我 们 可 以 使 用 各 种 检 视 变 数 的 功 能 来 检 查 截 至 此 一 叙 述 以 前 执 行 的 结 果 。 (特 别 注 意 : 所 谓 中 断 点 是 还 没 有 执 行 的 叙 述 , 不 是 已 经 执 行 而 停 下 来 的 叙 述 )
设 定 中 断 点 的 另 一 个 方 法 是 使 用 Stop 叙 述 , 当 程 式 执 行 到 Stop 叙 述 时 , 也 会 进 入 中 断 模 式 。 使 用 Stop 叙 述 有 一 得 及 一 失 , 得 的 方 面 是 中 断 点 可 以 储 存 在 程 式 之 中 , 不 像 F9 所 设 定 的 中 断 点 , 在 专 案 结 束 时 即 告 消 失 , 失 的 方 面 则 是 含 有 Stop 叙 述 的 程 式 被 编 译 成 执 行 档 之 後 , 只 要 程 式 值 执 行 到 Stop 叙 述 即 会 结 束 执 行 , 这 使 得 我 们 在 程 式 编 译 成 执 行 档 之 前 必 须 把 所 有 的 Stop 叙 述 拿 掉 。
程 式 的 错 误 按 严 重 性 可 分 成「不 可 处 理」与「可 处 理」两 种 , 在 Windows 环 境 底 下 , 有 时 候 我 们 会 看 到 如 图 -1的 讯 息 窗 , 当 程 式 出 现 此 一 讯 息 窗 时 , 就 难 逃 被 踢 出 系 统 的 命 运 , 此 为 程 式 发 生 了 不 可 处 理 的 错 误 。
图 -1 不 可 处 理 的 错 误 所 显 示 的 讯 息 窗
不 可 处 理 的 错 误 最 常 发 生 於 程 式 企 图 将 资 料 写 入 於 不 该 写 入 的 记 忆 体 之 中 , 一 般 而 言 , C/C++ 程 式 由 於 可 直 接 操 作 记 忆 体 的 位 址 , 最 容 易 发 生 这 种 事 情 , VB 程 式 则 由 於 无 法 直 接 操 作 记 忆 体 的 位 址 , 绝 少 发 生 类 似 於 C/C++ 的 错 误 , 因 此 本 文 只 讨 论 可 处 理 的 错 误 。
发 生 可 处 理 的 错 误 时 , VB 会 显 示 错 误 的 讯 息 窗 , 例 如 某 一 阵 列 X 的 注 标 范 围 是 0 到 3, 则 执 行「X(4) = 123」时 将 会 产 生 如 图 -2的 可 处 理 错 误 。
图 -2 VB 所 显 示 的 可 处 理 错 误
此 时 我 们 若 按 下「侦 错」钮 , 即 可 使 程 式 进 入 中 断 模 式 , 而 此 时 中 断 点 将 停 留 在 发 生 错 误 的 叙 述 上 面 。 由 於 可 处 理 的 错 误 是 VB 侦 测 出 来 的 , 接 下 来 进 入 中 断 模 式 之 後 , 我 们 甚 至 可 以 把 错 误 的 叙 述 改 掉 , 然 後 继 续 执 行 程 式 。
不 管 任 何 时 间 , 只 要 我 们 按 下 Ctrl+Break 键 , 则 VB 程 式 即 会 进 入 中 断 模 式 , 此 一 方 法 在 程 式 进 入 无 穷 回 圈 时 最 为 实 用 , 例 如 以 下 程 式 :
Dim A As Single
A = 0
Do
A = A + 0.01
Loop Until A = 1
乍 看 之 下 , 程 式 并 不 会 进 入 无 穷 回 圈 , 但 实 际 上 却 因 为 电 脑 处 理 小 数 点 时 会 有 些 微 的 误 差 , 以 致 A 不 会 刚 好 等 於 1, 而 使 得 程 式 进 入 了 无 穷 回 圈 , 当 程 式 进 入 无 穷 回 圈 时 , VB 可 以 说 是 动 弹 不 得 , 唯 独 Ctrl+Break 可 以 中 断 程 式 。
在 VB 的 工 作 环 境 底 下 , 按 下 F5 执 行 程 式 是 最 普 遍 的 方 式 , 而 按 下 F5 执 行 程 式 必 须 采 用 上 述 的 方 法 才 能 够 中 断 程 式 , 如 果 我 们 一 开 始 就 按 下 F8, 也 可 以 执 行 程 式 , 但 将 来 只 要 执 行 一 行 叙 述 , 程 式 便 会 进 入 中 断 模 式 , 接 着 在 中 断 模 式 底 下 , 只 要 我 们 继 续 按 下 F8, 则 仍 然 维 持 一 次 执 行 一 行 叙 述 的 方 式 , 这 是 VB 所 提 供 的 逐 行 执 行 的 功 能 。
VB 所 提 供 的 侦 错 功 能 的 确 不 少 , 当 我 们 进 行 程 式 的 侦 错 时 , 该 如 何 选 择 适 当 的 功 能 呢 ? 以 下 让 笔 者 以 实 例 的 案 例 来 说 明 。
对 於 刚 刚 写 好 而 又 没 有 呼 叫 其 他 自 定 程 序 的 程 式 而 言 , 使 用 逐 行 执 行 来 测 试 最 为 适 合 , 举 例 来 说 , 以 下 是 一 个 互 换 两 个 变 数 内 容 的 副 程 式 :
Sub Swap(A, B)
Dim temp
temp = A
A = B
B = temp
End Sub
假 设 我 们 不 确 定 副 程 式 执 行 的 结 果 是 否 正 确 , 可 以 采 用 逐 行 执 行 的 方 式 , 如 下 :
1. 输 入 以 上 副 程 式 , 然 後 在 表 单 上 布 置 一 个 命 令 钮 , 并 且 在 命 令 钮 的 Click 事 件 程 序 中 撰 写 以 下 程 式 :
Private Sub Command1_Click()
X = 100
Y = 200
Swap X, Y
End Sub
2. 按 下 F8 执 行 程 式 , 然 後 按 下 表 单 上 的 命 令 钮 , 此 时 程 式 视 窗 会 弹 到 萤 幕 的 最 前 端 , 而 在 程 式 视 窗 中 标 示 着 箭 号 的 叙 述 为 下 一 个 被 执 行 的 叙 述 , 如 图 -3, 持 续 按 下 F8, 直 到 程 式 进 入 Swap 副 程 式 为 止 。
图 -3 程 式 视 窗 中 下 一 个 被 执 行 的 叙 述
3. 进 入 Swap 副 程 式 之 後 , 选 取 VB 功 能 表 的「检 视 /区 域 变 数 视 窗」, 此 时 所 调 出 的 区 域 变 数 视 窗 如 图 -4。
图 -4 利 用 区 域 变 数 视 窗 检 验 变 数 的 内 容
4. 接 着 还 是 以 F8 逐 行 执 行 程 式 , 而 由 於 按 下 F8 只 执 行 一 行 程 式 , 因 此 我 们 可 以 在 每 执 行 一 行 程 式 之 後 , 即 检 验 区 域 变 数 视 窗 中 A、 B、 temp 变 数 的 结 果 是 否 正 确 , 若 有 错 误 便 可 以 轻 易 地 找 到 。
如 果 程 式 比 较 大 或 含 有 回 圈 , 就 不 适 合 采 用 逐 行 执 行 的 侦 测 技 巧 , 此 时 我 们 通 常 会 每 隔 一 段 程 式 设 定 一 个 中 断 点 , 并 且 在 程 式 执 行 到 中 断 点 时 检 验 变 数 的 内 容 是 否 正 确 , 如 果 正 确 便 继 续 向 下 测 试 , 如 果 不 正 确 , 即 表 示 这 个 中 断 点 与 上 一 个 中 断 点 之 间 的 程 式 有 错 。
接 着 还 是 让 笔 者 以 实 例 来 说 明 侦 测 的 过 程 , 假 设 有 一 副 程 式 如 下 , 此 一 副 程 式 的 作 用 是 以 空 白 为 分 割 字 元 , 将 参 数 Sin 字 串 分 割 成 多 个 字 串 , 并 且 存 放 在 Sout 字 串 阵 列 中 , 而 参 数 N 则 会 记 录 放 到 Sout 之 中 的 字 串 数 目 , 但 这 个 副 程 式 有 bug, 以 下 让 我 们 找 出 bug 所 在 , 并 且 加 以 修 正 :
' 参 数 一 Sin : 输 入 的 字 串
' 参 数 二 Sout: 传 回 的 字 串 阵 列
' 参 数 叁 N : 传 回 的 字 串 数 目
Sub Parse(ByVal Sin As String, Sout() As String, N As Integer)
Dim pos As Integer
N = 0
S = Trim(S)
Do
pos = InStr(Sin, " ") ' 找 出 空 白 字 元 的 所 在 位 置
If pos > 0 Then ' 如 果 含 有 空 白 字 元
N = N + 1
Sout(N) = Left(Sin, pos - 1)
Sin = Trim(Mid(Sin, pos + 1))
End If
Loop Until pos = 0
End Sub
1. 输 入 以 上 副 程 式 , 然 後 在 表 单 上 布 置 一 个 命 令 钮 , 并 且 在 命 令 钮 的 Click 事 件 程 序 中 撰 写 以 下 程 式 :
Private Sub Command1_Click()
Dim X(1 To 10) As String, N As Integer
Parse "AAA BBB CCC", X, N
End Sub
2. 假 设 Parse 副 程 式 是 正 确 的 , 所 以 执 行「Parse "AAA BBB CCC", X, N」之 後 , 我 们 预 期 的 结 果 是「X(1)="AAA"、 X(2)="BBB"、 X(3)="CCC"、 N=3」, 为 了 检 验 结 果 , 首 先 将 输 入 游 标 移 到「Parse "AAA BBB CCC", X, N」的 下 一 行 叙 述 , 然 後 按 下 F9 将 中 断 点 设 定 在 此 一 叙 述 之 後 。
3. 按 下 F5 执 行 程 式 , 然 後 再 按 下 表 单 上 的 命 令 钮 , 使 程 式 执 行 过「Parse "AAA BBB CCC", X, N」, 接 着 会 进 入 中 断 模 式 , 此 时 请 检 视 区 域 变 数 视 窗 的 X 及 N, 结 果「X(1)="AAA"、 X(2)="BBB"、 N=2」, 与 我 们 期 望 的 不 符 合 , 这 表 示 Parse 副 程 式 是 有 错 误 的 。
4. 要 测 试 Parse 副 程 式 , 采 用「逐 行 执 行」固 然 也 可 行 , 但 比 较 节 省 时 间 的 作 法 是 将「Do」及「Loop Until pos = 0」两 行 叙 述 设 定 成 中 断 点 , 因 为 我 们 可 以 在 每 次 执 行 回 圈 之 前 先 检 视 Sin、 Sout、 及 N 参 数 的 内 容 , 而 每 执 行 回 圈 一 次 , 这 几 个 参 数 的 内 容 应 该 都 会 改 变 才 对 , 所 以 也 在「Loop Until pos = 0」叙 述 上 面 设 定 中 断 点 , 以 检 验 结 果 。
5. 设 定 好 中 断 点 之 後 , 按 下 F5 及 命 令 钮 执 行 程 式 , 并 且 在 每 次 中 断 时 检 查 Sin、 Sout、 及 N 变 数 的 内 容 , 然 後 再 按 下 F5 执 行 程 式 , 结 果 各 次 检 验 的 结 果 如 下 :
| 中 断 点 | 检 验 结 果 |
| 进 入 Do 时 | Sin="AAA BBB CCC", N=0 |
| 第 1次 的 Loop Until | Sin="BBB CCC", N=1, Sout(1)="AAA" |
| 第 2次 的 Loop Until | Sin="CCC", N=2, Sout(1)="AAA", Sout(2)="BBB" |
| 第 3次 的 Loop Until | Sin="CCC", N=2, Sout(1)="AAA", Sout(2)="BBB" |
检 验 到 第 3 次 的 Loop Until 时 , 我 们 可 以 发 现 在 Sin 等 於 "CCC"(不 含 空 白 字 元 )时 , 程 式 并 未 将 它 设 定 给 Sout(3), 所 以 Parse 副 程 式 中 的 If-End If 叙 述 需 修 正 如 下 :
If pos > 0 Then ' 如 果 含 有 空 白 字 元
N = N + 1
Sout(N) = Left(Sin, pos - 1)
Sin = Trim(Mid(Sin, pos + 1))
Else ' 如 果 未 含 空 白 字 元
N = N + 1
Sout(N) = Sin
End If
在 上 一 个 测 试 的 例 子 中 , 不 知 道 您 会 使 用 哪 一 种 视 窗 来 检 验 变 数 的 内 容 , 笔 者 使 用 的 是 监 看 视 窗 , 因 为 区 域 变 数 视 窗 会 列 出 所 有 的 变 数 , 看 起 来 比 较 凌 乱 , 使 用 监 看 视 窗 则 是 指 定 想 要 检 视 的 变 数 , 相 对 之 下 就 清 爽 多 了 。 承 续 上 一 个 例 子 , 假 设 我 们 想 将 Parse 副 程 式 的 Sin 变 数 加 入 於 监 看 视 窗 中 , 则 方 法 如 下 :
1. 选 取 功 能 表 的「检 视 /监 看 视 窗」, 然 後 在「监 看 视 窗」上 面 按 下 滑 鼠 右 钮 , 待 出 现 快 显 功 能 表 时 , 选 取「新 增 监 看 式」命 令 。
2. 待 出 现「新 增 监 看 式」交 谈 窗 时 , 分 别 在「程 序」栏 位 中 选 取「Parse」, 在「运 算 式」栏 位 输 入 Sin, 则 Sin 变 数 即 会 成 为 被 监 看 的 变 数 。
使 用 监 看 式 还 有 另 一 个 特 殊 的 功 能 , 以 下 笔 者 直 接 以 实 例 来 说 明 , 假 设 一 回 圈 如 下 :
Dim A As Single
A = 0
Do
A = A + 0.01
Loop Until A = 1
此 一 程 式 会 进 入 无 穷 回 圈 , 假 设 我 们 不 清 楚 为 什 麽 , 所 以 想 利 用 VB 侦 错 的 功 能 加 以 检 验 , 此 时 如 果 我 们 将 中 断 点 设 定 在「Loop Until A=1」, 则 为 了 让 A 的 值 接 近 於 1 以 检 验 为 什 麽「A = 1」不 成 立 , 所 需 按 下 F5 持 续 执 行 程 式 的 次 数 大 约 是 100 次 , 这 好 像 太 辛 苦 了 一 点 , 所 以 便 有 了 以 下 的 方 法 :
1. 在「新 增 监 看 式」交 谈 窗 中 , 输 入「A > 0.95」到「运 算 式」栏 位 中 , 然 後 选 取「监 看 方 式」栏 位 中 的「数 值 由 False改 变 为 True时 中 断」, 如 图 -5, 此 一 设 定 的 作 用 是 当 A > 0.95 时 , 使 程 式 进 入 中 断 模 式 。
图 -5 条 件 式 中 断 设 定 法
2. 除 了 新 增「A > 0.95」的 监 看 式 之 外 , 也 增 加 变 数 A 的 监 看 式 , 然 後 按 下 F5 执 行 程 式 , 结 果 在 A=0.9599994 时 进 入 中 断 模 式 , 您 可 以 发 现 A 竟 然 不 等 於 9.6, 这 是 电 脑 在 小 数 点 方 面 的 些 微 误 差 所 致 。
3. 接 下 来 则 利 用 F8 逐 行 执 行 程 式 , 果 然 发 现 A 最 接 近 1 的 两 个 值 分 别 是 0.9999993 及 1.009999, 并 没 有 出 现 等 於 1 的 数 值 , 这 是 以 上 回 圈 会 进 入 无 穷 回 圈 的 原 因 。
对 於 比 较 复 杂 的 程 式 而 言 , 程 序 连 续 向 下 呼 叫 的 情 况 时 有 所 见 , 当 程 式 中 断 於 某 一 程 序 时 , 我 们 往 往 也 需 要 知 道 整 个 呼 叫 的 过 程 中 , 是 从 哪 一 个 程 序 开 始 呼 叫 的 , 经 过 哪 些 程 序 , 执 行 过 哪 些 叙 述 , 如 此 方 可 正 确 地 判 断 目 前 所 呈 现 的 结 果 是 为 何 而 来 。
当 呼 叫 历 程 超 过 两 个 程 序 以 上 , 而 且 又 进 入 中 断 模 式 时 , VB 便 允 许 我 们 选 取 功 能 表 的「检 视 /呼 叫 堆 叠」以 显 示 程 序 的 呼 叫 历 程 , 举 例 来 说 , Command_Click 程 序 呼 叫 了 Insert 副 程 式 , 而 Insert 副 程 式 又 呼 叫 了 Swap 副 程 式 , 则 当 程 式 中 断 於 Swap 副 程 式 时 , 则 选 取 功 能 表 的「检 视 /呼 叫 堆 叠」, 可 显 示 如 图 -6 的「呼 叫 堆 叠」视 窗 。
图 -6 呼 叫 堆 叠 视 窗
此 时 我 们 可 以 选 取 并 且 显 示 呼 叫 堆 叠 视 窗 中 的 任 何 一 个 程 序 , 接 着 被 选 取 的 程 序 便 会 显 示 在 程 式 视 窗 之 中 , 并 且 以 箭 号 标 示 出 此 一 程 序 最 後 一 个 被 执 行 的 叙 述 , 如 果 想 要 确 知 程 式 执 行 的 经 过 , 则 只 要 逐 一 选 取 并 且 显 示 呼 叫 堆 叠 视 窗 中 的 每 一 个 程 序 即 可 。
此 一 功 能 对 於 多 模 组 程 式 的 侦 测 很 有 帮 助 , 因 为 即 使 是 跨 越 不 同 模 组 的 呼 叫 也 都 会 显 示 在 呼 叫 堆 叠 视 窗 中 , 而 藉 此 我 们 可 以 浏 览 程 序 完 整 的 呼 叫 过 程 , 不 必 为 了 寻 找 不 同 程 序 而 在 各 个 模 组 之 间 切 来 切 去 。
以 上 所 介 绍 的 侦 错 功 能 都 必 须 在 中 断 模 式 进 行 , 但 也 许 我 们 觉 得 程 式 已 经 测 试 得 差 不 多 了 , 应 该 不 会 有 bug 了 , 於 是 不 再 中 断 程 式 , 而 让 程 式 一 路 执 行 下 去 , 并 且 期 望 它 能 够 正 确 地 执 行 , 但 往 往 事 与 愿 违 , 结 果 又 出 现 了 bug, 此 时 该 怎 麽 办 呢 ? 当 然 必 须 重 新 设 定 中 断 点 重 新 侦 错 , 然 而 随 着 程 式 越 写 越 大 , 必 须 设 定 中 断 点 的 地 方 也 越 来 越 多 , 而 重 新 设 定 的 工 作 也 会 越 来 越 沈 重 , 由 此 可 见 只 使 用 以 上 的 侦 错 功 能 仍 然 是 不 够 的 。
为 了 解 决 上 述 的 问 题 , 我 们 通 常 会 在 程 式 之 中 比 较 关 键 的 地 方 埋 设「侦 测 码」, 举 例 来 说 , 呼 叫 某 一 副 程 式 之 後 , 将 呼 叫 的 结 果 显 示 出 来 , 以 检 测 副 程 式 是 否 正 确 执 行 , 而 当 我 们 埋 设 很 多 侦 测 码 之 後 , 将 来 只 要 程 式 一 执 行 , 就 不 断 会 有 阶 段 性 的 执 行 结 果 被 显 示 出 来 。 若 程 式 执 行 正 确 , 我 们 可 以 不 去 理 会 阶 段 性 的 执 行 结 果 , 如 果 发 生 错 误 , 才 利 用 阶 段 性 的 执 行 结 果 清 查 首 度 出 现 错 误 的 程 式 段 , 然 後 将 侦 错 的 火 力 集 中 在 这 段 程 式 上 面 。
显 示 阶 段 性 的 执 行 结 果 , VB 程 式 常 用 的 方 法 有 二 : MsgBox 及 Debug.Print。 使 用 MsgBox 的 好 处 是 会 显 示 讯 息 窗 , 等 我 们 看 清 楚 结 果 後 , 才 按 下「确 定」钮 关 闭 讯 息 窗 , 但 笔 者 并 不 鼓 励 您 使 用 MsgBox, 因 为 它 会 影 响 视 窗 或 控 制 元 件 的 某 些 状 态 (主 要 是「作 用 中」及「非 作 用 中」状 态 ), 进 而 引 发 其 他 的 事 件 , 至 於 Debug.Print 则 是 把 结 果 输 出 於 即 时 运 算 视 窗 中 , 是 笔 者 比 较 建 议 的 方 法 。
使 用 Debug.Print 好 处 多 多 , 首 先 我 们 不 必 中 断 程 式 , 只 要 开 启 即 时 运 算 视 窗 , 就 可 以 看 到 Debug.Print 输 出 的 结 果 , 此 外 , 程 式 编 译 成 执 行 档 时 它 并 不 会 被 编 译 进 来 , 不 像 大 部 分 的 程 式 语 言 , 在 程 式 最 後 编 译 成 执 行 档 之 前 , 必 须 先 拿 掉 侦 测 码 。
最 後 笔 者 想 提 醒 您 一 点 , 除 去 所 有 已 知 的 错 误 并 不 表 示 程 式 就 没 有 错 误 了 , 这 就 好 像 没 看 到 蟑 螂 并 不 表 示 家 里 没 有 蟑 螂 了 , 如 何 写 出 没 有 错 误 的 程 式 , 又 是 另 一 个 挑 战 性 的 课 题 , 但 这 才 是 程 式 最 後 的 目 标 , 不 是 吗 ?