从基础到中级:BREAK 和 CONTINUE 语句·进阶篇
(2/3)· 接上篇 WHILE 循环,搞懂 RETURN、BREAK、CONTINUE 如何精准掐断或跳过迭代
死代码与编译器优化陷阱
上面这段 MQL5 片段里,第 28 行用 Print(__FUNCTION__, " ", __LINE__, " This line will never be executed...") 输出函数名、行号和一句提示,但它在逻辑上位于永远走不到的分支之后。MT5 的 MetaEditor 在编译期就可能把这类不可达代码直接剔除,运行时你根本看不到这条日志。 __FUNCTION__ 在编译后会展开为当前函数名,__LINE__ 则是该语句在源文件中的实际行号;这两个宏对排查『以为跑了其实没跑』的死代码很有用。外汇与贵金属杠杆高,策略里若混进永不触发的日志,会误导你以为某分支正常生效,实盘前务必在 MT5 用编译警告级别调高来验证。 把这类代码贴进 EA 的 OnInit 或自定义函数末尾做对照:若『This line will never be executed』没出现在专家日志,就说明编译器已判定它为死路,不必再等它触发。
class="num">28. Print(__FUNCTION__, " ", __LINE__, " This line will never be executed..."); class="num">29. } class="num">30. class=class="str">"cmt">//+------------------------------------------------------------------+
◍ BREAK 在嵌套循环里只断内层
BREAK 不是随便塞进循环体就能用的。它通常藏在 IF 里,条件成立才触发,行为和 RETURN 类似但作用域更窄:只终止「当前所在的这一层循环」,对外层循环没有管辖权。 看这段把上一节 RETURN 换成 BREAK 的代码(代码 05)。初始 info = 10,每次循环减 3,当 info < -5 时 break。终端会依次打印 Loop: 7、Loop: 4、Loop: 1、Loop: -2、Loop: -5,然后跳出 while,继续执行第 28 行那句「It will be executed anyway...」。 嵌套循环才是坑点。代码 06 里外层循环从第 18 行开始,内层循环从第 22 行开始,第 24 行做条件检查并可能 break。跑起来你会发现:内层 break 后,外层还在转,所以第 28 行那种打印只在某些轮次出现,并不是内层条件暗示的那么频繁。 别把正态当圣经 新手常误以为 BREAK 能跨层终止,或者在 switch 以外的地方乱用。它确实也能用于另一种控制结构(后面再讲),但现在先认死理:嵌套里它只杀最内那层。把代码 06 的 break 改成 return 再跑一次,终端输出差异立刻看出来——return 是直接废掉整个 Procedure_Loop。 让小布替你跑这套 在 MT5 里把下面代码原样贴进脚本,先跑 break 版,再改 return 版对比。外汇和贵金属脚本测试请在策略测试器用纯数值模拟,实盘前务必清楚这类控制流误操作可能导致下单逻辑提前退出,属高风险动作。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); Procedure_Loop(); Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); } class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void Procedure_Loop(class="type">void) { class="type">char info = class="num">10; Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); class="kw">while (info) { info = info - class="num">3; Print("Loop : ", info); if (info < -class="num">5) break; } Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); } class=class="str">"cmt">//+------------------------------------------------------------------+
「do-while 里套 while 的嵌套陷阱」
下面这段 MQL5 把 do-while 和内部 while 混在一起,初看像普通循环,实际执行路径有点绕。Procedure_Loop 里 info 初值 10、limit 初值 5,外层 do 块至少跑一次,内部 while(info>limit) 负责把 info 每次减 3,直到不大于 limit。 内部 while 退出后,info 会加上当前 limit,limit 再减 2,然后回到外层条件 info>0 判断。按初始值推演:第一轮 info 从 10 减到 7(仍>5),再减到 4(≤5)退出,info=4+5=9、limit=3;第二轮 info 从 9 减到 6(>3)再减到 3(≤3)退出,info=3+3=6、limit=1;第三轮 info 从 6 减到 3(>1)再减到 0(≤1)退出,info=0+1=1、limit=-1;第四轮 info=1 不大于 limit(-1),内部 while 不进,info=1+(-1)=0、limit=-3,外层 info>0 不成立收尾。 开 MT5 把这段代码丢进脚本跑一遍,终端会打出每轮的行号与变量,能直观看到 limit 降到负数后内部 break 保护并未触发(因为 limit<0 判断在 while 体里,而 limit 变负时外层已先停)。外汇与贵金属 EA 写循环时,这种嵌套若没理清退出条件,可能造成重复下单或挂单逻辑卡死,属高风险操作。
class="num">06. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); class="num">07. class="num">08. Procedure_Loop(); class="num">09. class="num">10. Print(__FUNCTION__, " ", __LINE__, " It will be executed anyway..."); class="num">11. } class="num">12. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">13. class="type">void Procedure_Loop(class="type">void) class="num">14. { class="num">15. class="type">char info = class="num">10, class="num">16. limit = class="num">5; class="num">17. class="num">18. do class="num">19. { class="num">20. Print(__FUNCTION__, " ", __LINE__, " : Info(", info, ") Limit(", limit, ")"); class="num">21. class="num">22. class="kw">while (info > limit) class="num">23. { class="num">24. if (limit < class="num">0) break; class="num">25. class="num">26. info = info - class="num">3; class="num">27. class="num">28. Print(__FUNCTION__, " ", __LINE__, " : Info(", info, ") Limit(", limit, ")"); class="num">29. } class="num">30. info = info + limit; class="num">31. limit = limit - class="num">2; class="num">32. class="num">33. Print(__FUNCTION__, " ", __LINE__, " : Info(", info, ") Limit(", limit, ")"); class="num">34. class="num">35. }class="kw">while (info > class="num">0); class="num">36. class="num">37. Print(__FUNCTION__, " ", __LINE__, " : Info(", info, ") Limit(", limit, ")"); class="num">38. }
CONTINUE 怎么把 WHILE 变成死循环
在 MQL5 的循环里塞 CONTINUE,最容易被忽略的坑就是它直接把控制流弹回循环开头,中间任何代码都不跑。WHILE 和 DO-WHILE 里尤其危险,因为只要循环条件一直为真,程序就卡死在那儿,直到你手动从 MT5 里强杀脚本。 下面这段代码(代码 07 变体)第 15 行给 info 赋了 10,第 19 行 WHILE 条件是 (info) && (!ESC按下) && (!IsStopped())。第 21 行写的是 if (info) continue;——只要 info 不为 0,每次进来都 CONTINUE,于是 23~27 行永远不执行,info 永远减不到让条件变 false,死循环就成型了。 如果不在循环条件里加 IsStopped() 这种外部退出通道,MT5 尝试关闭脚本时会一直重试并在终端报图 07 那种强制终止消息;加上第 19 行第三个条件后,表现就和正常点停止脚本一样。 把第 19 行改成 while ((info > 0) && ... ) 这类写法(代码 08),虽然 23~27 行依旧因 CONTINUE 不执行,但 info 初始为 10 时循环条件第一轮就可能不满足,不再必然死循环。这说明同样用 CONTINUE,循环表达式怎么写决定了会不会卡死。 DO-WHILE 更隐蔽:代码 09 里先执行循环体再判条件,第 26 行 CONTINUE 会跳回底部判断而非顶部,导致第 28 行永远跑不到。经验少的开发者常误以为会回到第 20 行。外汇和贵金属 EA 里用这类结构,高风险在于实盘挂上后脚本无响应,只能手动干预。
<span class="number">class="num">01</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">02</span>. <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose"</span> <span class="number">class="num">03</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">04</span>. <span class="keyword">class="type">void</span> <span class="functions">OnStart</span>(<span class="keyword">class="type">void</span>) <span class="number">class="num">05</span>. { <span class="number">class="num">06</span>. <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">" "</span>, <span class="keyword">__LINE__</span>, <span class="class="type">class="kw">string">" It will be executed anyway..."</span>); <span class="number">class="num">07</span>. <span class="number">class="num">08</span>. Procedure_Loop(); <span class="number">class="num">09</span>. <span class="number">class="num">10</span>. <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">" "</span>, <span class="keyword">__LINE__</span>, <span class="class="type">class="kw">string">" It will be executed anyway..."</span>); <span class="number">class="num">11</span>. } <span class="number">class="num">12</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">13</span>. <span class="keyword">class="type">void</span> Procedure_Loop(<span class="keyword">class="type">void</span>) <span class="number">class="num">14</span>. { <span class="number">class="num">15</span>. <span class="keyword">class="type">char</span> info = <span class="number">class="num">10</span>; <span class="number">class="num">16</span>. <span class="number">class="num">17</span>. <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">" "</span>, <span class="keyword">__LINE__</span>, <span class="class="type">class="kw">string">" : Info("</span>, info, <span class="class="type">class="kw">string">")"</span>); <span class="number">class="num">18</span>. <span class="number">class="num">19</span>. <span class="keyword">class="kw">while</span> ((info) && (!<span class="functions">TerminalInfoInteger</span>(<span class="macro">TERMINAL_KEYSTATE_ESCAPE</span>)) && (!<span class="functions">IsStopped</span>())) <span class="number">class="num">20</span>. { <span class="number">class="num">21</span>. <span class="keyword">if</span> (info) <span class="keyword">class="kw">continue</span>; <span class="number">class="num">22</span>. <span class="number">class="num">23</span>. info = info - <span class="number">class="num">3</span>; <span class="number">class="num">24</span>. <span class="number">class="num">25</span>. <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">" "</span>, <span class="keyword">__LINE__</span>, <span class="class="type">class="kw">string">" : Info("</span>, info, <span class="class="type">class="kw">string">")"</span>); <span class="number">class="num">26</span>. <span class="number">class="num">27</span>. <span class="keyword">if</span> (info < -<span class="number">class="num">5</span>) <span class="keyword">class="kw">return</span>; <span class="number">class="num">28</span>. }