首页 / 交易学院 / 小布知识库 / MQL5数组编程从怕到精通
MQL5数组编程从怕到精通
📊

MQL5数组编程从怕到精通

全面拆解静态动态多维数组与性能陷阱

MQL5开发 难度 · 入门 2026-03-09 7 分钟阅读
#MQL5#数组#动态数组#编程基础

数组到底是什么:和变量有什么关系

很多编程新手对数组心存惧意,其实数组和普通的变量类似。不考虑符号特性的细节,语句的编写没有太大区别,无论是使用简单变量还是数组。区别只在于使用数组时变量名称包含括号,且在声明时只需写一次名字并在括号中指定元素数量。在处理大量实际任务时,数组相比多个独立变量具有压倒性优势:你可以用循环统一处理成百上千个元素,而不必复制粘贴代码。

数组是具有相同名称的编号变量集,基本属性包括名称、类型(int、double等)和大小。MQL5中数组元素索引从0开始,因此最后一个元素的索引比元素数量小1。例如 double Variable[3] 包含 Variable[0]、Variable[1]、Variable[2]。这种从0开始计数虽初看不便,但相比从1开始的语言,在指针运算和内存布局上更一致,也便于与C++生态对接。

double Variable[3];

Variable[0]=1;
Variable[1]=2;
Variable[2]=Variable[0]+Variable[1];
✦ 记忆括号位置
方括号 [ ] 很少在数组以外使用,它们逻辑上位于 Enter 键两侧,左括号后是右括号,记住这个位置能减少编码不适。

静态数组与动态数组的核心区别

数组分为静态和动态。静态数组在声明时指定大小,之后无法在程序中更改;动态数组声明时不指定大小,使用 ArrayResize() 在运行时多次调整。静态大小可直接写数字或用 #define 常量,动态则必须先用 ArrayResize 设定尺寸才能使用。

#define SIZE 3
double Variable[SIZE];

double Variable[];
ArrayResize(Variable,3);

释放数组使用 ArrayFree(),其将大小设为0,等价于 ArrayResize(Variable,0),在不再需要数组或函数开头重置收集数据时很有用。ArrayIsDynamic() 可判断数组是否动态,返回 true/false。动态数组是MQL5处理不定长数据(如挂单列表)的基础,但错误使用会带来严重性能问题。

ArrayFree(Variable);
bool dynamicArray=ArrayIsDynamic(Variable);

数组初始化与批量赋值技巧

声明时可用值列表直接初始化,如 string Variable[]={"Button 1","Button 2","Button 3"},此时虽未指定大小仍是静态数组。建议用 ArraySize() 而非硬编码数字获取大小,方便后续改列表不改主代码。若需同值填充用 ArrayInitialize(),部分填充用 ArrayFill()。

string Variable[] = {"Button 1", "Button 2", "Button 3"};
ArrayInitialize(Variable,1);
double V[4];
ArrayFill(V,0,2,1);
ArrayFill(V,2,2,2);
⚠ 不要写死大小
初始化列表已定义元素数时,若再写死数组维度如 [3],后续增删元素需同时改两处,易出错。用 ArraySize 或常量更稳。

数组迭代循环的正确写法

通常用 for 循环处理数组。静态已知大小可正反向迭代;动态数组应在循环前取大小存变量,避免每次迭代调用 ArraySize() 拖慢速度。若算法允许反向,可省大小变量,因 ArraySize 只调用一次。正向且频繁取大小是典型性能反模式。

int Size=ArraySize(Var);
for(int i=0; i<Size; i++){ /* ... */ }
for(int i=ArraySize(Variable)-1; i>=0; i--){ /* ... */ }
⚠ 禁止循环内调ArraySize
for(int i=0; i<ArraySize(Variable); i++) 每次迭代都调用函数,大数据集下耗时数倍,务必先用变量缓存。

多维数组结构与第一维度动态性

MQL5支持最多4维数组。仅第一维可静态或动态,其余必须静态。如 double Variable[][3][3] 可用 ArrayResize 改第一维。ArraySize 返回总元素数,不是第一维大小,需用 ArrayRange 或除法计算。多维初始化用嵌套花括号,ArrayInitialize/Fill 按一维序列处理。

double Variable[][3][3];
ArrayResize(Variable,3);
int Size=ArraySize(Variable)/9;
double V[][3][3]={{1,2,3},{4,5,6},{7,8,9}};

多维数组传参时除第一维外须明确大小,因此无法写通用维度计算函数。推荐用常量定义维度,在初始化阶段算好。若需变其他维度,可用OOP或更大数组加变量模拟。

函数传参:数组只能引用传递

与变量不同,数组只能通过引用(&)传给函数,函数内直接操作原数组,改动会影响外部。普通变量按值传无法被函数修改,加 & 后可改。传递数组自变量要写括号表明是数组引用。

void Func(double &arg[]){ }
double var[][3][3];
void Func(double &arg[][3][3]){ }

文件保存与载入的维度陷阱

