掌握 MQL5:从入门到精通(第二部分)基本数据类型和变量的使用·进阶篇
(2/3)· 为什么同样的报价处理,有人写出一堆 bug 有人代码清爽?变量与类型的地基没打牢
不少交易者抄来一段 EA 代码,改两行就编译报错,却说不清变量和常量到底差在哪。把数据随手塞进没声明的名字里,运行时报价一变就溢出或静默出错。先搞懂 RAM 里这块地怎么划,后面写策略才不至于半夜爆仓还找不到原因。
◍ 什么时候该用变量
在 MQL5 里,只要某段数据在 EA 或指标运行期间会变动,就该用变量来装,而不是写死常量。声明方式极简:先写类型,再写你起的名字,编译器就会按比例分配内存。 例 11 给出的模板只有一行,却决定了后续能不能用名字随时读写这块内存。类型与占用字节数后面会展开,但先把「可变即变量」这个判断钉牢,能少写很多重复代码。 外汇与贵金属波动快、重连行情频繁,变量用错作用域或类型,可能在跳空时读出脏值,实盘前务必在 MT5 策略测试器里跑一遍验证。
type variable_name;
给变量起名时容易踩的坑
MQL5 里你自定义的变量名、函数名统称标识符,只能由拉丁字母、数字和下划线组成,且不能以数字开头,也不能和保留字撞车,长度上限是 63 个字符。 标识符区分大小写,myVariable 和 MyVariable 在编译器眼里是两个独立变量。虽然语法允许,但同一文件里故意用仅大小写不同的名字纯属给自己埋雷。 手滑写错大小写时,编译器会报 'Undeclared variable',定位还算容易;真正麻烦的是两个名字都合法、只是大小写不同,读代码时极容易看串。 其余基本没限制,理论上你甚至能用内置函数名当变量名,但真这么干的人大概率是想让同事崩溃。外汇与贵金属杠杆高、波动猛,写 EA 前先把命名规矩定死,能少掉很多诡异 bug。
「变量写入与初始化那点事」
在 MQL5 里把数据放进变量靠的是赋值操作,声明时顺手给值叫初始化,二者本质都是用等号把右侧结果搬进左侧单元格。下面的片段先在建变量时塞了 0,后又改成 10,两次写入都会覆盖原值,旧数据直接丢弃。 变量一旦写好,程序任何位置都能按名字调用它,比如丢进函数当参数或塞进表达式参与运算。对外汇和贵金属 EA 来说,这种覆盖式写入意味着状态变量若不小心被重复赋值,逻辑会悄悄跑偏,属于高频踩坑点。 [CODE] 里两行就是最直白的示范:第一行 int counter = 0; 是创建即初始化,第二行 counter = 10; 是普通赋值。等号左侧必须是已声明变量名,右侧得是能算出整数值的表达式。
class=class="str">"cmt">// Initialization(at creation) class="type">int counter = class="num">0; class=class="str">"cmt">// Normal assignment counter = class="num">10;
◍ 赋值表达式里的右向左陷阱
赋值操作在 MQL5 表达式里优先级最低,整体从右向左执行:右侧先算出一个表达式结果,再写进左侧变量。比如 a = b + c,是先加后写,不是边写边算。 同一个变量出现在等号两侧时,可以缩写。a = a - c 直接写成 a -= c 就行,这种简写对乘、除、移位等所有二元运算符都有效。但前提是变量能干净地分离出来——a = a*(1+1/a) 不展开括号就没法简写,而 a = a*(b+c) 写成 a *= b+c 毫无压力。 整数加减 1 不用走赋值,递增递减运算符更短。它们是只吃一个变量的一元操作,有前缀和后缀两种写法:++a 先加后用,a++ 先用后加。代码里 Print(++a) 输出 2 且 a 变成 2,Print(a++) 输出 2 但 a 已经变成 3,这个差异在条件判断里容易埋坑。 多个赋值可以级联,顺序仍是右到左。int a=1, c=3; a = c = a+c; 先算 a+c 得 4,c 被赋 4,a 再等于 c 也就是 4。外汇与贵金属 EA 里用级联初始化全局状态时要手动推一遍,MT5 策略测试器里单步能直接看寄存器变化。
a = b + c; a = a — c; a -= c; a++; class=class="str">"cmt">// Increment. Increase a by class="num">1 b--; class=class="str">"cmt">// Decrement. Decreases b by class="num">1 class="type">int a = class="num">1; Print(++a); class=class="str">"cmt">// class="num">2, and a == class="num">2 Print(a++); class=class="str">"cmt">// class="num">2, but a == class="num">3 class="type">int a=class="num">1, c=class="num">3; a = c = a+c; class=class="str">"cmt">// first a+c(class="num">4), then c = class="num">4, then a = c(i.e. a = class="num">4)
MQL5 基础类型到底怎么占内存
计算机底层只认整数,MQL5 里整数运算既快又直接,但一旦超出类型上限就会静默截断。比如 uchar 只占 1 字节,范围锁死在 [0,255],你硬写 256 或 -1 会发生溢出,多余位直接丢,结果绕回区间内。有符号的 char 则是 [-128,127],正负对半劈。实际写 EA 最常用 int(4 字节,写起来省事)和 long(8 字节,编译器能优化到接近原生速度),但做像素级仓位标记时 uchar 也挺顺手。 bool 占 1 字节,只能是 true 或 false;非零数字当逻辑值时都算 true,只有 0 是 false,不过拿数字硬当布尔用容易埋坑。实数方面现在基本全用 double(8 字节,最小正值约 2.22e-308,最大约 1.80e+308),float 只在超大数据集省内存时偶尔露面。实数算完最后一位几乎必有误差,可能得到 1.00000001 而非 1.0,比较两个价格别直接用 ==,取差值再比一个大于误差的小量才稳。 datetime 占 8 字节,存的是 1970-01-01 起算的秒数,有效到 3000-12-31,编译期常量 __DATETIME__ 能拿精确时间戳。color 占 4 字节但只用 3 字节装 RGB,每个分量 0–255,混合比例决定色调,写策略时直接从调色板挑 clr 开头常量最省心。枚举本质是有符号 4 字节整数,不显式赋值就从 0 自增;若只给首个元素定 1,后面依次顺延。 下面这段把颜色和枚举的写法揉在一起,开 MT5 新建脚本粘进去就能跑: color myColor1=C'100,200,30'; // 用十进制 RGB 构一个绿调颜色 color myColor2=C'0xFF,0x00,0x5A'; // 十六进制写法,品红偏蓝 color a = C'255,0,0'; // 纯红 color b = C'0,255,0'; // 纯绿 color d = a+b; // 分量相加,超 255 会回卷 Print(a," ",b," ",d); // 打印三个颜色的内部整数值 //--- 第一步:定义枚举类型(新数据类型) enum ENUM_DIRECTION { Upward, // 默认 0 Downward, // 默认 1 Aside // 默认 2 }; //--- 第二步:声明该类型变量并初始化 ENUM_DIRECTION next=Upward; //--- 第三步:使用变量 Print(next); // 输出 0 //--- 显式指定枚举值写法 enum DIRECTION { Upward = 1, Downward = -1, Aside = 0 }; 外汇和贵金属杠杆高、滑点跳空频繁,用整数存 tick 计数或枚举标方向时,先想清楚范围再选类型,否则实盘里溢出丢数据没人提醒你。
class="type">color myColor1=C&class="macro">#x27;class="num">100,class="num">200,class="num">30&class="macro">#x27;; class="type">color myColor2=C&class="macro">#x27;0xFF,0x00,0x5A&class="macro">#x27;; class="type">color a = C&class="macro">#x27;class="num">255,class="num">0,class="num">0&class="macro">#x27;; class="type">color b = C&class="macro">#x27;class="num">0,class="num">255,class="num">0&class="macro">#x27;; class="type">color d = a+b; Print(a," ",b," ",d); class=class="str">"cmt">//--- First step: creating a new list(new data type) enum ENUM_DIRECTION { Upward, Downward, Aside }; class=class="str">"cmt">//--- Second step: description(and, if necessary, initialization) of a variable of this type ENUM_DIRECTION next=Upward; class=class="str">"cmt">//--- Third step: using the variable Print(next); class=class="str">"cmt">//--- First step: creating a new list(new data type) enum DIRECTION { Upward = class="num">1, Downward = -class="num">1, Aside = class="num">0 };