尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
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-else、for、while、do-while、三元运算符等控制流的正确写法理解为什么直接照搬 Python 版插入排序到Solidity会触发underflow报错并拿到一份可直接在 Remix 中编译运行的正确版本。同时本讲是理解 Solidity 安全缺陷如数值溢出的重要入门案例对后续阅读合约审计相关内容很有帮助。Solidity 控制流与其他语言相似的六大结构Solidity的控制流与C、JavaScript等主流语言高度相似主要包含以下结构。所有示例代码均来自仓库 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用于根据条件执行不同分支。上例中入参_number为0时返回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三部分组成上例计算01...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; }上例一行代码即返回x与y中的较大值等价于完整的if-else分支。6. continue 与 break除上述结构外Solidity还支持循环控制关键字continue立即跳过本次循环的剩余语句进入下一次迭代break立即跳出当前整个循环。这两个关键字与其它语言语义一致常用于在循环中过滤数据或提前终止搜索。插入排序最简单也最容易写错的排序算法排序算法解决的问题是把一组无序数字例如[2, 5, 3, 1]按从小到大排列。插入排序Insertion Sort是最简单的排序算法之一也是很多人学习的第一个算法。它的思路很朴素从前往后遍历把每一个数与排在它前面的数逐个比较若比前面的数小就将其向前移动直到插入到正确位置。举例来说处理数组[2, 5, 3, 1]时算法会先把5与前面的2比较无需移动再把3与前面的5、2比较并插入到2与5之间最后把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[j1] arr[j] j - 1 arr[j1] key return arr核心逻辑是用key保存当前待插入元素j从i-1开始向前扫描凡比key大的元素都后移一位最后把key放到空出的位置。照搬到 Solidity 后出现了 BUGuint 下溢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 ji-1; while( (j 0) (temp a[j])){ a[j1] a[j]; j--; } a[j1] 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--一旦j从0变为-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 ji; 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[j1] a[j]正确版j ij 1j 最小为 0temp 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并包含ifElseTest、forLoopTest、whileTest、doWhileTest、ternaryTest全部控制流示例可直接对照阅读。葡萄牙语版本见 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 提供了对全仓库合约运行测试的脚本可用于自动化验证。总结这一讲我们完成了两件事一是系统梳理了Solidity的if-else、for、while、do-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),仅供参考
RELATED

相关推荐

基于ZYNQ的远程固件升级系统:网口传输与QSPI固化实现

基于ZYNQ的远程固件升级系统:网口传输与QSPI固化实现

简介:基于ZYNQ平台的远程固件升级系统完整工程资料,面向FPGA与嵌入式开发人员,演示如何通过网口完成FPGA设备的远程固化升级。方案基于ALINX固件固化例程二次开发,并集成CRC32校验机制,保障传输与烧写过程的数据完整性…

📅 2026/9/15 12:09:52
静态代码分析工具横向对比:从ESLint到SonarQube,如何选型与落地

静态代码分析工具横向对比:从ESLint到SonarQube,如何选型与落地

很多团队的代码质量管控,其实一直卡在一个很尴尬的位置:代码评审靠人眼盯,低级错误靠运行时炸出来,线上出问题再回头补测试。我在经历过几次“本地运行得好好的,一上线就被空指针打脸”之后,彻底意识到&…

📅 2026/9/15 12:09:52
Instructor Patching 机制深度解析:如何为 LLM 客户端注入结构化输出能力

Instructor Patching 机制深度解析:如何为 LLM 客户端注入结构化输出能力

Instructor Patching 机制深度解析:如何为 LLM 客户端注入结构化输出能力 【免费下载链接】instructor structured outputs for llms 项目地址: https://gitcode.com/GitHub_Trending/in/instructor Patching 是 Instructor 的核心机制之一:它不…

📅 2026/9/15 12:04:52
MORE NEWS

更多资讯

📰

C#网关解析SPARQL被GC毛刺卡死?我用MemoryExtensions手搓零分配词法器,国产图数据库P99直降80%!

下面是整个系统的架构链路图: #mermaid-svg-nx7HZHavLF8trkuP{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}…

📰

TMS320VC5509 UART DMA驱动实战:绕过CPU瓶颈的寄存器级实现

简介:本资源是一份面向嵌入式初学者的TMS320VC5509 DSP DMA传输入门实践材料,聚焦UART通信场景下的DMA机制理解与代码实现,解决CPU频繁干预导致的数据传输效率瓶颈问题。压缩包共2个文件(1个C源码文件、1个说明文本)&a…

📰

200个Flink SQL迁国产库全崩了?3层AI“语义降级”引擎,搞定大数据信创的“基因重组

🔥关注墨瑾轩,带你探索编程的奥秘!🚀 🔥超萌技术攻略,轻松晋级编程高手🚀 🔥技术宝库已备好,就等你来挖掘🚀 🔥订阅墨瑾轩,智趣学习不…

📰

基于51单片机与Proteus的货车超重监测系统仿真设计

简介:基于51单片机的货车超重监测系统仿真设计资料包,面向单片机学习者与课程设计/毕设人员,用于掌握超载检测系统的完整设计流程。压缩包共19个文件,包含Proteus仿真电路DSN、Keil工程UV2、C/A51源程序、可烧录HEX文件及备份文件…

📰

滑模控制在车辆稳定性中的Simulink实现与优化

1. 为什么需要滑模控制解决车辆稳定性问题现代汽车电子稳定系统面临的核心挑战在于:如何在轮胎非线性特性、路面突变和驾驶员操作不确定性等多重干扰下,保持车辆的横向稳定性。传统PID控制在转向工况下会出现超调振荡,而线性二次型调节器&…

📰

al-folio 如何用 upgrade CLI 跟踪本地 override 与插件 gem 更新的漂移?

al-folio 如何用 upgrade CLI 跟踪本地 override 与插件 gem 更新的漂移? 【免费下载链接】al-folio A beautiful, simple, clean, and responsive Jekyll theme for academics 项目地址: https://gitcode.com/GitHub_Trending/al/al-folio al-folio 的 v1.x…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