前言:
在前面的篇章中,对于强制类型转换和隐式类型转换有了一个基本的了解,详见C语言学习笔记—操作符详解,接下来写一篇文章通透的理解和区分。
类型转换从根本上说是类型铸造,或者说是从一种类型转换为另外一种类型的过程。通俗的说,数据间类型转换是常用的编程和运算技巧。
一般有以下常见情况:
(1)、运算需要同类型的数据时:在进行数学运算时,如果两个操作数的数据类型不同,则必须先将其中一个数据类型转换为另一个的数据类型。
(2)、向函数传递参数时:在调用函数,如果函数的参数类型与传递的数据类型不同,则需要进行数据类型转换。
(3)、从外部输入数据时:在读取外部数据时,如从文件中读取的字符串数据,需要将其转换为合适的数据类型才能在程序中使用。
(4)、存储和传输数据时:在存储或传输时,数据类型可能需要转换以符合特定的存储或传输要求。
主要分为两种形式:
(1)、显示类型转换,或称为强制类型转换:
也就是利用强制类型转换操作符对数据进行强制转换,其中对数据类型的范围不做约束 (大->小 或 小->大 均可,由程序员需求决定),故为强制类型转换。
但是值得注意的是,当由大范围数据向小范围数据强转时,可能由产生数据丢失,因为转换过程中,小范围的数据类型,不一定能够存放大范围数据类型的数据,所以会数据截断才能存放,从而丢失数据精度。
(2)、隐式类型转换:
是一种运算规则,通常是编译器以安全形式由编译器自身处理的类型转换,不会导致丢失数据;它在不同的情景下有不同的表述,一般的隐式类型转换只能将小类型转换成较大类型的数据类型,具体还要根据问题另行讨论。
利用 ( ) 强制类型转换操作符,对一个数据类型进行强制转换为另一个数据类型,对数据类型的范围不做约束。但大范围向小范围可能导致数据丢失。
小范围数据类型向大范围数据类型转换情况:
#include
int main()
{
float a = 10.5;
printf("%f\n", a);//10.500000
int b = (int)a;//强制类型转换
printf("%d\n",b);//10
return 0;
}
解释说明:
根据以上代码,首先定义一个浮点型的变量a,接着正常打印出来为10.500000;
然后,把a强制类型转换后赋值给一个整型变量b,最后打印b,对比两次打印就会发现,当初a变量的数据,最后以整型的数据形式打印出来,那么这个过程就是显示类型转换,即强制类型转换。不难看出,这里是通过强制类型转换操作符直接将小数电后面的数据丢掉。然后,再来看看大范围向小范围强转的情况:
大范围数据类型向小范围数据类型转换情况:
#include
int main()
{
int a = 300;
printf("%d\n", a);//300
//0000 0000 0000 0000 0000 0001 0010 1100 ---- 300 原码、反码、补码相同
char b = (char)a;//强制类型转换
//char : -128 ~ 127 范围,强制类型转换,产生数据截断
//0010 1100 ---- 44 符号位0正数,原码、反码、补码相同
//以%d格式打印,是以原码打印,仍然为:
//0010 1100 ---- 44 原码、反码、补码相同
printf("%d\n", b);//44
return 0;
}
解释说明:
首先,定义一个整型变量a,然后正常打印为:300;然后,将a强制类型转换转换为char类型后赋值给变量b,最后打印出来为:44;其中,300二进制序列为:0000 0000 0000 0000 0000 0001 0010 1100 – 正数原码、反码、补码相同,
但是char的范围只有-128~127,所以小范围存放不下时,就会产生数据截断为:0010 1100 — 44,导致数据丢失,使得打印结果为44。
补充说明:
注意赋值运算符:+= ,-=,*=,/=,%=中默认自带了强制类型转换的特性。
如:
short a = 1;
sum += a;
则等价于:
short sum = (short)(sum +a);
隐式类型转换,又称为默认类型转换,是从存储范围较小的数据类型向较大范围的数据类型转换。
隐式类型转换主要分为两类:
(1)、整型提升
(2)、算术转换
主要转换规则如下:
1、字符必须先转换为整数(C语言规定字符类型数据和整型数据之间可以通用) 。
2、short型转换为int型(同属于整型) 。
3、float型数据在运算时一律转换为双精度(double)型,以提高运算精度(同属于实型)
4、算术运算中不同类型之间的运算也遵循隐式转换规则和整型提升规则。
补充说明:
byte、short、char三者(同属于整型)之间一般不作转换,一旦参与运算则默认转换为int类型后再进行运算。
概念:
C的整型算数运算总是至少以“缺省”整型类型的精度来进行的。为了获取精度,表达式中的字符和短整型在使用前被转换为普通类型,这种转换就是整型提升。
整型提升的意义:
表达式的整型运算要在CPU的相应运算器内执行,CPU内整型运算器(ALU)的操作数的字节长度,一般就是int的字节长度,同时也是CPU的通用寄存器长度。
因此,即使两个char类型的相加,在CPU执行时实际上也要转换为CPU内整型操作数的标准长度。 通用CPU是难以直接实现两个8比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
即:在表达式计算时,小于int长度的各种整型首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型,然后再执行表达式的运算。
如何整型提升?
是按照变量的数据类型的 符号位 来提升的
#include
int main()
{
char a = 3;
//0000 0000 0000 0000 0000 0000 0000 0011---3
//char:0000 0011---a 数据截断
char b = 127;
//0000 0000 0000 0000 0000 0000 0111 1111---127
//char:0111 1111---b 数据截断
char c = a + b;
//整型提升:
//0000 0000 0000 0000 0000 0000 0000 0011---3---a最高位是0,那么表示正数,然后高位全补0
//0000 0000 0000 0000 0000 0000 0111 1111---127-b最高位是0,那么表示正数,然后高位全补0
//0000 0000 0000 0000 0000 0000 1000 0010
//char截断:1000 0010--c
//分析:c最高位为1,表示负数,提升后补1
//发现a和b均是char类型,都没有达到一个int的大小
//这里就会发生整型提升
//char截断:1000 0010--c(-126)
//1111 1111 1111 1111 1111 1111 1000 0010--补码
//1111 1111 1111 1111 1111 1111 1000 0001--反码
//1000 0000 0000 0000 0000 0000 0111 1110--原码,以%d格式打印
printf("%d\n",c);//-126
return 0;
}
#include
int main()
{
char a = 0xb6;
short b = 0xb600;
int c = 0xb6000000;
//a和b,char和short会整型提升,所以提升后会改变
//c本身就是int型,所以不变
if (a == 0xb6)
{
printf("a\n");
}
if (b == 0xb600)
{
printf("b\n");
}
if (c == 0xb6000000)
{
printf("c\n");//c
}
return 0;
}
证明:存在隐式类型整型提升
#include
int main()
{
char c = 1;
printf("%u\n",sizeof(c));//1 --- char
printf("%u\n",sizeof(+c));//4 --- unsigned int
printf("%u\n",sizeof(-c));//4 --- unsigned int
printf("%u\n",sizeof(!c));//4 gcc编译为-4
return 0;
}
如果某个操作符的各个操作符属于不同的类型,那么除非其中一个操作数的转换为另外一个操作数的类型,否则操作就无法进行。
下面的层次体系称为寻常算术转换:
long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个操作数的类型后执行运算。
注意:
(1)、算术转换要合理,不然会有潜在问题报错。
(2)、同类型条件下,大于等于int类型时,就属于算术转换了,小的向大的转换
(3)、不同类型之间如float浮点数类型与int整形运算,字节相同均为4,则int向上精度更高的隐式算术类型转换为float,再进行算术运算。
(4)、算术转换仅限于数据类型的大小 ,大于等于int的类型才使用,比它小的会进行整型提升。
#include
int main()
{
int a = 4;
unsigned int b = 2;
float c = 2.5;
double result;
result = a / b * c;
printf("Result: %lf\n", result);
return 0;
}
#include
int main()
{
int a = 4;
float f = 4.5f;//f表示为float类型的常量,没有f默认为double类型
//如果a + f;
//则相同字节的条件下,int向上转换为float,向精度更高的转换,再相加。
return 0;
}
a、强制类型转换:就是显示类型转换,数据类型的强转范围不受约束,但是语法上,大的强转成小的有可能导致精度丢失或者溢出的问题。
b、隐式类型转换:分为整型提升和算术转换。小的向大的转换。
(1)、整型提升:是相同类型的转换,在表达式计算时,小于int长度的各种整型(如short、char)首先要提升为int类型,如果int类型不足以表示则要提升为unsigned int类型,然后再执行表达式的运算。
(2)、算术转换:大于等于int类型时,就属于算术转换了,是不同类型之间的转换,在层次体系的数据类型里,向字节更长的类型转换,字节相同向精度更高的方向转换。
所以想要程序保证数据的安全和运算精度处理,必须对涉及的类型转换和编译器实现转换的过程有一定的掌握,从而避免一些数据的严重失误。