news 2026/9/15 12:09:39

WTF Solidity 极简入门:Solidity 控制流与插入排序实现,以及 uint 下溢(underflow)陷阱全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WTF Solidity 极简入门:Solidity 控制流与插入排序实现,以及 uint 下溢(underflow)陷阱全解析

WTF Solidity 极简入门:Solidity 控制流与插入排序实现,以及 uint 下溢(underflow)陷阱全解析

【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程,供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity

本篇文章对应 WTF-Solidity 教程第 10 讲,围绕Solidity的控制流结构与如何在链上合约中用Solidity实现经典排序算法——插入排序(Insertion Sort)展开。读完你将掌握if-elseforwhiledo-while、三元运算符等控制流的正确写法,理解为什么直接照搬 Python 版插入排序到Solidity会触发underflow报错,并拿到一份可直接在 Remix 中编译运行的正确版本。同时,本讲是理解 Solidity 安全缺陷(如数值溢出)的重要入门案例,对后续阅读合约审计相关内容很有帮助。

Solidity 控制流:与其他语言相似的六大结构

Solidity的控制流与CJavaScript等主流语言高度相似,主要包含以下结构。所有示例代码均来自仓库 10_InsertionSort/InsertionSort.sol 中的InsertionSort合约,你可以在 Remix 中直接粘贴编译运行。

1. if-else 条件分支

function ifElseTest(uint256 _number) public pure returns(bool){ if(_number == 0){ return(true); }else{ return(false); } }

if-else用于根据条件执行不同分支。上例中,入参_number0时返回true,否则返回false。注意Solidity中布尔条件两侧的括号是必须的,与 JavaScript 风格一致。

2. for 循环

function forLoopTest() public pure returns(uint256){ uint sum = 0; for(uint i = 0; i < 10; i++){ sum += i; } return(sum); }

for循环由初始化语句(uint i = 0)、循环条件(i < 10)和迭代语句(i++)三部分组成,上例计算0+1+...+9 = 45。由于该函数不读取也不修改链上状态,声明为pure可以节省 gas。

3. while 循环

function whileTest() public pure returns(uint256){ uint sum = 0; uint i = 0; while(i < 10){ sum += i; i++; } return(sum); }

while在进入循环体之前先判断条件,条件为假时循环体一次都不会执行,因此又叫"前测试循环"。

4. do-while 循环

function doWhileTest() public pure returns(uint256){ uint sum = 0; uint i = 0; do{ sum += i; i++; }while(i < 10); return(sum); }

do-while先执行一次循环体再判断条件,因此循环体至少执行一次。这是它与while的核心区别,在需要"先操作后判断"的场景中非常有用。

5. 三元(条件)运算符

三元运算符是Solidity中唯一接受三个操作数的运算符,规则为条件 ? 条件为真时的表达式 : 条件为假时的表达式,经常作为if语句的快捷写法:

// 三元运算符 ternary/conditional operator function ternaryTest(uint256 x, uint256 y) public pure returns(uint256){ // return the max of x and y return x >= y ? x: y; }

上例一行代码即返回xy中的较大值,等价于完整的if-else分支。

6. continue 与 break

除上述结构外,Solidity还支持循环控制关键字:

  • continue:立即跳过本次循环的剩余语句,进入下一次迭代;
  • break:立即跳出当前整个循环。

这两个关键字与其它语言语义一致,常用于在循环中过滤数据或提前终止搜索。

插入排序:最简单也最容易写错的排序算法

排序算法解决的问题,是把一组无序数字(例如[2, 5, 3, 1])按从小到大排列。插入排序(Insertion Sort)是最简单的排序算法之一,也是很多人学习的第一个算法。它的思路很朴素:从前往后遍历,把每一个数与排在它前面的数逐个比较,若比前面的数小,就将其向前移动,直到插入到正确位置

举例来说,处理数组[2, 5, 3, 1]时,算法会先把5与前面的2比较(无需移动),再把3与前面的52比较并插入到25之间,最后把1一路移动到数组最前面,得到[1, 2, 3, 5]

Python 版实现

先看插入排序的 Python 实现,一共 8 行:

# Python program for implementation of Insertion Sort def insertionSort(arr): for i in range(1, len(arr)): key = arr[i] j = i-1 while j >=0 and key < arr[j] : arr[j+1] = arr[j] j -= 1 arr[j+1] = key return arr

核心逻辑是:用key保存当前待插入元素,ji-1开始向前扫描,凡比key大的元素都后移一位,最后把key放到空出的位置。

照搬到 Solidity 后出现了 BUG:uint 下溢(underflow)

将上面的 Python 代码"逐行翻译"成Solidity,函数、变量、循环等一一对应,只需要 9 行代码:

// 插入排序 错误版 function insertionSortWrong(uint[] memory a) public pure returns(uint[] memory) { for (uint i = 1;i < a.length;i++){ uint temp = a[i]; uint j=i-1; while( (j >= 0) && (temp < a[j])){ a[j+1] = a[j]; j--; } a[j+1] = temp; } return(a); }

把这段代码放到 Remix 上运行,输入[2, 5, 3, 1],结果却直接报错!这正是原文档特别强调的警示:90% 以上的人用Solidity写插入排序都会出错

下图是 Remix 中运行错误版合约时"decoded output"的解码失败界面(来源 10_InsertionSort/img/10-1.jpg,该图与 Languages/pt-br/10_InsertionSort/img/10-1.jpg 内容相同):

图中可以看到:合约接收到的输入数组是uint256[]类型的[2, 3, 5, 1],但输出解码时报出Failed to decode output: Error: over-flow (faults) overFlow, operation: toNumber(),并附带一个远超uint256最大值(2^256-1)的畸形数字。也就是说,合约内部的运算已经产生了不可预期的异常结果。

