从基础到中级:数组和字符串(三)·进阶篇
📘

从基础到中级:数组和字符串(三)·进阶篇

第 2/3 篇

「自己写进制转换比标准库多一个二进制出口」

这段脚本把 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 里若要做位标志压缩存储,这种自写转换在日志排错时比标准库更直接,但需注意长整型位操作在溢出时行为不可控,实盘前务必在策略测试器用极端值验证。

MQL5 / C++
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.",调用前得自己约束入参范围,否则拿到这句英文才算踩坑。下面贴出原文片段,逐行对着看比脑补快。

MQL5 / C++
   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 涉及杠杆,代码崩在实时账户可能错失平仓,高危环境务必本地先测。

MQL5 / C++
<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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">uchar</span>&nbsp;&nbsp; psw[];
<span class="number">class="num">16</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos;
<span class="number">class="num">17</span>.
<span class="number">class="num">18</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">ArrayResize</span>(psw, <span class="functions">StringLen</span>(szArg));
<span class="number">class="num">19</span>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; {
<span class="number">class="num">21</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos = (<span class="keyword">class="type">uchar</span>)(szArg[c] % <span class="functions">StringLen</span>(szCodePhrase));
<span class="number">class="num">22</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; psw[c] = (<span class="keyword">class="type">uchar</span>)szCodePhrase[pos];
<span class="number">class="num">23</span>.&nbsp;&nbsp;&nbsp;&nbsp; }
<span class="number">class="num">24</span>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <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>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">uchar</span>&nbsp;&nbsp; psw[], <span class="number">16</span>.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pos; <span class="number">17</span>. <span class="number">18</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="functions">ArrayResize</span>(psw, SizePsw); <span class="number">19</span>.&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">for</span> (<span class="keyword">int</span> c = <span class="number">0</span>; szArg[c]; c++) <span class="number">20</span>.&nbsp;&nbsp;&nbsp;&nbsp; { <span class="number">21</sp

常见问题

用除基取余法把整数压进数组,按 2/8/16 切分并映射到自定义字符表,最后逆序输出即可多一个二进制出口。
用数组长度做上限判断,生成前先算最大位,超了就降进制或截断,避免溢出和位数失控。
可以,小布能跑你的代码片段、比对各进制输出并提示数组越界风险,省去手动逐位核对。
标准封装常只覆盖常用显示,自己写除基取余可灵活加二进制并控制字符映射,适配特殊场景。
字符表下标必须和余数严格对应,写死常量数组并单测 0~15 映射,别在循环里动态拼表。