MQL5 简介(第 6 部分):MQL5 中的数组函数新手指南 (二)·进阶篇
(2/3)· 接上篇基础,继续拆解 ArrayPrint 到 ArrayRemove 的 5 个函数,少走一半弯路
数组插入与复制的覆盖差异
在 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 下才暴露,建议用历史数据回放验证。
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 账户上意外终止,动手前先在策略测试器验证。
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 输出就能确认你本地编译器对未初始化动态第一维的默认处理,不必盲信文档。
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 验证一眼便知。
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 列,这一个现象就够验证类型选择了。
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);