根因:Solidity 的 uint 是无符号整数,减到负值即触发 underflow

排查几小时后,问题终于定位:Solidity中最常用的变量类型是uint(无符号整数),它无法表示负数。而在插入排序算法中,变量j在循环结束时有可能递减到-1

  • 外层循环处理到某个元素时,内层while循环条件(j >= 0) && (temp < a[j])里,j >= 0对无符号整数来说永远为真
  • 于是j不断执行j--,一旦j0变为-1,由于uint类型下溢(underflow),-1会被包装成uint能表示的最大值2^256 - 1(即约1.15 × 10^77);
  • 此时再访问a[j],索引超出数组长度,且j的取值已经完全失控,最终导致输出解码失败、交易回滚。

这正是上图中出现那个天文数字的原因——它不是合法的排序结果,而是uint下溢后的错误索引与错误数据。这个案例直观说明:Solidity 默认不做整数溢出检查的运行时保护时,uint的减法下溢会带来灾难性后果(注:Solidity 0.8 起默认对算术运算做溢出检查,但本案例的问题在于把负数语义强加给了无符号类型,属于逻辑层面的错误)。

正确的 Solidity 插入排序

修复的思路是:j永远无法取到负值。做法是把j的初始值加 1,比较时改用a[j-1],并把循环条件从j >= 0改为j >= 1

// 插入排序 正确版 function insertionSort(uint[] memory a) public pure returns(uint[] memory) { // note that uint can not take negative value for (uint i = 1;i < a.length;i++){ uint temp = a[i]; uint j=i; while( (j >= 1) && (temp < a[j-1])){ a[j] = a[j-1]; j--; } a[j] = temp; } return(a); }

两版代码的差异集中在一处:

版本j 初始值循环条件比较对象赋值位置
错误版j = i-1j >= 0(恒真,隐患)temp < a[j]a[j+1] = a[j]
正确版j = ij >= 1(j 最小为 0)temp < a[j-1]a[j] = a[j-1]

正确版中j的取值范围被限制在1 ~ ij--最多递减到0,绝不会触及负数,从而彻底规避了uint下溢。在 Remix 中重新运行正确版,输入[2, 5, 3, 1],即可得到排序结果[1, 2, 3, 5],与预期完全一致。

仓库中的源码与编译环境

  • 合约完整源码位于 10_InsertionSort/InsertionSort.sol,其中同时保留了错误版insertionSortWrong与正确版insertionSort,并包含ifElseTestforLoopTestwhileTestdoWhileTestternaryTest全部控制流示例,可直接对照阅读。葡萄牙语版本见 Languages/pt-br/10_InsertionSort/InsertionSort.sol。
  • 仓库根目录 foundry.toml 将solc固定为0.8.34,该合约声明pragma solidity ^0.8.34,可同时在 Remix(选用 0.8.34 及以上版本)或使用 Foundry 的forge build环境下编译验证。
  • 仓库根目录 scripts/run-forge-tests.sh 提供了对全仓库合约运行测试的脚本,可用于自动化验证。

总结

这一讲我们完成了两件事:一是系统梳理了Solidityif-elseforwhiledo-while、三元运算符以及continue/break等控制流结构;二是用Solidity实现了插入排序,并亲历了一次经典的踩坑过程——因为uint是无符号整数,照搬其他语言的写法让变量j减到-1,触发underflow导致合约报错,最终通过改写循环边界条件修复。

插入排序看起来简单,实际写对并不容易。这正是Solidity的特性:坑很多,每个月都有项目因为这些不起眼的小 bug 损失几千万甚至上亿美元。掌握好基础、多动手练习,是写出高质量Solidity代码的前提。建议读者在 10_InsertionSort/InsertionSort.sol 源码基础上,自行尝试把错误版改为正确版、再尝试排序不同输入数组,深入体会无符号整数边界条件的处理。

【免费下载链接】WTF-SolidityWTF Solidity 极简入门教程,供小白们使用。Now supports English! 官网: https://wtf.academy项目地址: https://gitcode.com/GitHub_Trending/wt/WTF-Solidity

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 12:09:29

3步实现qq直接登录网站无需下载,附避坑指南

3步实现qq直接登录网站无需下载,附避坑指南 网站被黑挂马不知道怎么办?别慌,先检查登录接口是否裸露。很多站长为了省事,直接调用第三方接口却不加验证,这就是最大的漏洞。这篇避坑指南,专为设计师转前端的你准备,用真实案例拆解如何安全实现qq直接登录网站无需下载,避开90%的新手坑。…

作者头像 李华
网站建设 2026/9/15 12:07:49

2026年4月Product Hunt热榜:AI、AR与可持续科技趋势解析

1. 项目概述Product Hunt每日热榜是一个持续追踪全球最新科技产品动态的项目。2026年4月2日这期榜单特别值得关注&#xff0c;因为当天涌现了多个突破性产品&#xff0c;涵盖了AI助手、AR开发工具和可持续科技三大热门领域。作为长期关注科技创新的从业者&#xff0c;我发现这期…

作者头像 李华
网站建设 2026/9/15 12:06:36

Web与AI融合:构建企业级技能生态实践

1. 项目概述&#xff1a;当Web技术遇上AI能力十年前我们还在讨论如何把企业服务搬上网页&#xff0c;今天已经站在了AI重构行业工作流的十字路口。最近帮某医疗集团部署的AI问诊技能让我深刻体会到&#xff1a;行业专属Skills生态正在成为企业数字化转型的新基建。这个系统通过…

作者头像 李华