掌握 MQL5:从入门到精通(第二部分)基本数据类型和变量的使用
概述
在上一篇文章中,我们了解了 MQL5 程序员使用的主要程序(并得出了 MetaEditor IDE 非常适合初学者的结论)。此外,我们还快速了解了函数的概念,并创建了一个在系统日志中打印一条信息的简单脚本。这些信息可在终端窗口底部的 "专家" 选项卡中查看。
让我提醒大家,函数 是对行为的描述。
我们只使用了预定义的函数:OnStart 和 Print。至于第一个函数,我们使用内容来填充它。第二个显示我们所需的信息的函数是已经完成的,我们只需将参数传递给它。程序员可以创建自己所需的自定义函数,以解决特定任务。
每个函数都由非常基本的分步操作组成,这些步骤被称为语句。这些操作非常简单:比较两个数字、多次重复一段代码、将两个文本粘合在一起、调用另一个函数等。这样的操作不多。我们将在本文中讨论其中的一些语句。
一连串的语句构成了一种算法。
算法由清晰易懂的指令组成,执行者(在本例中为计算机)根据指令执行某些操作,从而解决特定的、更全面的任务。由于同样的问题通常可以用多种不同的方法解决,因此算法的数量非常庞大。
有关交易的时候更是如此。进场交易、退出交易、打印相应日志和其他操作可以通过不同的方式实现。我们需要绝对清楚地向计算机解释,在特定情况下,您(或您的客户)到底想要什么。
我们可以说,更复杂的算法是由更简单的算法组成的,而每种算法都是由某些函数实现的,执行某些操作。这些操作是应用于数据的。这些数据可以是每秒多次到达的买卖报价或交易量。其他数据示例还包括屏幕上的点,您可以在这些点之间绘制直线或曲线。也可以是在执行交易时播放的声音。数据可以是文本,例如某个时间段的报价列表。可以有很多不同的例子。我希望这个想法是清楚的。
现在,我们要讨论的是,这些数据必须存储在某个地方。
今天,我们将讨论如何在 RAM 中存储数据。数据可以作为变量或常量存储在内存中。
它们的差别是显而易见的:
- 变量可以变化,即程序有权改写这些数据。
- 常量在整个程序生命周期内保持恒定(不变),如果程序员试图覆盖其值,将返回编译错误。
除此之外,它们的含义绝对相似:这是 RAM 中存储数据而非处理器指令的特定区域。通常,人们会为这些内存区域提供有意义的名称,以便知道它们的用途。
编译器会移除这些名称,但如果您可以访问源代码(文本文件),您总是可以根据变量名理解变量的用途。当然,前提是描述正确。
常量在某些情况下可能没有名称。程序员只需写入需要处理的内容(例如,我们传递给 Print 函数的字符串)。这种没有名称的常量被称为字面常量。
在本文中,我们将仔细研究基本数据类型、描述变量和常量的方法,以及程序员用来创建算法的基本语句。这反过来又能让你创建更多有用的程序,而不仅仅是 "Hello, World"。
测试文章中所有表达式的基本代码
在上一篇文章中,我们创建了一个简单的程序:在终端底部面板的"专家"选项卡中打印数据的脚本。就是这样:
//+------------------------------------------------------------------+ //| HelloWorld.mq5 | //| Oleg Fedorov (aka certain) | //| mailto:coder.fedorov@gmail.com | //+------------------------------------------------------------------+ #property copyright "Oleg Fedorov (aka certain)" #property link "mailto:coder.fedorov@gmail.com" #property version "1.00" //#property script_show_inputs //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- Print("Hello, MQL5 world!"); } //+------------------------------------------------------------------+
例 1. 最简单脚本的全文
今天,我们将使用这段代码:我们将在大括号内插入示例中的代码行来修改这段代码(除非示例说明中明确指出了插入的其他位置)。
字面符号
让我们看看如何打印数据。
我们将从字符串开始。
字符串文字用双引号<"> 括起来。然而,并非所有字符都能以直观的方式显示。其中有些字符在语言中具有特殊含义(相同的引号),有些则完全不显示,例如换行符。
此类字符有一个特殊的约定:使用反斜杠书写:
Print( "Symbol <\"> can't be placed" " without (\\).\n And we can carrying over the string now." );
例 2.使用反斜杠打印特殊字符
在例 2 中,描述输出字符串的引号用绿色突出显示,特殊字符用黄色突出显示。因此,"\n"表示换行。
图1. 字符串字面输出示例
还请注意以下功能。在 MQL5 中,超长字符串可以在任意位置分割(通过将每个部分放在引号中)。无需添加任何其他操作来合并这些行。如果编译器遇到两个连续的字符串字面量(如示例中),它会自动将这两个字符串合并为一个大字符串,然后搜索其中的特殊字符。
完整的特殊字符表以及更详细的字符常量说明,请参见官方帮助。
第二种最常用的类型是数字。
整数有统一的形式:
Print(5);
例 3.整数输出
小数也很容易输出。整数和小数部分之间的分隔符是点:
Print(5.5);
例4. 小数输出
对于非常大或非常小的数字,可以使用浮点符号(有时称为指数形式):
Print(5.5e3); Print(5.5e-3);
例 5. 使用浮点字面量
使用所有这些数字形式的脚本结果如图 2 所示。
图 2. Print 函数打印数字的结果
请注意在最后两行中函数是如何转换传递给它的数据的。这表明数据被识别为数字,并得到了正确处理。
有时,程序员需要处理十六进制数字。初学者很少需要这种类型,但有时它也很有用,例如在描述颜色时。十六进制数由数字(0...9)和/或字母(a...f)组成。
为了让程序理解这是十六进制文字,需要在数字开头添加 "0x"(0 和字母x)。
该表示法不区分大小写。
Print(0xAF35);
例6. 使用十六进制数
图 3. 十六进制数字输出结果
脚本结果如图 3 所示。该数字已被转换(为普通整数),这意味着计算机完全按照需要来理解它。
MQL5 程序员经常需要使用日期。
如果要完整地标注日期和时间,请以大写字母"D"开头,然后加上单引号< ' >,必要时用圆点或斜线写出所需的日期,在空格后标注时间,用冒号分隔分钟和秒,再加上另一个单引号:
Print(D'24/12/23'); Print(D'24.12'); Print(D'24.12.2023 7:55:34');
例 7.使用日期和时间
请注意,如果有第二行,我们会收到编译器警告,提示日期不完整。不过,文件将被编译,所有三行都将正常工作:
图 4. 关于日期字面不完整的警告(MetaEditor)
图 5. 日期输出脚本的结果(终端)
请注意,在编译第一行时,替换的是一天的开始时间,而在编译第二行时,替换的是编译的时间。既然格式转换正确,我们可以断定程序理解正确。
在任何字面量上,都可以执行给定数据类型允许的任何操作。
例如,您可以比较数字、执行算术运算并将数字作为参数传递给函数。
字符串可以添加(粘在一起),但不能相减。
让我们看看下面的例子:
Print( "This will be calculated: "+4+9 ); Print( "This will be calculated: "+(4+9) );
例 8.在书写表达式时使用括号。
图 6. 编译器警告在字符串表达式中使用数字
图 7. 函数输出计算结果
在没有括号的地方,数字只是"粘"在文本上。当我们使用括号时,一切都计算正确。这是一个相当难以捉摸的错误,因此编译器会立即发出警告。有一些特定函数可以明确地将数字转换为字符串。但现在请记住:括号很重要。
使用 #define 预处理器指令定义常量
如果不想在自己的代码中混淆,又想理解程序中使用数据的目的,就应该尽量为所有常量设置有意义的名称。为此,通常使用#define预处理器指令:#define name value
例 9.#define 指令
让我提醒您,预处理器语言就像是 "语言中的语言",这种语言描述的是编译开始之前的操作。
通常,预处理器的目的是用其他代码片段替换某些代码片段。例如,第一次传递时的#define指令会指示编译器在整个代码中将"name" 替换为"value",然后才开始语法检查。
例如,我们可以这样写:
#define MY_SUPER_CONSTANT 354 Print(MY_SUPER_CONSTANT);
例 10.#define 指令
图 8. 程序会输出常量的值,而不是名称
执行时,程序显示的正是数字 354,而不是名称。
注意,描述数字的文字后面没有分号。
使用预处理器指令声明常量时,不需要分号。
如果我们添加了分号,预处理器就会在 Print 括号内插入分号和数字,我们就会收到编译错误信息。
因此,请记住,我们命名一个常量,并在表达式中使用它的名称,而不是值。
顺便说一句,当同一个常量在程序的不同地方被多次使用,或者几个常量的值相同时, 名称就非常有用了。如果常量的值发生变化,只需在一个地方(通常是在文档的开头,甚至是在一个单独的文件中)进行更改即可,而不必在多个地方进行搜索和更改。
变量的描述
请记住:如果预计内存中的数据会在工作过程中发生变化,请使用变量。
对变量的描述也非常简单,只需写下您想要存储的内容即可:
type variable_name;
例 11.变量声明模板
这条内容意味着编译器必须为数据分配一定量的内存。稍后我们将详细介绍类型和大小。
现在可以通过名称(variable_name) 访问这些数据。
标识符约定
变量名(通常称为标识符)以及您创建的任何名称必须
- 要包含信息("chartNumber "比 "sss "更好)、
- 由拉丁字母、数字和下划线 (_) 组成。
名称不能
- 以数字开头、
- 与保留字冲突、
- 长于 63 个字符。
名称区分大小写。例如,myVariable 和 MyVariable 是两个不同的名称。(显然,强烈建议不要在同一个文件中同时使用这两个名称)。
如果不小心在程序文本的某个地方混淆了大小写,编译器就会给出错误信息:"Undeclared variable"(未声明的变量),这样你就可以很容易地修复它。但如果按照所有规则来描述这两个变量,但使用了只是大小写不同的相似名称,那就太容易混淆了。
其它方面,没有任何限制。如果你愿意,甚至可以用某个内置函数来命名你的变量(希望不要)。
赋值操作符
要将数据写入变量,请使用赋值操作。有时在声明时进行赋值,这种情况被视为初始化:
// Initialization (at creation) int counter = 0; // Normal assignment counter = 10;
例 12.简单赋值
int表示变量只能包含整数类型的数据。
本例中的符号"="表示赋值运算符。在这种情况下,一个整数被写入名为 "counter"的单元格。
赋值前单元格中的所有数据都会丢失。
该变量中的数据可以在程序中的任何地方按名称使用,例如,可以将其作为参数传递给任何函数或在表达式中使用(本文稍后将举例说明)。
赋值操作 - 功能特点
赋值可以非常简单,如上一节所述。但如果您在表达式中使用此操作,下面的说明将帮助您更有效地使用它。
- 赋值操作的优先级最低,因此从右向左执行。也就是说,右侧是一个表达式,左侧是一个写入数据的变量。首先,表达式为:
a = b + c;
例 13.赋值的优先级最低。
上面的表达式会先将 b 和 c 相加,然后将表达式的结果写入变量 a。
- 根据前面的分析,可以在表达式中使用变量的值,然后将结果写入同一变量:
a = a — c;
例 14.您可以在表达式中使用变量的前值
- 在 MQL5 中,如果同一变量在左右两侧各出现一次(如上例),则可以通过将表达式符号移至操作的左侧来简化表达式:
a -= c;
例 15.赋值的简短用法
这种简短的用法适用于任何二元(使用两个值,如和或乘)运算符:乘法、除法、移位等。不过要注意的是,这个表达式中的变量应该很容易分离出来。
例如,对于表达式 a = a*(1+1/a),如果不打开括号,这一招就不再管用,但对于 a = a*(b+c),就很容易了:a *= b+c。
- 如果需要将整数增大或减小 1,则根本不需要使用赋值。可以使用递增和递减操作来代替:
a++; // Increment. Increase a by 1 b--; // Decrement. Decreases b by 1
例 16.递增和递减
这些操作都是一元操作,也就是说,它们只需要一个变量就能进行操作。此类运算有两种符号形式:前缀和后缀。
以前缀形式使用时,将首先执行操作,然后在表达式中使用结果。
以后缀形式使用时,将首先使用变量的旧值,然后变量值将变化 1:
int a = 1; Print (++a); // 2, and a == 2 Print (a++); // 2, but a == 3
例 17.递增的前缀和后缀形式(递减的用法完全相同)
- 通过 "级联",您可以连续使用多个赋值运算符。从右到左的操作顺序仍旧保留。
int a=1, c=3; a = c = a+c; // first a+c (4), then c = 4, then a = c (i.e. a = 4)
例 18."级联"赋值
基本数据类型
数据类型相对较多。
其中有些是 "简单"(或 "基本")的。这些类型包括字符串、数字、日期、颜色等。其他类型则是"复杂"类型;MQL5 程序开发人员会创建此类类型。通常情况下,"复杂"数据类型是简单数据类型的组合,即为了更方便而将多个变量合并到一个数据块中。
本文只介绍基本类型。我们将在下一部分讨论复杂类型的问题。
整数类型
整数是计算机"思考"的主要方式。
计算机上的整数运算既简单又快速。但如果操作结果超出了一定的数值范围,就可能导致数据丢失。
第二个要点:整数可以是"signed"(有符号)或 "unsigned"(无符号)的。
如果数字是 "unsigned",则可以使用从 0 到最大值的数字。例如,占用 1 个字节的数字可以从 0 增加到 28-1 = 255,总共 256 个值。
如果有人试图将数字 "256" 或 "-1" 写入这样的变量,就会出现 "溢出"。在这种情况下,结果将限制在相同的范围内 [0.255],而其余数据将丢失 。有时这样做可能有用,但在绝大多数情况下,最好使用其他方法进行此类转换。例如,可以使用余数运算符(本文稍后讨论)。最好使用能接受全部数据而不会丢失的变量类型。
以 0 开头的类型(实际上是描述自然数的类型)的名称前面都有字母 "u"(用于unsigned)。
"signed"数使用相同的数值范围,但被分成两半。前半部分存储负数,后半部分存储正数。也就是说,同样的单字节数字在[-128...127] 范围内是正确的。
表格1 - 整数数据类型
名称 | 大小字节数 | 最小值 | 最大值 |
---|---|---|---|
char | 1(8 位) | -128 | 127 |
uchar | 1(8 位) | 0 | 255 |
short | 2(16 位) | -32768 | 32767 |
ushort | 2(16 位) | 0 | 65535 |
int | 4(32 位) | -2147483648 | 2147483647 |
uint | 4(32 位) | 0 | 4294967295 |
long | 8(64 位) | -9223372036854775808 | 9223372036854775807 |
ulong | 8(64 位) | 0 | 18446744073709551615 |
在实践中,最常用的类型是 int(因为这种字写起来很快,而且这种类型的数据相当大)和 long(其大小足以满足绝大多数任务的需要,编译器可以优化字节码,使其在现代计算机上达到最佳性能)。
不过,其他类型也很有用。
布尔型
该类型由bool关键字指定,占用1 个字节的内存,只能取两个值:true 或 false。
如有必要,任何数字都可以用作逻辑数据。例如,如果数字等于 0,那么它就是 "false";在所有其他情况下,它就是 "true"。不过,为这种目的而使用数字时要非常小心。
实数(又称浮点数)
表 2.实数类型
名称 | 大小字节数 | 最小正值 | 最大值 |
---|---|---|---|
float | 4(32 位) | 1.175494351e-38 | 3.402823466e+38 |
double | 8(64 位) | 2.2250738585072014e-308 | 1.7976931348623158e+308 |
现在的实际应用主要是"double"类型了,我已经很久没有在别人的代码中看到"float"类型了。这可能是为了与旧版本兼容。不过,在数据集非常大的情况下,它可以有效地节省内存空间。
实数可以表示价格、货币数量和其他有用的概念。
它们的取值范围比整数大得多。
然而,计算机使用这些类型并不十分方便。首先,实数运算比整数运算稍慢。其次,由于格式的特殊性,计算结果的最后一位数几乎总是有误差。因此,在某些操作中,您可能得到 1.00000001,而不是 1.0;在另一些操作中,您可能得到 0.99999999。
因此,如果需要比较两个实数,通常应取它们的差值,然后与某个小值进行比较,当然这个小值肯定要大于误差。这是一种更可靠的方法。
日期和时间
该类型用单词datetime 表示,在内存中占8 个字节。
该类型的每个变量都包含从1970 年 1 月 1 日到所需日期的秒数。因此,它是一个正整数。
最后的有效日期是3000 年 12 月 31 日。在我们有生之年,这已经足够了,所以我们不必担心 "2000 年问题"(如果你还记得的话)。
有一些特殊的预定义常量:
- __DATE__ — 编译日期
- __DATETIME__ — 编译日期和时间
- 您可以使用表达式__DATETIME__-__DATE__ — 它只描述编译时间,不包含日期。
在书写字面量时,可以省略所有内容,将值写成D''(D 和两个单引号)。该符号等同于 __DATETIME__。不过,这样会降低代码的可读性。
颜色
MQL5 中的颜色由单独的类型 color 表示。在描述颜色时,可以使用固定值:
color myColor1=C'100,200,30'; color myColor2=C'0xFF,0x00,0x5A';
例 19.使用十进制或十六进制数字描述颜色
您还可以使用为网页颜色预定义的常量。颜色常量名称以 clr 开头(例如,clrBlue 表示蓝色)。在 MetaEditor 中键入clr或访问官方文档,即可查看常量的完整列表。
在例 12 中,您可以看到每种颜色的描述都由三个片段组成。每个片段描述屏幕上一个光点的红色、绿色或蓝色强度(红、绿、蓝 = RGB)。它们组合在一起,呈现出各种各样的色彩。
通过混合红色和绿色,我们可以得到黄色、褐色和橙色的各种色调。
红色和蓝色产生紫粉色调。
绿色和蓝色呈现出不同的绿松石色、青色等。
将这三种色调等比例混合,就会产生一系列灰色色调:从黑色(所有成分都处于 "关闭 "状态,即强度为 0)到白色(所有成分的最大强度都为 255(0xFF))。
如果比例不相等,即每个成分的强度都与其他成分不同,则会产生显示器上的所有其他色调。绿色通常会使整体颜色变浅,而蓝色会使整体颜色变深。当然,一般来说,成分越亮,颜色就越浅(整体颜色也越浅)。
表 3 举例说明了所有这些规则,我只是用编辑器中可用的颜色给单元格着色。
在实际操作中,通常不需要知道每种颜色的数值,只需从一个特殊的调色板中选择颜色或传递一个预定义的常数即可。不管怎么说,我相信了解万事万物的运作原理是有益的。
颜色数据在内存中占4个字节,但只使用其中3个字节。历史上曾发生过这种情况,对于任何可以使用这种颜色描述模型的程序来说,这是一个普遍的共识。
表 3.颜色示例
0, 0, 0 | 156, 15, 15 | 106, 0, 86 | 0, 49, 110 | 0, 110, 41 | 56, 37, 9 | 56, 37, 9 |
51, 51, 51 | 191, 3, 3 | 133, 2, 108 | 0, 67, 138 | 0, 137, 44 | 243, 195, 0 | 87, 64, 30 |
102, 102, 102 | 226, 8, 0 | 160, 39, 134 | 0, 87, 174 | 55, 164, 44 | 255, 221, 0 | 117, 81, 26 |
153, 153, 153 | 232, 87, 82 | 177, 79, 154 | 44, 114, 199 | 119, 183, 83 | 255, 235, 85 | 143, 107, 50 |
204, 204, 204 | 240, 134, 130 | 193, 115, 176 | 97, 147, 207 | 177, 210, 143 | 255, 242, 153 | 179, 146, 93 |
255, 255, 255 | 249, 204, 202 | 232, 183, 215 | 164, 192, 228 | 216, 232, 194 | 255, 246, 200 | 222, 188, 133 |
如果需要,可以像处理普通整数一样处理颜色。
示例代码如下:
color a = C'255,0,0'; color b = C'0,255,0'; color d = a+b; Print(a," ",b," ",d);
例 20.在算术表达式中使用颜色
结果如下:
图 9. 在算术表达式中使用颜色的结果
枚举
最后一种基本类型是枚举。
在某些情况下,根据问题的具体情况,变量只能取某些值。例如,趋势可以是下降、上升或盘整。此外,只存在以下买入订单类型:买入(以市价买入)、止损买入(等待价格达到一定水平以实现突破)和限价买入(挂单,期待反弹)。一周内的天数总是一样的。我认为原则是明确的。
在这种情况下,我们就可以使用枚举。
描述枚举包括三个步骤。
- 第一步,你需要创建列表本身,并以某种方式为其命名。生成的名称就是变量或函数的类型名称。这个名称与预定义类型的唯一区别是,它是我们自己得出的。
- 第二步,创建一个此类变量。
- 最后,在第三步中,你可以使用这个变量。
//--- First step: creating a new list (new data type) enum ENUM_DIRECTION { Upward, Downward, Aside }; //--- Second step: description (and, if necessary, initialization) of a variable of this type ENUM_DIRECTION next=Upward; //--- Third step: using the variable Print(next);
例 21.枚举说明和用法
通常情况下,枚举列表是在文件的开头创建的,紧接在预处理器指令之后。在这种情况下,我们应用程序的所有函数都可以使用它。
虽然你可以在某个函数内部对其进行局部描述。这样,其他函数就无法看到该枚举。大多数情况下,这并没有什么意义,但你永远不知道会面临什么样的任务。
枚举的元素名称在大括号内指定,中间用逗号隔开。
在任何类型描述(包括枚举)的结尾大括号之后,都必须有一个分号。其他区块的情况可能并非如此。
语言中预定义的枚举名称是以大写字母书写的,这些名称以前缀 ENUM_ 开头。您可以随意(在规定范围内)命名您的枚举,但遵守相同的标准是一种良好的做法。
枚举的内部表示形式是一个带符号 整数,在内存中占 4个字节。
如果我们尝试执行示例 21 中的代码,就会看到数字 0。这意味着,当我们让 MQL5 自行为枚举元素选择数字时,它会从零开始。
但您也可以指定其他数字,只需明确设置即可:
//--- First step: creating a new list (new data type) enum DIRECTION { Upward = 1, Downward = -1, Aside = 0 };
例 22.明确设置枚举值
无需指定所有值。
如果指定了某些值,而某些值没有指定,MQL5 将根据元素的顺序和最后一个最高数字自行选择数字。这意味着,在例 15 中,如果我们设置Upward = 1并删除所有其他初始化,那么 Downward 将等于 2,Aside 将等于 3。你应该自己检查一下。
表达式和简单运算符
当我们处理数据时,能够对数据进行比较、执行数学运算等非常重要。不同的表达式可用于不同的数据类型。
比较运算符
这些运算符对所有数据类型都有意义。
比较结果是逻辑型。
比较运算符如下:
- 大于 ( > )、
- 小于 ( < )、
- 大于或等于 (>= )、
- 小于或等于 ( <= )、
- 等于 ( ==)、
- 不等于 (!= )
所有这些操作的优先级都是一样的。
在比较字符串时,计算机将遵循编码中的字符排列。例如,大写字母 "A "在小写字母 "a "之前,因此会变小。因此:
"An apple" < "a pal" //true
例 23.比较字符串。大写字母小于小写字母
如果一行中有多个相同字符,则选择第一个不相等的字符进行比较。
"An apple" > "A pal" //true
例 24.第一个字母相同
例 24 中的表达式是正确的,因为编码中的空格位于字母字符之前,而且第一个字母是相同的。
如果一个字符串已经结束,而第二个仍在继续,且开头相同,则认为结束的一个较小。示例:
"An apple" < "An apple was found" //true
例 25.不同的字符串长度
算术运算
结果是由表达式中使用的数据类型决定的。
您可以对数字进行算术运算:
- 数字 ( -3) 前面的符号(有时称为 "一元 "减号);
- 乘法 (*)、除法 (/)(整数向下舍入)、除法余数 (%)(仅适用于整数,5%2 == 1);
- 加法 (+),减法 (-);
- 递增 (++), 递减 (--)
列表是按优先顺序排列的。
不过,最好不要将普通表达式中的增量和减量与其他算术运算符混用,因为可能会出现结果未定义的情况。
操作 (+) 也是为字符串定义的,但在这里指的是粘合(把两个短字符串粘成一个长字符串)。
位操作
结果是一个整数。
对于整数,还有以下位操作:
- 位反(~)
- 右移(>>)
- 左移(<<)
- 位"与"(&)
- 位"或"(|)
- 位"异或"(^)
如果您需要它们,那么您肯定不再是初学者了,您可以在语言文档中找到所需的信息。:-)
列表是按优先顺序排列的。
逻辑运算符
结果是逻辑型值。
- 逻辑反 ( ! )
- 逻辑乘法 - 逻辑"与"(&&);
- 逻辑加法 - 逻辑"或"(||)。
列表是按优先顺序排列的。
还有其他操作,将在其他文章中讨论。此外,我们还将在其他文章中详细讨论使用上述所有运算符的示例。目前,您可以使用您所理解的部分或查看语言文档。应该不会有什么特别困难的。
类型转换
有时,一个算术表达式涉及多种数据类型。例如,我们经常使用 Print 函数打印字符串和数字,在本文中我们甚至还遇到了颜色。
结果会是什么类型?在这里,我们有两种选择:要么由我们自己决定最终结果,要么相信编译器。
当然,编译器是很聪明的,它能想出办法来。但并非总是如此。
因此,让我们来看看编译器会做些什么,以及可以"手动 "做些什么,以避免丢失重要数据,在编译过程中不会收到警告,并对编译结果充满信心。
编译器做了哪些事?
首先,如果表达式使用了相同类型的数据,那么结果也将是相同类型的。这是最简单的情况。
如果涉及不同的类型,编译器会尝试将结果扩展为最准确的类型。例如,如果我们尝试将一个 4 字节整数(int)与一个日期(datetime)相加,我们将得到一个日期类型(因为其范围更广)。
整数字面量属于 int 类型,浮点数通常属于 double 类型,除非以小写字母 "f "结尾:
5 + 3.4f + 4.25 // The result is double, since 5 is first converted to float, and then 4.25 sets double precision
例 26.使用固定值时的类型转换
帮助文档包括类型转换优先级方案:
图 10.类型转换优先级
需要注意的是,有符号类型和无符号类型相互转换可能会导致数据丢失,而转换为浮点类型可能会导致精度损失。
因此,如果您不确定编译器将如何转换数据,可以考虑人工指定将哪些数据转换为哪些数据。
如果需要将结果(或特定值)转换为特定类型,可以这样做:
- 将结果写入某个类型的变量。这种方法本质上是自动转换的一种变体,因此必须谨慎使用。
- 使用特殊的数据转换函数;
- 使用类型转换的简单形式。
(int)a+b // converts a to an integer. b remains unchanged double (c+d) // absolutely similar to the previous one. In this case, the result of the summation is converted // and so on - you can use any suitable type
例 27.类型转换的简单形式
不要忘记,括号可以更改操作顺序,因为括号的优先级最高。如果不确定,请使用括号。
结论
我们已经学习了有关基本数据类型、变量和表达式的大量理论知识。如果你理解了这篇文章和下一篇文章的内容,那么你几乎就不再是初学者了,而是会进入一个更高的层次。如果你理解了变量(本文的内容)和函数(将在下一篇文章中介绍)的工作原理,那么你就可以自信地继续学习更复杂的主题,比如 OOP。
OOP(面向对象编程)被认为是一项复杂的内容。其实,思想观念方面的困难多于技术方面的困难。
对于那些不理解某些要点的人,我建议他们慢慢地重新阅读这篇文章(一次或多次),一个概念一个概念地读,检查代码中提到的所有内容。
对于什么都已经明白,又不想等到我准备好下一篇文章的人,我建议他们编写自己的脚本,以显示操作的交易品种和余额的有用信息,从而让等待的时间更充实。其中大部分信息可从 AccountInfo 和 SymbolInfo 函数族中获取。
尝试使用 MetaEditor 查找这些系列中每个函数的全名,然后在帮助中查找它们的描述。有了这些说明和本文所涉及的材料,创建这个脚本就不需要您花费太多精力了。
备注:标准库中有这样一个脚本示例。如果您不想编写自己的脚本,可以尝试分析现成的脚本。如果你能编写自己的程序,请将其与标准程序库中的程序进行比较。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/13749