张银峰的编程课堂

数据类型转换

自动类型转换

当一个表达式含有几个不同类型的操作数时,就需要应用一些规则将操作数转换为同一类型进行求值。

int    a = 3;
double b = 4;
a + b;          // int + double
  • 在表达式 a+b 的求值过程中,会先将a转换为double类型再与b求和。
  • 在 b=4; 赋值语句中,也存在int到double的转换。

这种将窄类型转型为宽类型且不会丢失信息的变换,通常程序可以自动完成,称为自动类型转换或隐式类型转换,这里宽窄是指用sizeof计算的数据类型字节数。类型转换也暗示了类型拥有等级的概念,浮点类型高于整型类型。下图展示了各类型之间的等级转换关系:同等类型从左向右转,不同层次由低向高转。

(低)          (高)
------------------------ ↑
float  --->  double      |  浮点类型   (高)
                         |
char   --->  int         |  整型类型   (低)
------------------------->

目前我们对浮点数的表示使用的是double类型,称为双精度浮点类型。此外C语言还提供了float这种单精度浮点类型,它所能存储的数值精度比double小。

float pi = 3.14f;
printf("pi: %f\n", pi)

默认的浮点字面量是double类型,在后面追加'f'标记,将指示字面量为float类型。通常sizeof(float)与 sizeof(int)都是4个字节。但是由于float是比int更高等级的类型, 故而在 int op float 中,int会自动转型为float,表达式结果将是float类型。最后,表达式中的类型转换是逐项进行的,而不是一次转型到结果目标类型。在下面的示例中,我们以t表示单步表达式计算的中间结果,箭头后面是该项表达式的结果类型,!号表示这里发生了自动类型转换。

int a = 3;
int b = 4;
float s = 1.5f;
double r = 0.7;
a / b * s + r;      // #1: t = a / b    int    / int    --> int
                    // #2: t = t * s   !float  * float  --> float
                    // #3: t = t + r   !double + double --> double

ZYF

强制类型转换

有时我们也需要将较宽的数据类型转换为较窄的数据类型,或在两种不同的数据类型之间进行转换。这时可以使用显示类型转换,也称为强制类型转换,语法表示为:(目标类型)表达式

#include <stdio.h>

int main()
{
    float a = 45.7f;
    float b = 70.5f;

    int c = (int)a + (int)b;
    int d = (int)(a + b);

    printf("c = %d\n", c);
    printf("d = %d\n", d);

    return 0;
}

glimix.com

  • 计算c的过程中,a与b分别被强制转换为int型,转型并不会发生舍入操作,相当于求45+70的值。
  • 计算d的过程中,先计算两个float的和,然后将结果转换为int型。

数值溢出

当从较宽的数据类型转换到较窄的数据类型时,在现代编译器上,这可以自动完成。由于较宽数据类型存储的数值可能会超过较窄数据类型的表达范围,这可能并不是我们想要的结果。下图展示了从int转换到char的过程 ,左侧的图例中,被丢弃的字节全是0,c与a的值都是65,看起来并没有什么影响;右侧a的初始值是6000,转型后只保留最低位一个字节,c的值为112。

+-------------------+                   +-------------------+
| 00 | 00 | 00 | 41 |   int a = 65;     | 00 | 00 | 17 | 70 |   int a = 6000;
+-------------------+                   +-------------------+
--- discard -- | 41 |   char c = a;     --- discard -- | 70 |   char c = a;
               +----+                                  +----+

这些情况下建议使用显示类型转换,突出强调作用:我强制将大类型转换为小类型,有潜在数据丢失风险!

#include <stdio.h>

int main()
{
    int a = 65;

    /*
       这里int自动转换到了char
       由于c的大小足以存储结果值,因此值意义是上正确的
    */
    char c = a;
    printf( "%d, %c\n", c, c );

    /* a的值已经超过c的可存储范围,因此发生想不到的结果 */
    a = 6000;
    c = a;
    printf( "%d, %c\n", c, c );

    return 0;
}

数值a的二进值表达为 0001 0111 0111 0000,字符 c 仅能够容纳其低8位值。在将a赋值给c后,c的值就是 0111 0000,即十进制的112。

glimix.com

ZYF