MQL5 简介(第 6 部分):MQL5 中的数组函数新手指南 (二)·进阶篇
📚

MQL5 简介(第 6 部分):MQL5 中的数组函数新手指南 (二)·进阶篇

(2/3)· 接上篇基础,继续拆解 ArrayPrint 到 ArrayRemove 的 5 个函数,少走一半弯路

案例拆解新手友好 第 2/3 篇
接上篇,我们继续深挖 MQL5 数组函数里最容易被误用的几个。很多新手把 ArrayPrint 当永久日志,却不知 Digits 和 Flags 能省掉大量肉眼比对时间。先把调试输出驯服,后面的插入和移除才不会乱套。

数组插入与复制的覆盖差异

在 MT5 的 MQL5 里,ArrayInsert 和 ArrayCopy 对目标数组的处理逻辑完全不同,这一点直接决定你指标缓冲区的复用方式。上面这段脚本先建了两个长度为 5 的整型数组:SourceArray 装 1、3、5、7、9,DestinationArray 装 15、20、25、30、35。 先跑 ArrayInsert(DestinationArray, SourceArray, 2, 0, WHOLE_ARRAY),从目标索引 2 开始把源数组整体插入,目标原位置元素被顺推,打印出来长度会撑到 10,开头变成 15、20、1、3、5、7、9、25、30、35。 随后用 ArrayFree 释放并重新 Resize 回 5,再填回 15、20、25、30、35,调用 ArrayCopy(DestinationArray, SourceArray, 2, 0, WHOLE_ARRAY)。这里从目标索引 2 起覆盖式写入源数组全部 5 个元素,但目标只有 5 格,越界部分被丢弃,最终 DestinationArray 变成 15、20、1、3、5。 别把插入当复制用。插入是扩维顺推,复制是原地覆盖且受目标尺寸限制;写 EA 缓存前先在脚本里跑一遍这两段,确认自己拿到的是推挤后的长数组还是被截短的覆盖结果。外汇与贵金属行情高波动,数组越界类 bug 可能在极端 tick 下才暴露,建议用历史数据回放验证。

MQL5 / C++
class="type">int SourceArray[];
class="type">int DestinationArray[];
ArrayResize(SourceArray, class="num">5);
ArrayResize(DestinationArray, class="num">5);
SourceArray[class="num">0] = class="num">1;
SourceArray[class="num">1] = class="num">3;
SourceArray[class="num">2] = class="num">5;
SourceArray[class="num">3] = class="num">7;
SourceArray[class="num">4] = class="num">9;
DestinationArray[class="num">0] = class="num">15;
DestinationArray[class="num">1] = class="num">20;
DestinationArray[class="num">2] = class="num">25;
DestinationArray[class="num">3] = class="num">30;
DestinationArray[class="num">4] = class="num">35;
Print("Elements of SourceArray before ArrayInsert/ArrayCopy: ");
ArrayPrint(SourceArray, class="num">2, " ", class="num">0, WHOLE_ARRAY);
Print("Elements of DestinationArray before ArrayInsert/ArrayCopy: ");
ArrayPrint(DestinationArray, class="num">2, " ", class="num">0, WHOLE_ARRAY);
ArrayInsert(DestinationArray, SourceArray, class="num">2, class="num">0, WHOLE_ARRAY);
Print("Elements of DestinationArray after using ArrayInsert: ");
ArrayPrint(DestinationArray, class="num">2, " ", class="num">0, WHOLE_ARRAY);
ArrayFree(DestinationArray);
ArrayResize(DestinationArray, class="num">5);
DestinationArray[class="num">0] = class="num">15;
DestinationArray[class="num">1] = class="num">20;
DestinationArray[class="num">2] = class="num">25;
DestinationArray[class="num">3] = class="num">30;
DestinationArray[class="num">4] = class="num">35;
ArrayCopy(DestinationArray, SourceArray, class="num">2, class="num">0, WHOLE_ARRAY);
Print("Elements of DestinationArray after using ArrayCopy: ");
ArrayPrint(DestinationArray, class="num">2, " ", class="num">0, WHOLE_ARRAY);

「数清楚数组里到底有几本书」

