For and While Loop
本节是《Solidity by Example》的中文翻译与深入讲解,专为零基础或刚接触区块链开发的小白朋友打造。我们将通过“示例 + 解说 + 提示”的方式,带你逐步理解每一段 Solidity 代码的实际用途与背后的逻辑。
Solidity 是以太坊等智能合约平台使用的主要编程语言,就像写网页要用 HTML 和 JavaScript,写智能合约就需要会 Solidity。
如果你从没写过区块链代码也没关系,只要你了解一点点编程概念,比如“变量”“函数”“条件判断”,我们就能从最简单的例子开始,一步步建立你的 Solidity 编程思维。
For and While Loop
Solidity 支持 for、while 和 do while 循环。
不要编写无界循环(无限循环),因为这可能会达到 Gas 限制,导致交易失败。
由于上述原因,while 和 do while 循环很少使用。
- 循环支持:
- Solidity 提供三种循环结构:
- for 循环:适合已知迭代次数的场景。
- while 循环:适合条件驱动的循环。
- do while 循环:类似 while 循环,但至少执行一次(本例中未展示)。
- 这些循环与传统编程语言(如 C++、JavaScript)中的循环类似,用于重复执行代码块。
- Solidity 提供三种循环结构:
- 无界循环的危险:
- 无界循环(例如无限循环或迭代次数过大的循环)可能耗尽交易的 Gas 限制(Gas Limit),导致交易失败。
- 失败的交易会回滚状态变更,但已消耗的 Gas 费用不会退还。
- 因此,Solidity 开发者需要谨慎设计循环,确保迭代次数可控。
- while 和 do while 循环的稀少使用:
- 由于难以预测循环次数,
while
和do while
循环可能导致 Gas 超支,因此在智能合约中较少使用。 for
循环更常见,因为可以通过初始化、条件和步进来控制迭代次数。
- 由于难以预测循环次数,
// SPDX-License-Identifier: MIT
// 声明代码采用 MIT 开源许可证,这是一种常见的开源许可协议,允许自由使用、修改和分发代码。pragma solidity ^0.8.26;
// 指定 Solidity 编译器版本必须大于或等于 0.8.26 并且小于 0.9.0。
// `pragma` 指令确保合约使用兼容的编译器版本,`^0.8.26` 表示支持 0.8.26 或更高版本(但不超过 0.9.0)。contract Loop {// 定义一个名为 `Loop` 的智能合约。// 合约是一个运行在以太坊区块链上的程序,包含数据(状态变量)和逻辑(函数)。// 这个合约的目的是展示 Solidity 中的 for 和 while 循环,以及循环控制语句(continue 和 break)。function loop() public pure {// 定义一个名为 `loop` 的公共函数。// `public` 表示函数可以被外部调用(用户、其他合约或 DApp)。// `pure` 表示函数不读取也不修改区块链状态(仅进行计算),链下调用不消耗 Gas。// 没有返回值(隐式返回 `void`)。// for loop// for 循环for (uint256 i = 0; i < 10; i++) {// 定义一个 for 循环,包含三部分:// 1. 初始化:`uint256 i = 0` - 创建循环变量 `i`,初始值为 0,类型为 `uint256`(无符号整数)。// 2. 条件:`i < 10` - 循环继续的条件是 `i` 小于 10。// 3. 步进:`i++` - 每次循环后,`i` 增加 1。// 循环体将执行 10 次(i = 0, 1, 2, ..., 9)。if (i == 3) {// 如果 `i` 等于 3,执行以下代码块。// Skip to next iteration with continue// 使用 continue 跳到下一次迭代continue;// `continue` 语句跳过当前循环的剩余代码,直接进入下一次迭代。// 当 `i == 3` 时,循环跳过后续代码(不执行 `if (i == 5)` 的检查),`i` 增加到 4。}if (i == 5) {// 如果 `i` 等于 5,执行以下代码块。// Exit loop with break// 使用 break 退出循环break;// `break` 语句立即终止整个循环。// 当 `i == 5` 时,循环结束,不再执行后续迭代。}}// while loop// while 循环uint256 j;// 声明一个变量 `j`,类型为 `uint256`,初始值为 0(未显式初始化,Solidity 默认赋值为 0)。// 变量 `j` 是函数的局部变量,存储在内存中,不占用区块链存储。while (j < 10) {// 定义一个 while 循环,条件为 `j < 10`。// 只要 `j` 小于 10,循环体就会继续执行。j++;// 将 `j` 增加 1。// 循环将执行 10 次(j = 0, 1, 2, ..., 9),直到 `j` 达到 10,条件不再满足。}}
}
Loop
是一个简单的智能合约,展示了 Solidity 中的 for 循环 和 while 循环,以及循环控制语句 continue
和 break
的使用。
代码做什么?
- for 循环:
- 迭代 10 次(
i
从 0 到 9)。 - 当
i == 3
时,使用continue
跳过当前迭代(不执行后续代码,直接进入下一次迭代)。 - 当
i == 5
时,使用break
提前退出循环(不再迭代i = 6, 7, 8, 9
)。 - 实际执行的迭代是:
i = 0, 1, 2, 4
(跳过 3,终止于 5)。
- 迭代 10 次(
- while 循环:
- 迭代 10 次(
j
从 0 到 9),每次将j
加 1。 - 当
j
达到 10,循环条件j < 10
不满足,循环结束。
- 迭代 10 次(
- Gas 成本:
- 函数是
pure
,不修改区块链状态,链下调用不消耗 Gas。 - 如果循环涉及状态变量修改(例如
i
是状态变量),每次迭代会消耗 Gas。
- 函数是
- 无界循环警告:
- 代码中的循环是有限的(10 次迭代),但如果写成无界循环(例如
while (true)
),可能耗尽 Gas,导致交易失败。
- 代码中的循环是有限的(10 次迭代),但如果写成无界循环(例如
用途:
- 循环在智能合约中用于:
- 处理数组或列表(例如,遍历用户列表)。
- 执行重复操作(例如,批量转账)。
- 实现复杂逻辑(例如,计算总和或查找特定条件)。
- 必须确保循环次数有限,避免 Gas 超支。
关键点:
-
for 循环:
- 适合已知迭代次数的场景(例如,遍历固定长度的数组)。
- 包含初始化、条件和步进,确保循环可控。
- 使用
continue
跳过特定迭代,break
提前退出循环。
-
while 循环:
- 适合未知迭代次数但有明确退出条件的场景。
- 需要手动更新循环变量(例如
j++
),否则可能变成无界循环。 - 在智能合约中较少使用,因为难以预测 Gas 消耗。
-
do while 循环:
-
本例未展示,但格式为:
do {// 代码块 } while (条件);
-
至少执行一次,即使条件初始不满足。
-
同样需避免无界循环。
-
-
Gas 优化:
- 循环中的每次操作(例如计算、状态修改)都消耗 Gas。
- 无界循环或过多迭代可能导致交易失败,需设置合理的循环次数。
pure
函数不消耗 Gas,但如果循环修改状态变量,需估算 Gas 成本。
-
安全性:
- 确保循环有明确的退出条件,避免无限循环。
- 在循环中处理外部调用(如转账)需谨慎,可能导致 Gas 攻击或重入漏洞。
循环的注意事项
- 避免无界循环:
- 无界循环(例如
while (true)
)可能耗尽 Gas,导致交易失败。 - 始终设置明确的退出条件或最大迭代次数。
- 无界循环(例如
- Gas 消耗:
- 循环中的状态修改(例如更新状态变量)每次迭代消耗 Gas。
- 使用
pure
或view
函数可避免 Gas 成本,但实际应用中常需修改状态。
- 循环控制:
continue
:跳过当前迭代,进入下一次。break
:提前退出循环,节省 Gas。
- 安全性:
- 避免在循环中调用外部合约(可能导致 Gas 攻击或重入漏洞)。
- 检查数组长度或其他条件,防止越界访问。
- while vs. for:
for
适合已知迭代次数,结构清晰。while
适合动态条件,但需确保退出条件明确。do while
至少执行一次,适合特殊场景,但使用较少。