从基础到中级:按值传递还是按引用传递(基础篇)
📘

从基础到中级:按值传递还是按引用传递(基础篇)

第 1/2 篇

◍ 值传递和引用传递到底差在哪

MQL5 里写函数,第一道坎就是参数怎么传。按值传递(value)会把实参拷一份进函数,里面怎么改都不影响外面;按引用传递(reference,用 & 声明)直接拿原变量内存地址操作,函数内改动会穿透回调用处。 很多新手在 EA 里用数组算指标缓冲,若误用值传递,函数里改了数组外头却没变,回测时信号错位却查不出原因。2025 年 6 月 20 日官方示例文档里给出的基础演示中,同样一个修改数组元素的函数,引用传递版本外部数组长度从 0 变为 3,值传递版本外部依旧为 0。 高风险提示:外汇与贵金属杠杆交易可能瞬间击穿本金,下面代码仅用于理解机制,别直接挂实盘。开 MT5 新建脚本把这段跑一遍,比看十遍文字都管用。

「值传递还是引用传递」

MQL5 初学者在写函数与过程时,最容易踩的坑就是搞不清什么时候该按值传、什么时候该按引用传。按引用传在部分场景下更省事,但按值传通常更安全,不容易在函数内部误改外部变量。 原文没有给出绝对规则,只强调「得看情况」:某些场景引用传递最合适,另一些场景值传递才正确。编译器自身会尝试选更高效的生成方式,但交易者自己得明白每个调用的真实需求,才能写出既安全又不太拖慢 EA 回测的代码。 如果你还分不清变量与常量,先去补「从基础到中级:变量(一)」那篇。控制操作符后面才讲,这一节先把传参底子打平,后面写条件分支和循环时才不会反复掉进作用域坑里。

值传递里那层玻璃窗

在 MT5 里跑一段最朴素的调用,就能看清按值传递到底发生了什么。下面这段代码把整型变量 info 赋值为 10,再把它丢给 Procedure 函数,终端会依次打出四行:OnStart 见到 10、Procedure 见到 10、Procedure 内部改成 13、回到 OnStart 仍是 10。 [CODE] //+------------------------------------------------------------------+ #property copyright "Daniel Jose" //+------------------------------------------------------------------+ void OnStart(void) { int info = 10; Print(__FUNCTION__, " : #1 => ", info); Procedure(info); Print(__FUNCTION__, " : #2 => ", info); } //+------------------------------------------------------------------+ void Procedure(int arg1) { Print(__FUNCTION__, " : #1 => ", arg1); arg1 = arg1 + 3; Print(__FUNCTION__, " : #2 => ", arg1); } //+------------------------------------------------------------------+ [/CODE] 逐行拆一下:第 6 行在 OnStart 里定义 info=10;第 8 行用 __FUNCTION__ 宏打印当前例程名(编译期被替换成 "OnStart")和 info 值;第 9 行调用 Procedure(info),把值交给形参 arg1;第 13 行 Procedure 接收 int arg1,这里编译器通常让 arg1 引用 info 的值但不共享内存,像隔着玻璃看——能读不能改。 第 15 行打印 arg1,显示还是 10;第 16 行执行 arg1 = arg1 + 3 时,编译器才给 arg1 分配独立空间并拷贝当前值,之后它跟 info 彻底脱钩;第 17 行打出 13。回到第 10 行,info 原封不动是 10。这就解释了为什么函数内改了形参、外面变量却不动。 别把玻璃窗当摆设 如果哪天你根本不想让 arg1 被改,光靠小心盯着代码不靠谱——人总会手滑。把形参声明成 const int arg1,编译期就会在第 16 行直接报错,强制锁死。想输出 13 也简单:在第 16 行写 int x = arg1 + 3,把结果交给新变量再 Print,既保住常量约束又不耽误事。外汇和贵金属 EA 开发里这种误改常引发隐性 bug,用 const 让编译器替你盯梢更稳。

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)
{
    class="type">int info = class="num">10;

    Print(__FUNCTION__, " : #class="num">1 => ", info);
    Procedure(info);
    Print(__FUNCTION__, " : #class="num">2 => ", info);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Procedure(class="type">int arg1)
{
    Print(__FUNCTION__, " : #class="num">1 => ", arg1);
    arg1 = arg1 + class="num">3;
    Print(__FUNCTION__, " : #class="num">2 => ", arg1);
}
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ const 参数挡不住的是副本不是逻辑

上面这段 MT5 脚本把同一个意图写了两版,差别只在 Procedure 里怎么动参数。第一版(13–18 行)形参标了 const int arg1,函数体内先打印 arg1,再执行 arg1 = arg1 + 3,编译会直接报错:const 限定符禁止对 arg1 赋值。 第二版(13–17 行)绕开了这点,不在函数内改 arg1,而是打印 arg1 + 3。OnStart 里 info 初始化为 10,调用 Procedure(info) 后回到 OnStart 再打印 info,输出仍是 10。说明即便没有 const,MT5 也是按值传参,函数内拿到的是副本,外部变量不会被带跑。 实盘写 EA 时这是个容易踩的坑:你以为在函数里把止损点数加了 3 就改了外部风控变量,其实只是改了栈上的副本。想让函数改外部值,得用引用或全局变量。外汇和贵金属波动大、滑点频繁,这类传参误解可能让仓位管理逻辑偏离预期,开 MT5 把下面代码跑一遍最能看清。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Script program start function                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart(class="type">void)
  {
   class="type">int info=class="num">10;
   Print(__FUNCTION__," : #class="num">1 => ",info);
   Procedure(info);
   Print(__FUNCTION__," : #class="num">2 => ",info);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Procedure                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Procedure(const class="type">int arg1)
  {
   Print(__FUNCTION__," : #class="num">1 => ",arg1);
   Print(__FUNCTION__," : #class="num">2 => ",arg1+class="num">3);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

常见问题

你用的是按值传递,函数拿到的是副本,改副本不影响原变量;要改原值需用引用传递加&。
因为函数内只能读副本、不能改原数据,原变量受保护;适合只做计算不修改输入的场景。
可以,小布能扫描你的策略代码,标出该用引用却用了值传递的位置,并给修改建议。
意义不大,const只挡住副本不被改,原逻辑漏洞挡不住;它防的是函数内手误改副本。
默认用值传递更安全,避免误改原始K线数据;只有明确要回写结果时才用引用。