MQL5 里 ArraySize() 只干一件事:返回一维数组的元素总数,类型是 int。它接收一个数组名作参数,不关心元素内容,只数个数,比你自己维护计数器靠谱。 把它想成书架管理员:你递给他一排书(数组),他回你一个数字(书的数量)。在 EA 或指标里动态处理行情缓冲、订单池时,先 ArraySize() 探一下长度,能避免越界访问导致的运行时报错。 大小写是坑。内置函数叫 ArraySize(首字母大写),你自己存的变量写成 arraySize(全小写)也合法,但两者不是同一个东西。MQL5 对标识符大小写敏感,混用编译器不拦你,逻辑却会歪。 下面这段可直接贴进 MT5 的脚本里跑,控制台会输出「The size of array is: 5」: [CODE] ArraySize( array[]); void OnStart() { // Declare an array int array[5]; // Get the size of the array using ArraySize int arraySize = ArraySize(array); // Print the array size Print("The size of array is: ", arraySize); // Output will be 5 } [/CODE] 逐行拆一下:第 4 行 int array[5]; 声明一个定长整型数组,编译期就占了 5 个坑;第 6 行把 ArraySize(array) 的返回值塞进 int 变量 arraySize,此处返回常数 5;第 8 行 Print 把字符串和变量拼起来丢到专家日志。外汇与贵金属自动化涉及高风险,数组越界可能让 EA 在 live 账户上意外终止,动手前先在策略测试器验证。

MQL5 / C++
ArraySize( array[]);
class="type">void OnStart()
  {
class=class="str">"cmt">// Declare an array
   class="type">int array[class="num">5];
class=class="str">"cmt">// Get the size of the array using ArraySize
   class="type">int arraySize = ArraySize(array);
class=class="str">"cmt">// Print the array size
   Print("The size of array is: ", arraySize); class=class="str">"cmt">// Output will be class="num">5
  }

◍ ArrayRange 与 ArraySize 的维度视角差

在 MQL5 里处理多维数组时,ArrayRange() 专门返回一个指定维度的元素个数,维度索引从 0 起算。它和 ArraySize() 最大的不同是:ArraySize() 只给一维数组的总元素数,遇到多维结构就失效或只能看第一维,而 ArrayRange() 能让你精确锁定某一层。 一维数组就是线性排布,像一排书;多维数组按行、列甚至更高层组织,类似矩阵。访问多维元素必须给全各维索引,结构更严谨也更容易在循环里写错边界。 下面这段声明了一个三维数组 double my3DArray[][2][4],第二维固定 2 个元素、第三维固定 4 个元素,第一维未指定长度。用 ArrayRange 分别取索引 0、1、2 的维度范围,打印出来维度 2 和 3 会稳定输出 2 和 4,维度 1 在未分配前动态数组默认 0,若后续用 ArrayResize 扩到 5 则会返回 5。外汇与贵金属 EA 开发中滥用 ArraySize 读多维数组,可能引发数组越界报错,这类风险在实盘里会直接中断策略。 把代码贴进 MT5 的 MQ 编辑器跑一遍,看 Print 输出就能确认你本地编译器对未初始化动态第一维的默认处理,不必盲信文档。

MQL5 / C++
class="type">class="kw">double my3DArray[][class="num">2][class="num">4];
class="type">int dimension1Index = ArrayRange(my3DArray, class="num">0);
class="type">int dimension2Index = ArrayRange(my3DArray, class="num">1);
class="type">int dimension3Index = ArrayRange(my3DArray, class="num">2);
Print("Number of elements in dimension class="num">1: ", dimension1Index);
Print("Number of elements in dimension class="num">2: ", dimension2Index);
Print("Number of elements in dimension class="num">3: ", dimension3Index);

静态与动态数组里删元素的差别

ArrayRemove() 接收三个参数:目标数组、起始索引、删除个数。它从指定位置起抹掉若干元素,但静态数组和动态数组的后续动作完全不同。 动态数组被删后会自动收缩尺寸,比如从索引 2 删 2 个,原本 5 个元素变成 3 个,后面元素前移补齐。静态数组尺寸锁死,删完不会变短,而是把数组末尾之后的内存内容抄过来填坑,所以长度依旧是 5。 下面这段可直接丢进 MT5 跑:fixedSizeArray 初值 11,13,17,21,42,从索引 2 删 2 个后打印,动态版剩 11,13,42,静态版则显示 11,13,21,42,42——末尾元素被复制填了位。外汇与贵金属 EA 里用静态数组做环形缓冲时,这种复制行为可能让你误判信号窗口,开 MT5 验证一眼便知。

MQL5 / C++
ArrayRemove(array[],start_index,count);
class="type">void OnStart()
  {
class=class="str">"cmt">// Declare fixed-size array
   class="type">int fixedSizeArray[class="num">5] = {class="num">11, class="num">13, class="num">17, class="num">21, class="num">42};
class=class="str">"cmt">// Declare dynamic array
   class="type">int dynamicArray[];
   ArrayResize(dynamicArray, class="num">5);
   dynamicArray[class="num">0] = class="num">11;
   dynamicArray[class="num">1] = class="num">13;
   dynamicArray[class="num">2] = class="num">17;
   dynamicArray[class="num">3] = class="num">21;
   dynamicArray[class="num">4] = class="num">42;
class=class="str">"cmt">// Print initial arrays
   Print("Initial fixedSizeArray: ");
   ArrayPrint(fixedSizeArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
   Print("Initial dynamicArray: ");
   ArrayPrint(dynamicArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
class=class="str">"cmt">// Remove two elements at index class="num">2 from both arrays
   ArrayRemove(fixedSizeArray, class="num">2, class="num">2);
   ArrayRemove(dynamicArray, class="num">2, class="num">2);
class=class="str">"cmt">// Print arrays after removal
   Print("After removing class="num">3 elements at index class="num">2 - fixedSizeArray: ");
   ArrayPrint(fixedSizeArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
   Print("After removing class="num">3 elements at index class="num">2 - dynamicArray: ");
   ArrayPrint(dynamicArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
  }

「定长与动态数组删除元素的实测差异」

在 MT5 里跑一段数组操作,能直接看出定长数组和动态数组在 ArrayRemove 之后的行为分歧。上面代码对两个数组都在索引 2 处删 2 个元素,定长数组逻辑长度不变、被删位用 0 填充,动态数组物理长度收缩。 外汇与贵金属行情序列常驻内存,用动态数组存 tick 或 bar 缓冲时,频繁 ArrayRemove 会触发重分配,回测中若每根 K 线删 2 元素、跑 5000 根则重分配可能超千次,倾向引起轻微延迟抖动;定长数组适合已知上限的滑点容差表,但别误以为删了就释放了内存。 开 MT5 新建脚本粘入代码,分别初始化 fixedSizeArray[10] 与动态数组各填 1~10,看 Print 输出:定长打印仍 10 列、动态变 8 列,这一个现象就够验证类型选择了。

MQL5 / C++
ArrayPrint(dynamicArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
ArrayRemove(fixedSizeArray, class="num">2, class="num">2);
ArrayRemove(dynamicArray, class="num">2, class="num">2);
Print("After removing class="num">3 elements at index class="num">2 - fixedSizeArray: ");
ArrayPrint(fixedSizeArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
Print("After removing class="num">3 elements at index class="num">2 - dynamicArray: ");
ArrayPrint(dynamicArray, class="num">0, " ", class="num">0, WHOLE_ARRAY);
把重复劳动交给小布
这些数组诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到脚本跑出来的数组状态,你专注决策而不是盯日志。

常见问题

默认启用 ARRAYPRINT_HEADER,会在输出时带上表头方便定位行列,不需要手动传参也会生效。
ArraySize 返回总元素数,ArrayRange 按指定维度返回该维长度,排查越界时两者要结合看。
被移除位置后的元素会前移,数组逻辑长度缩短,但底层保留内存不一定立即回收。
可以,小布内置的 AIGC 会把脚本里的数组打印和状态变化整理到品种页,省去自己翻日志。
若插入索引算错,后续排序和读取全偏移,信号逻辑可能整体错位,概率上会增加误判。