从基础到中级:BREAK 和 CONTINUE 语句·进阶篇
🔁

从基础到中级:BREAK 和 CONTINUE 语句·进阶篇

(2/3)· 接上篇 WHILE 循环,搞懂 RETURN、BREAK、CONTINUE 如何精准掐断或跳过迭代

含代码示例实战向 第 2/3 篇
很多交易者写 EA 时把循环当黑盒,遇到异常不知道何时该跳出、何时该跳过,结果回测跑飞还查不出原因。其实 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』没出现在专家日志,就说明编译器已判定它为死路,不必再等它触发。

MQL5 / C++
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 版对比。外汇和贵金属脚本测试请在策略测试器用纯数值模拟,实盘前务必清楚这类控制流误操作可能导致下单逻辑提前退出,属高风险动作。

MQL5 / C++
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 写循环时,这种嵌套若没理清退出条件,可能造成重复下单或挂单逻辑卡死,属高风险操作。

MQL5 / C++
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 里用这类结构,高风险在于实盘挂上后脚本无响应,只能手动干预。

MQL5 / C++
<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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; Procedure_Loop();
<span class="number">class="num">09</span>.
<span class="number">class="num">10</span>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">"&nbsp;&nbsp;"</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>.&nbsp;&nbsp;&nbsp;&nbsp;
<span class="number">class="num">19</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="kw">while</span> ((info) &amp;&amp; (!<span class="functions">TerminalInfoInteger</span>(<span class="macro">TERMINAL_KEYSTATE_ESCAPE</span>)) &amp;&amp; (!<span class="functions">IsStopped</span>()))
<span class="number">class="num">20</span>.&nbsp;&nbsp;&nbsp;&nbsp; {
<span class="number">class="num">21</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; info = info - <span class="number">class="num">3</span>;
<span class="number">class="num">24</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span class="number">class="num">25</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">Print</span>(<span class="keyword">__FUNCTION__</span>, <span class="class="type">class="kw">string">"&nbsp;&nbsp;"</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>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span> (info &lt; -<span class="number">class="num">5</span>) <span class="keyword">class="kw">return</span>;
<span class="number">class="num">28</span>.&nbsp;&nbsp;&nbsp;&nbsp; }
把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到循环逻辑异常提示,你专注策略决策就好。

常见问题

在函数里 RETURN 是必需的退出并返回值;在过程代码块里它是可选的,可提前结束当前执行流,但不止限于循环内使用。
可以,小布盯盘的 AIGC 模块对品种页策略做静态扫描时,会标记可能单独使用跳出语句导致的逻辑空洞,辅助你复盘代码。
BREAK 会直接终止整个循环,若条件覆盖不全可能漏掉本该处理的 K 线或订单,造成信号丢失,概率上增加漏单风险。
只想跳过某类不符合条件的柱用 CONTINUE 继续往后看;确认后续都没意义才用 BREAK 整体退出,避免无谓遍历拖慢执行。