从基础到中级:按值传递还是按引用传递(基础篇)
◍ 值传递和引用传递到底差在哪
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 让编译器替你盯梢更稳。
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 把下面代码跑一遍最能看清。
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">//+------------------------------------------------------------------+