C 语言中的类型转换
我们在前面学习了 C 语言的数据类型,那么变量在参与运算的时候类型是始终如一不变的吗?
带着这个疑问,我们可以先看一个例子:
#include <stdio.h>
#define typename(x) _Generic((x), /* Get the name of a type */ \
\
_Bool: "_Bool", unsigned char: "unsigned char", \
char: "char", signed char: "signed char", \
short int: "short int", unsigned short int: "unsigned short int", \
int: "int", unsigned int: "unsigned int", \
long int: "long int", unsigned long int: "unsigned long int", \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
float: "float", double: "double", \
long double: "long double", char *: "pointer to char", \
void *: "pointer to void", int *: "pointer to int", \
default: "other")
int main()
{
int a=1,b=2;
float c=3.14159,d=0;
printf("a type: %s, b type: %s, c type: %s, d type: %s\n",typename(a),typename(b),typename(c),typename(d));
a=b+c;
printf("a=b+c, a=%d\n",a);
printf("type (b+c): %s\n",typename(b+c));
d=b+c;
printf("d=b+c, d=%f\n",d);
return 0;
}
经过编译运行后得到如下的结果:
Tips:有关如何编译运行的内容请参考前面的章节。
Tips:同时值得注意的是,这个程序中的 _Generic 来自于 C11 标准中,所以在你之前可能看到的 C 语言的书籍中是没有的。请大家不要使用过于古老的编译器,请使用推荐的较新的编译器。也就是支持 C11 以上标准的编译器。具体情况可以参照你们所选择的编译器的手册。或者直接选择 GCC 7 及更新版本的编译器。
a type: int, b type: int, c type: float, d type: float
a=b+c, a=5
type (b+c): float
d=b+c, d=5.141590
这里面的 a, b 变量为整形,而 c 和 d 都是浮点型。那么当一个整形和一个浮点型相加的时候会发生什么呢?这就是我们今天要介绍的内容。
1. 隐式类型转换
C 语言是强类型语言,也就是说不同类型的数据之间是不能进行运算的。必须保持一致的类型才能进行运算。也就是说在这个不同数据类型的计算过程中,C 语言自动进行了一次类型转换,使得两个变量的数据类型一致,才能进行相关的计算。这种自动的转换,也称之为隐式类型转换。
从前面提及的例子还可以看出,我们定义的数据类型,在不同的类型的数据运算结束后,并没有发生改变,也就是数据类型保持着我们最开始定义时候的类型。这时会发生丢弃精度的事情,也就是上面例子中小数点后面的数值就会消失。
那么这种隐式的转换有什么规律可循吗?
下面的表格就展示了类型转换的规律,当在计算过程中,数值类型不一致的时候,就会发生自动的类型转换,转换的类型是将表格中处于下方的较低优先级的数据类型,向表格上方的较高优先级的数据类型进行转换。
级别 | 数据类型 |
---|---|
1 | long double |
2 | double |
3 | float |
4 | unsigned long long |
5 | long long |
6 | unsigned long |
7 | long |
8 | unsigned int |
9 | int |
10 | char short int |
根据这个表格我们就可以看出之前的转换中,int 与 float 类型进行计算,编译器会自动将 int 类型转换为 float 类型进行计算。从而使得运算在相同的数据类型间进行。
2. 显式类型转换
如果说隐式类型转换是编译器自动进行的类型转换,那么显式类型转换,则是我们人为的进行数据类型的转换,这里可以理解为是一种强制的类型的转换,这种转换将不再遵守上面的转换规则,而是按照我们人为的标明的类型进行转换。
就是在我们需要指定类型的变量前加上数据类型,并用圆括号包裹。例如: (int)a, (float)b, (long)c 等。
下面我们通过一个示例程序来看一下显式类型转换:
#include <stdio.h>
#define typename(x) _Generic((x), /* Get the name of a type */ \
\
_Bool: "_Bool", unsigned char: "unsigned char", \
char: "char", signed char: "signed char", \
short int: "short int", unsigned short int: "unsigned short int", \
int: "int", unsigned int: "unsigned int", \
long int: "long int", unsigned long int: "unsigned long int", \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
float: "float", double: "double", \
long double: "long double", char *: "pointer to char", \
void *: "pointer to void", int *: "pointer to int", \
default: "other")
int main()
{
int a=1,b=2;
float c=3.14159,d=0;
printf("a type: %s, b type: %s, c type: %s, d type: %s\n",typename(a),typename(b),typename(c),typename(d));
a=(float)b+(float)c;
printf("a=b+c, a=%d\n",a);
printf("type (b+c): %s\n",typename((int)b+(int)c));
d=(int)b+(int)c;
printf("d=b+c, d=%f\n",d);
return 0;
}#include <stdio.h>
#define typename(x) _Generic((x), /* Get the name of a type */ \
\
_Bool: "_Bool", unsigned char: "unsigned char", \
char: "char", signed char: "signed char", \
short int: "short int", unsigned short int: "unsigned short int", \
int: "int", unsigned int: "unsigned int", \
long int: "long int", unsigned long int: "unsigned long int", \
long long int: "long long int", unsigned long long int: "unsigned long long int", \
float: "float", double: "double", \
long double: "long double", char *: "pointer to char", \
void *: "pointer to void", int *: "pointer to int", \
default: "other")
int main()
{
int a=1,b=2;
float c=3.14159,d=0;
printf("a type: %s, b type: %s, c type: %s, d type: %s\n",typename(a),typename(b),typename(c),typename(d));
a=(float)b+(float)c;
printf("a=b+c, a=%d\n",a);
printf("type (b+c): %s\n",typename((int)b+(int)c));
d=(int)b+(int)c;
printf("d=b+c, d=%f\n",d);
return 0;
}
下面是执行结果。
a type: int, b type: int, c type: float, d type: float
a=b+c, a=5
type (b+c): int
d=b+c, d=5.000000
通过显式类型转换,我们可以控制在计算过程中的数据类型。之前自动转换为 float 类型的数据,在我们显式指定为 int 类型后,计算过程中就会按照 int 类型来进行计算。
3. 小结
对于隐式类型转换。其实变化的原因主要是因为计算的数值为了匹配计算精度而进行的,一般情况下都是较低精度的变量类型会转变为较高精度的变量类型。
而显式类型转换则是我们主动控制了类型的精度,可以抛去我们不需要的高的精度。同时,由于指定了类型,在转换过程中不会产生歧义。
Tips: 请注意,这里在类型转换的过程中不会自动进行四舍五入等操作,因此如果使用不当会造成数据的精度丢失。