- 2.6.1.隐式类型转换
- 2.6.2.算术类型转换
- 2.6.3.显式类型转换
2.6.2.算术类型转换
在算术计算和比较表达式中,经常用不同类型的值作为操作数。为正确处理这些值,必须让这些类型达到某种“共同标准”。除非程序员指定了显式转换规则,否则编译器会尝试在没有程序员干预的情况下完成此操作(参见 显式类型转换)。在这种情况下,编译器会尽可能保持数字的最大精度。特别是,它产生了整数的表示范围扩大以及从整数到实数的转换(如涉及的话)。
整数扩展意味着从 bool、char、unsigned char、short、unsigned short 隐式转换为 int(或转换为 unsigned int,如果 int 不足以存储特定数字)。大值可以转换为 long 和 unsigned long。
如果变量的类型无法存储计算表达式所获得类型的结果,编译器将发出警告:
double d = 1.0;
|
初始化 x 和 y 变量的表达式包含实数 1.0,因此其他操作数(本例中为常数 10)被转换为 double,除法的结果也将是 double 类型。然而变量的类型是 int,因此它会被隐式转换。
计算 1.0/10 是由编译器在编译期间完成的,因此它得到一个 double 类型的常量 (0.1)。当然,在实践中,初始化常量不太可能超过接收变量的大小。因此,编译器警告“常量值截断”可以被认为是异常的。这只是复杂问题的简单化表示。
但是,基于变量的计算也会导致类似的数据丢失。我们在这里看到的第二个编译器警告(“类型转换可能会导致数据丢失”)出现得更为频繁。此外,不仅在从实数类型转换到整数类型时可能会丢失数据,反向转换时也可能会丢失数据。
double f = LONG_MAX; // truncation of constant value
|
我们知道,double 类型不能准确表示大整数(虽然它的有效值范围比 long 大得多)。
由于类型不匹配,我们可能遇到另一个警告:“整数常量溢出”。
long m1 = 1000000000;
|
MQL5 中的整数常量为 int 型,因此在执行百万乘百万的运算时会考虑该类型的范围,这个范围是 INT_MAX (2147483647)。值 1000000000000000000 导致溢出,m3 为该值除以范围后的得出的余数(更多相关信息见下方侧栏)。
接收变量 m3 为 long 类型,但一事实并不意味着表达式中的值必须事先转换成该类型。转换只在赋值的时候进行。为了根据长整型的规则执行乘法,您需要以某种方式在表达式中直接指定 long 类型。这可以通过显式转换或使用变量来完成。特别是,使用 long 类型的变量 m1 获得相同的乘积(如 m1 * m1)会得到 m2 的正确结果。
有符号和无符号整数
程序编写并不总是完美无缺,也无法防止所有可能的失败情况。因此,有时计算过程中获得的整数不适用于所选整数类型的变量。那么取这个值除以可写入相应字节数(类型大小)的最大值 (M) 的余数,再加上 1。因此,对于大小为 1 到 4 字节的整数类型,M + 1 分别为 256、65536、4294967296 和 18446744073709551616。
但是对于有符号类型有一个细微的差别。我们知道,有符号数的值范围在正负区域之间大致均等地分布。因此,新的“余数”值可能在 50% 的情况下超过正数或负数限制。在这种情况下,数字变为了相反数:数字的符号改变,最终与原数相差 M。
关键要理解,这种转换本质上由于对内部表示中的位状态的不同解释而发生的,并且状态本身对于有符号和无符号数是相同的。
我们来用一个最小整数类型的例子来说明:char 和 uchar。
由于unsigned char可以存储从 0 到 255 的值,256 映射到 0,-1 映射到 255,300 映射到 44,以此类推。如果我们尝试将 300 写入一个常规的有符号 char,也会得到 44,因为 44 在 0 到 127 的范围内(char 的正值范围)。但是,如果将 char 和 uchar 变量设置为 3000,结果会有所不同。3000 除以 256 的余数是 184。这个余数在 uchar 的情况下保持不变。但如果是 char,相同的位组合会得到结果 -72。很容易发现 184 和 -72 相差 256。
在下面的例子中,多亏了编译器警告,我们轻松地发现了问题。
char c = 3000; // truncation of constant value
|
但如果在计算过程中得到一个特别大的数字,则不会发出警告。
char c55 = 55;
|
当在同一个表达式中使用相同大小的有符号和无符号整数时,也会产生类似的效果,因为有符号操作数被转换为无符号操作数。例如:
uint u = 11;
|
两个负整数相加,我们得到预期的结果。第二个表达式将 -38 的和映射到“相反的”无符号数 4294967258。
由于这些潜在的问题,不建议在同一个表达式中混合使用有符号和无符号类型。
除此之外,如果我们从一个无符号整数中减去某个数,则需要确保结果不会是负数。否则,它将被转换为正数,并且会曲解算法的意思,特别是 while 循环的意思, 该循环检查变量的“大于等于零”条件:由于无符号数始终是非负的,我们很容易得到一个无限循环,即程序挂起。