从基础到中级:数组和字符串(三)·进阶篇
「自己写进制转换比标准库多一个二进制出口」
这段脚本把 0xFEDAF3F4F5F6F7F8 这个 64 位长整型同时丢给标准库 PrintFormat 和自写的 ValueToString 做对照输出。标准库用 %I64u、%I64o、%I64X 只能拿到十进制、八进制、大写十六进制三套文本;自写函数靠 format 参数 0/1/2/3 分别对应同样的三种外加二进制串,等于补上了 MT5 原生格式化不直出二进制的缺口。 ValueToString 的核心在 28 到 33 行:szChars 写死十六进制字符表,sz0 作累加容器,while(arg) 配合内部 switch(format) 按目标进制不断取低 4 位或低 3 位。原文 41 到 43 行是其中一个 case 的收尾——取 arg 低 4 位映射成字符拼到 sz0 头部,再把 arg 右移 4 位,break 退出本次循环。 跑这段代码你会看到同一数值的二进制表达长度达到 64 字符,而十六进制仅 16 字符。外汇与贵金属 EA 里若要做位标志压缩存储,这种自写转换在日志排错时比标准库更直接,但需注意长整型位操作在溢出时行为不可控,实盘前务必在策略测试器用极端值验证。
class="num">41. sz0 = StringFormat("%c%s", szChars[(class="type">uchar)(arg & 0xF)], sz0); class="num">42. arg >>= class="num">4; class="num">43. break; class="num">44. class="kw">default: class="num">45. class="kw">return "Format not implemented."; class="num">46. } class="num">47. } class="num">48. class="num">49. class="kw">return sz0; class="num">50. } class="num">51. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">01. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">02. class="macro">#class="kw">property copyright "Daniel Jose" class="num">03. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">04. class="type">void OnStart(class="type">void) class="num">05. { class="num">06. class="type">long value = 0xFEDAF3F4F5F6F7F8; class="num">07. class="num">08. PrintFormat("Translation via standard library.\n" + class="num">09. "Decimal: %I64u\n" + class="num">10. "Octal : %I64o\n" + class="num">11. "Hex : %I64X", class="num">12. value, value, value class="num">13. ); class="num">14. PrintFormat("Translation personal.\n" + class="num">15. "Decimal: %s\n" + class="num">16. "Octal : %s\n" + class="num">17. "Hex : %s\n" + class="num">18. "Binary : %s", class="num">19. ValueToString(value, class="num">0), class="num">20. ValueToString(value, class="num">1), class="num">21. ValueToString(value, class="num">2), class="num">22. ValueToString(value, class="num">3) class="num">23. ); class="num">24. } class="num">25. class=class="str">"cmt">//+------------------------------------------------------------------+ class="num">26. class="type">class="kw">string ValueToString(class="type">ulong arg, class="type">char format) class="num">27. { class="num">28. const class="type">class="kw">string szChars = "0123456789ABCDEF"; class="num">29. class="type">class="kw">string sz0 = ""; class="num">30. class="num">31. while (arg) class="num">32. class="kw">switch (format) class="num">33. {
把整数压成自定义字符的四种进制切法
这段 switch 是数字转字符串函数的核心分支,按 mode 决定每次从 arg 里抠几位、用哪个字符集映射。mode 0 走十进制,每次取 arg%10 的余数当索引,再 arg/=10 把个位削掉;mode 1 取低 3 位(arg&0x7)后右移 3 位,等价于八进制输出。 mode 2 取低 4 位(arg&0xF)后右移 4 位,是十六进制思路;mode 3 只取最低位(arg&0x1)右移 1 位,即二进制。所有分支都用 StringFormat("%c%s", szChars[idx], sz0) 把新字符拼到原串前面,保证高位在左。 没覆盖到的 mode 直接返回 "Format not implemented.",调用前得自己约束入参范围,否则拿到这句英文才算踩坑。下面贴出原文片段,逐行对着看比脑补快。
case class="num">0: sz0 = StringFormat("%c%s", szChars[(class="type">uchar)(arg % class="num">10)], sz0); arg /= class="num">10; break; case class="num">1: sz0 = StringFormat("%c%s", szChars[(class="type">uchar)(arg & 0x7)], sz0); arg >>= class="num">3; break; case class="num">2: sz0 = StringFormat("%c%s", szChars[(class="type">uchar)(arg & 0xF)], sz0); arg >>= class="num">4; break; case class="num">3: sz0 = StringFormat("%c%s", szChars[(class="type">uchar)(arg & 0x1)], sz0); arg >>= class="num">1; break; class="kw">default: class="kw">return "Format not implemented."; } class="kw">return sz0; } class=class="str">"cmt">//+------------------------------------------------------------------+
◍ 用数组长度卡住密码位数
MQL5 里数组分静态和动态两种,区别只在声明写法:像 uchar psw[] 这种带空方括号的就是动态数组,必须在运行时用 ArrayResize 分配内存,否则访问任一元素都会让 EA 直接崩。字符串本质也是动态数组,但编译器会自动管内存;普通 uchar 数组不行,得自己写第 18 行那种分配。
代码 04 里第 18 行用 StringLen(szArg) 把密码长度绑死成秘密短语长度。想手动控密码为 8 位,就给函数加参数,在第 18 行传进去。但原文实测:若第 6 行短语长度超过 8(本例葡语短语 44 字符),循环靠找 NULL 停,越界访问会让程序崩溃——这是动态数组最常见的暗坑。
改成只取短语最后 8 字符(代码 06)能跑通,但第 24 行在固定位置反复覆盖,最终密码看起来只有 8 个字符真生效,其余字符白写,制造虚假安全感。
更进一步(代码 07、08):第 20 行把数组初始化为 0,第 24 行改为对各字符 ASCII 值求和,再用一次循环把总和映射回 szCodePhrase 中的可见字符。跑出来结果恰有重复字符,说明求和法能把短语里所有符号都利用上。
开 MT5 把代码 04 的 OnStart 改传长度参数,先试 8 再试 44,对比崩溃与正常输出,就能直观抓到动态数组越界的边界。外汇与贵金属 EA 涉及杠杆,代码崩在实时账户可能错失平仓,高危环境务必本地先测。
<span class="number">class="num">01</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">02</span>. <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose"</span> <span class="number">class="num">03</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">04</span>. <span class="keyword">class="type">void</span> <span class="functions">OnStart</span>(<span class="keyword">class="type">void</span>) <span class="number">class="num">05</span>. { <span class="number">class="num">06</span>. <span class="keyword">const</span> <span class="keyword">class="type">class="kw">string</span> sz_Secret_Phrase = <span class="class="type">class="kw">string">"Um pequeno jabuti xereta viu dez cegonhas felizes"</span>; <span class="number">class="num">07</span>. <span class="number">class="num">08</span>. <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Result:\n"</span>, PassWord(sz_Secret_Phrase)); <span class="number">class="num">09</span>. } <span class="number">class="num">10</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">11</span>. <span class="keyword">class="type">class="kw">string</span> PassWord(<span class="keyword">const</span> <span class="keyword">class="type">class="kw">string</span> szArg) <span class="number">class="num">12</span>. { <span class="number">class="num">13</span>. <span class="keyword">const</span> <span class="keyword">class="type">class="kw">string</span> szCodePhrase = <span class="class="type">class="kw">string">"The quick brown fox jumps over the lazy dog"</span>; <span class="number">class="num">14</span>. <span class="number">class="num">15</span>. <span class="keyword">class="type">uchar</span> psw[]; <span class="number">class="num">16</span>. pos; <span class="number">class="num">17</span>. <span class="number">class="num">18</span>. <span class="functions">ArrayResize</span>(psw, <span class="functions">StringLen</span>(szArg)); <span class="number">class="num">19</span>. <span class="keyword">for</span> (<span class="keyword">class="type">int</span> c = <span class="number">class="num">0</span>; szArg[c]; c++) <span class="number">class="num">20</span>. { <span class="number">class="num">21</span>. pos = (<span class="keyword">class="type">uchar</span>)(szArg[c] % <span class="functions">StringLen</span>(szCodePhrase)); <span class="number">class="num">22</span>. psw[c] = (<span class="keyword">class="type">uchar</span>)szCodePhrase[pos]; <span class="number">class="num">23</span>. } <span class="number">class="num">24</span>. <span class="keyword">class="kw">return</span> <span class="functions">CharArrayToString</span>(psw); <span class="number">class="num">25</span>. } <span class="number">class="num">26</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
「正文」
<span class="number">08</span>. <span class="functions">Print</span>(<span class="string">"Result:\n"</span>, PassWord(sz_Secret_Phrase, <span class="number">8</span>)); <span class="number">09</span>. } <span class="number">10</span>. <span class="comment">//+------------------------------------------------------------------+</span> <span class="number">11</span>. <span class="keyword">string</span> PassWord(<span class="keyword">const</span> <span class="keyword">string</span> szArg, <span class="keyword">const</span> <span class="keyword">uchar</span> SizePsw) <span class="number">12</span>. { <span class="number">13</span>. <span class="keyword">const</span> <span class="keyword">string</span> szCodePhrase = <span class="string">"The quick brown fox jumps over the lazy dog"</span>; <span class="number">14</span>. <span class="number">15</span>. <span class="keyword">uchar</span> psw[], <span class="number">16</span>. pos; <span class="number">17</span>. <span class="number">18</span>. <span class="functions">ArrayResize</span>(psw, SizePsw); <span class="number">19</span>. <span class="keyword">for</span> (<span class="keyword">int</span> c = <span class="number">0</span>; szArg[c]; c++) <span class="number">20</span>. { <span class="number">21</sp