MQL5 编程基础:数组(基础篇)
「MQL5 里数组到底怎么排」
MQL5 的数组分一维和多维,索引默认从 0 开始,这一点和 C 系语言一致,但 MT5 在运行时会对越界访问直接抛错而非静默截断。写 EA 或指标时,若用 ArrayResize() 动态扩容量,旧数据默认保留在低位,新元素补到尾部。 静态数组在声明时就要给定长度,例如 double a[10]; 表示 10 个双精度浮点,占用连续内存。实测在 64 位 MT5 终端里,这类数组的随机读写延迟通常在纳秒级,但跨周期拷贝大数组(超过 10 万元素)会明显拖慢 OnCalculate 的 tick 响应。 动态数组更灵活,配合 ArraySetAsSeries() 可把时间序翻转,让最新价落在索引 0,老数据往后排。做价格行为分析时,这种倒置能少写不少偏移计算,但也容易在循环里搞反方向,建议开 MT5 用下面代码自测一遍。
class="type">class="kw">double price[]; ArraySetAsSeries(price, true); CopyClose(_Symbol, _Period, class="num">0, class="num">100, price); class=class="str">"cmt">// price[class="num">0] 为最新收盘价,price[class="num">99] 为 class="num">100 根 K 线前
◍ 数组和变量到底差在哪
不少写 EA 的新手对数组有本能的抵触,其实它和普通变量本质是一类东西。抛开符号细节,写法和声明逻辑差别很小,只是数组名后面带了一对中括号。 声明时最能看出分野:普通变量得一个一个起名,数组只要写一次名字,在括号里填元素个数就行。当你要处理几十个价格样本时,用数组比堆 Variable0、Variable1…… 要干净太多。 有人怕数组,纯粹是怕那两个方括号 [ ] 找不着键位。它们就贴在 Enter 键两侧,左开右合,记住一次就不再别扭。 下面这段对照很直接:左边三个独立 double 变量做加法,右边用长度为 3 的数组做完全一样的计算。差异只在方括号里的下标,从 0 开始数。
Variable0=class="num">1; Variable1=class="num">2; Variable2=Variable0+Variable1; class="type">class="kw">double Variable[class="num">3]; Variable[class="num">0]=class="num">1; Variable[class="num">1]=class="num">2; Variable[class="num">2]=Variable[class="num">0]+Variable[class="num">1];
从 0 开始计数的数组索引
MQL5 里的数组是一组共用同一个名字、按编号排列的变量,所有元素必须同类型(int、double 等),数组大小就是实际元素个数。容易踩坑的是:元素索引从 0 起算,所以最后一个元素的索引比数组长度小 1。 比如声明 double Variable[3],真实元素是 Variable[0]、Variable[1]、Variable[2],肉眼看「长度 3」和「最大下标 2」对不上,但相比那些下标从 1 开始、或用末位下标代表长度的语言,这种 0-based 设计在指针寻址和循环处理上更干净。 要拿到数组实际长度,别手算,直接调 ArraySize()。下面这段代码跑完,Size 会被赋值为 3,你可以在 MT5 里建个脚本粘进去验证。
class="type">class="kw">double Variable[class="num">3]; class="type">class="kw">double Variable[class="num">3]; class="type">int Size=ArraySize(Variable);
class="type">class="kw">double Variable[class="num">3]; class="type">class="kw">double Variable[class="num">3]; class="type">int Size=ArraySize(Variable);
「静态与动态数组的边界怎么划」
MQL5 里数组分两种:声明时直接给定长度的就是静态数组,比如 double Variable[3] 或用 #define SIZE 3 再 double Variable[SIZE],这两种写法长度在运行期都锁死,程序里改不了。
声明时只写 double Variable[]、不填长度的,是动态数组。用之前必须先用 ArrayResize(Variable,3) 把大小撑开,否则就是个空壳。动态数组的核心好处是执行期间能反复 ArrayResize 改尺寸,这是和静态最本质的区别。
不再需要的时候,调 ArrayFree(Variable) 把数组释放掉,效果等同于 ArrayResize(Variable,0),大小归零、内存交还。常见用法是在函数开头清一遍,或者确认后续逻辑不再采集数据时释放,减少 MT5 终端的内存占用。
想确认某个数组到底是什么类型,用 ArrayIsDynamic(Variable) 即可:动态返回 true,静态返回 false。开 MT5 随便建个脚本把下面代码贴进去跑一下,能在观测窗口直接看到 dynamicArray 的取值差异。
<span class="keyword">class="type">class="kw">double</span> Variable[<span class="number">class="num">3</span>]; <span class="preprocessor">class="macro">#define SIZE <span class="number">class="num">3</span></span> <span class="keyword">class="type">class="kw">double</span> Variable[SIZE]; <span class="keyword">class="type">class="kw">double</span> Variable[]; <span class="functions">ArrayResize</span>(Variable,<span class="number">class="num">3</span>); <span class="functions">ArrayFree</span>(Variable); <span class="functions">ArrayResize</span>(Variable,<span class="number">class="num">0</span>); <span class="keyword">class="type">bool</span> dynamicArray=<span class="functions">ArrayIsDynamic</span>(Variable);
◍ 声明即赋值:静态数组的初始化套路
做 MT5 面板时,常要一次性排一排同类型按钮,每个按钮文字不同。与其复制几十行 ObjectCreate,不如直接把字符串塞进数组,再让循环去迭代——声明和赋值的活可以在同一行干完。 下面这种写法里,数组大小不用手写:花括号里有几个值,编译器就定多大。它本质还是静态数组,只是尺寸由值列表隐式决定。 string Variable[] = {"Button 1", "Button 2", "Button 3"}; 你也可以显式写 Variable[3],不会报错。但后面要加按钮或减按钮时,得同时改两处;更稳的做法是用 ArraySize() 拿长度,主逻辑里只认这个函数返回的值,值列表随便增删。 如果所有元素都要同一个初值,ArrayInitialize() 一行搞定。比如对 double 数组执行后,四个元素全是 1。只填部分元素就用 ArrayFill(),从下标 0 开始填 2 个 1、再从下标 2 填 2 个 2,跑完得到 [1,1,2,2] 这种分段初值。 别把数组大小硬编码进循环边界,外汇和贵金属 EA 面板迭代快,改一处值列表却漏改尺寸常量,轻则按钮缺失重则数组越界报错。
class="type">class="kw">string Variable[] = {"Button class="num">1", "Button class="num">2", "Button class="num">3"}; class="type">class="kw">string Variable[class="num">3] = {"Button class="num">1", "Button class="num">2", "Button class="num">3"}; ArrayInitialize(Variable,class="num">1); class="type">class="kw">double Variable[class="num">4]; ArrayFill(Variable,class="num">0,class="num">2,class="num">1); ArrayFill(Variable,class="num">2,class="num">2,class="num">2);
循环里别让 ArraySize 反复拖后腿
处理静态数组时,按任务需求决定正向还是反向跑 for 循环即可,编译期大小已知,没什么额外开销。 动态数组才容易踩坑。正确做法是先取一次 ArraySize 存进变量,再拿变量当循环上限;这样函数只调用一次。 若把 ArraySize() 直接写进循环条件,每次迭代都会重新调用,循环耗时显著拉长——调用函数比读变量贵得多,这种写法不建议留着。 算法允许的话,反向循环从 ArraySize(Variable)-1 跑到 0,函数同样只在开头算一次,速度就回来了。外汇与贵金属 EA 高频跑数组时,这类细节可能直接影响 tick 处理延迟,属于高风险环境下的隐性损耗。
class=class="str">"cmt">//--- forwards for(class="type">int i=class="num">0; i<SIZE; i++){ class=class="str">"cmt">// some manipulations on the Variable[i] element } class=class="str">"cmt">//--- backwards for(class="type">int i=SIZE-class="num">1; i>=class="num">0; i--){ class=class="str">"cmt">// some manipulations on the Variable[i] element } class="type">int Size=ArraySize(Var); for(class="type">int i=class="num">0; i<Size; i++){ class=class="str">"cmt">// some manipulations on the Variable[i] element } for(class="type">int i=class="num">0; i<ArraySize(Variable); i++){ class=class="str">"cmt">// some manipulations on the Variable[i] element } for(class="type">int i=ArraySize(Variable)-class="num">1; i>=class="num">0; i--){ class=class="str">"cmt">// some manipulations on the Variable[i] element }