文档首页> Linux命令> 【从0学习Solidity】控制流,用solidity实现

【从0学习Solidity】控制流,用solidity实现

发布时间:2025-08-29 00:10       

下面用“插入排序”带你把 Solidity 的控制流一次吃透:for/while/if、早返回、内存与存储的差别,以及节省 gas 的写法。🙂


核心要点(先记住)

  • 控制流:for 负责外层轮次,while 负责元素前移,if/return 做边界与早退出。
  • 数据区:calldata(只读、最省 gas)→ 复制到 memory 排序;storage 为链上状态,原地改写更贵。
  • 复杂度:插入排序 O(n^2),仅适合小数组(如几十到一两百个元素)。大数组应链下排序或换算法。
  • gas 技巧:unchecked { ++i; --j; } 跳过溢出检查;必要时早返回;减少重复读取长度。

可直接部署的示例(Solidity ^0.8.x)

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract InsertionSortDemo {
    uint[] public nums;

    // 装载一组数据到 storage(演示用)
    function setNums(uint[] calldata data) external {
        delete nums;
        for (uint i = 0; i < data.length; ) {
            nums.push(data[i]);
            unchecked { ++i; }
        }
    }

    // 在 memory 上排序并返回新数组(不修改状态)
    function sortMemory(uint[] calldata data) external pure returns (uint[] memory out) {
        out = new uint[](data.length);
        for (uint i = 0; i < data.length; ) { // 复制到 memory
            out[i] = data[i];
            unchecked { ++i; }
        }
        // 插入排序主体
        for (uint i = 1; i < out.length; ) {
            uint key = out[i];
            uint j = i;
            while (j > 0 && out[j - 1] > key) {
                out[j] = out[j - 1];
                unchecked { --j; }
            }
            out[j] = key;
            unchecked { ++i; }
        }
    }

    // 对 storage 上的 nums 原地排序(修改链上状态)
    function sortStorage() external {
        uint len = nums.length;
        if (len < 2) return; // 早返回降低 gas
        for (uint i = 1; i < len; ) {
            uint key = nums[i];
            uint j = i;
            while (j > 0 && nums[j - 1] > key) {
                nums[j] = nums[j - 1];
                unchecked { --j; }
            }
            nums[j] = key;
            unchecked { ++i; }
        }
    }
}
​

说明

  • sortMemory:输入放在 calldata,复制到 memory 排序并返回,不改状态,适合链上验证或只读场景。
  • sortStorage:直接对 storage 的 nums 排序,便捷但更耗 gas,建议配合长度上限与访问控制(如仅管理员可调)。
  • unchecked:Solidity 0.8+ 默认整型有溢出检查,循环自增自减可用 unchecked 降低 gas。

工作流程(vditor/Markdown 支持)

Syntax error in textmermaid version 10.8.0
Parse error on line 5:
...n-1] D --> E[key=a[i], j=i] E --> F{ ----------------------^ Expecting 'SQE', 'DOUBLECIRCLEEND', 'PE', '-)', 'STADIUMEND', 'SUBROUTINEEND', 'PIPE', 'CYLINDEREND', 'DIAMOND_STOP', 'TAGEND', 'TRAPEND', 'INVTRAPEND', 'UNICODE_TEXT', 'TEXT', 'TAGSTART', got 'SQS'

原理对照表(控制流 × 数据区)

维度 选择 影响与理由
外层轮次 for 明确起止边界,便于自增优化
元素前移 while 条件驱动,直观表达“向前腾挪”
边界处理 if (len<2) return 早返回,避免无效循环
数据区 calldata → memory 排序副本不改状态,成本更低
原地改写 storage 改链上数据,需限长与权限
gas 优化 unchecked自增/自减 避开溢出检查,少量省气

实战建议 🔧

  1. 只读路径优先:能在 memory 排序就别碰 storage。
  2. 限制规模:上链排序须强约束长度,防止超时/超费。
  3. 复用性:提炼为库函数(library)或 internal 工具,复用在不同合约中。
  4. 可测试性:准备小样本(如 [5,3,4,1,2])与有序/重复/空数组用例,覆盖边界。

一句话总结:用清晰的 for+while+if 组合和恰当的数据区选择,就能把插入排序在 Solidity 中写得既直观又省 gas。🚀