保存一维数组先写总大小再写数组;载入时读大小、Resize、读数组。多维载入需算第一维大小,如三维且后两维常量SIZE1、SIZE2时,第一维=总/(SIZE1*SIZE2)。注意 Var[2][3] 与 Var[3][2] 总元素同,若要区分需存更多元信息。

bool LoadArrayFromFile3(string FileName,double &Array[][SIZE1][SIZE2]){
   int h=FileOpen(FileName,FILE_BIN|FILE_READ);
   if(h==-1)return(false);
   int SizeTotal=FileReadInteger(h,INT_VALUE);
   int Elements=SIZE1*SIZE2;
   int Size=SizeTotal/Elements;
   ArrayResize(Array,Size);
   FileReadArray(h,Array);
   FileClose(h);
   return(true);
}

动态数组扩容性能:逐次加1 vs 块扩容

不确定大小时动态数组很有用。若每加一个订单就 ArrayResize(ar,i+1) 逐次加1,速度极慢(测试数秒);用块扩容(如预留1024,满再扩)近乎瞬时。附 CDynamicArray 类封装了块扩容,应在 Include 文件夹使用。

class CDynamicArray{
private:
   int m_ChunkSize; int m_ReservedSize; int m_Size;
public:
   double Element[];
   void CDynamicArray(int ChunkSize=1024){ m_Size=0; m_ChunkSize=ChunkSize; m_ReservedSize=ChunkSize; ArrayResize(Element,m_ReservedSize); }
   void AddValue(double Value){ m_Size++; if(m_Size>m_ReservedSize){ m_ReservedSize+=m_ChunkSize; ArrayResize(Element,m_ReservedSize); } Element[m_Size-1]=Value; }
   int Size(){ return(m_Size); }
};
✦ 性能对比
sTest_Speed.mq5 脚本显示:5万次逐次Resize耗时秒级,块扩容毫秒级。实盘EA必用块扩容类。

索引编排顺序与ArraySetAsSeries陷阱

ArraySetAsSeries(ar,true) 设逆序索引,新元素加开头;false 正常序加末尾。注意:Resize 后再 SetAsSeries 会导致元素移位且可能引入随机值(用户评论证实)。正确顺序是先 Resize 再 SetAsSeries。逆序索引常用于EA复制价格数据,从右向左数柱。

double ar[];
ArrayResize(ar,2); ar[0]=1; ar[1]=2;
ArraySetAsSeries(ar,true);
ArrayResize(ar,3); ar[0]=3;
Alert(ar[0]," ",ar[1]," ",ar[2]);
⚠ Resize与Series顺序坑
先SetAsSeries再Resize会复制数值到新位置产生乱序,社区已报bug。务必Resize在前,Series在后。

复制、排序与搜索标准函数

ArrayCopy 可整体或局部复制,自动扩动态数组;也可同数组内移位。ArraySort 排一维数组;ArrayBsearch 二分搜须先排序,返回匹配或最接近较小值索引,找不到超范围返0或末位。无序只能遍历。ArrayMaximum/Minimum 返极值索引,可限范围。

double ar1[]={1,2,3}; double ar2[]; ArrayCopy(ar2,ar1);
double ar[]={1,3,2,5,4}; ArraySort(ar);
int index=ArrayBsearch(ar,3);
int MaxIndex=ArrayMaximum(ar,5,3);

用OOP实现灵活多维数组

原文提供 CArrayBase 基类与 CDim/CArr 子类,可构造每第一维元素含不同长度第二维的锯齿数组。基类析构删对象,子类构造按大小Resize。虽非通用,但解决静态多维限制。读者也可借模板类 CDynamicArray<T> 泛型化。

class CArrayBase{
public: CArrayBase *D[]; double V[];
   void ~CArrayBase(){ for(int i=ArraySize(D)-1;i>=0;i--) if(CheckPointer(D[i])==POINTER_DYNAMIC) delete D[i]; }
};
✦ 官方函数频率
作者排位:ArraySize/Resize最常用;Copy/Initialize/Fill/Free简单好用;Sort重要但少;Bsearch罕见但关键;SetAsSeries等极少用。

常见问题

每次ArrayResize可能重分配内存,逐次加1元素极慢。应使用块扩容:预分配较大容量(如1024),满再成块增加,或用附带的CDynamicArray类AddValue方法。
ArraySize返回总元素数。第一维大小=总元素数/(其余维大小乘积),或用ArrayRange(arr,0)直接取第一维元素数。
务必先ArrayResize确定大小,再调用ArraySetAsSeries(ar,true)设逆序;反之先设逆序再Resize会导致元素错位和随机值,实盘会出bug。
数组只能引用传递,声明写成 void Func(double &arg[]),多维除第一维外须写明维度如 &arg[][3][3],函数内改动直接影响外部原数组。
一维先FileWriteInteger写ArraySize再FileWriteArray;读时读大小ArrayResize后FileReadArray。多维需已知后维常量,读总大小除以常量积得第一维,再Resize读取。