C语言类型转换
本文第一部分,“寻常算术转换”是《C专家编程》1.10的阅读笔记;第二部分“强制类型转换”是个人经验。
一、寻常算术转换
1、K&R标准(第6.6节:算术转换)
- 任何类型为char或为short的操作数都被转化为int,任何类型为float的操作数都被转换为double;
- 如果其中一个操作数的类型是double,那么另一个操作数和计算结果类型都是double;
- 如果其中一个操作数的类型是long,那么另一个操作数和计算结果类型都是long;
- 如果其中一个操作数的类型是unsigned,那么另一个操作数和计算结果类型都是unsigned;
2、ANSI C标准
ANSI C标准重新编写了有关内容,填补了其中的漏洞
- 字符和整型(整型升级):在需要使用int或者unsigned int的表达式中,char,short int,int型位段,包括有符号或无符号类型,以及枚举类型,会发生整型升级。如果int可以完整表示原类型的所有值,那就转换为int,否则转换为unsigned int。
- 如果其中一个操作数的类型是long double,那么另一个操作数也转换为long double;如果其中一个操作数的类型是double,那么另一个操作数也转换为double;如果其中一个操作数的类型是float,那么另一个操作数也转换为long float;其他情况将按照下面的规则进行整型升级,
- 如果其中一个操作数的类型是unsigned long int,那么另一个操作数也转换为unsigned long int;如果其中一个操作数的类型是long int,那么另一个操作数也转换为long int,另一个操作数是unsigned int,在long int 能够完整表示unsigned int的所有值时(比如long 是32位,int是16位),unsigned int 转换为long int,在long int 不能表示unsigned int的所有值时(比如long 和int都是32位),两个操作数都转换为unsigned long int;如果其中一个操作数的类型是unsigned int,那么另一个操作数也转换为long double;
- 如果其中一个操作数的类型是long int,那么另一个操作数也转换为long int;如果其中一个操作数的类型是unsigned int,那么另一个操作数也转换为unsigned int;
总而言之(便于记忆,但不精确),ANSI C 标准大致意思为:当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高,长度更长的方向转换,整型数如果转换为signed不会丢失信息,就转换为signed,否则转换为unsigned。
3、两者比较分析
K&R C采用无符号保留(unsigned preserving)原则,就是当一个无符号类型与int或更小的整型混合时,结果类型就是无符号类型。这是个简单的规则,与硬件无关。下列所示的例子,将会是一个负数丢失符号位。
ANSI C 采用值保留(value preserving)原则,就是当整型操作数混合使用时,结果类型有可能是符号数,也有可能是无符号数,取决于操作数类型的相对大小。
#include <stdio.h>
int main()
{
if(-1 < (unsigned char) 1)
printf("-1 is less than (unsigned char)1:ANSI semantics");
else
printf("-1 NOT less than (unsigned char)1:K&R semantics");
return 0;
}
程序中的表达式在两种编译器下的编译结果不同。-1的位模式是一样的,但是ANSI C将其解释为负数,而K&R C编译器将其解释为无符号数,表示为一个很大的正数。此外,若将上述条件改为“(-1 < (unsigned int) 1)”,在ANSI C编译器会把-1 解释为一个很大的正数。其转化规则如上所述。
4、分析启发
尽量不要在代码中使用无符号类型,以免增加不必要的复杂性。尤其是不要仅仅因为无符号数不存在负值(如:年龄)而用来表示数量。尽量使用像int那样的有符号类型,这样在涉及涉及升级混合类型的复杂细节时,不必担心边界情况(如-1被翻译为非常大的正数)。
只有在使用位段和二进制掩码时,才可以使用无符号数。应该在表达式中使用强制类型转换,使操作数均为有符号数或者无符号数,这样就不必有编译器来选择结果的类型。
二、强制类型转换
当C/C++进行强制类型转换时,会有两种方式:
1. 保持内存中的内容不变,仅修改对这段内存的解释方式, 如int和char的互转
2. 保持值不变(近似),但修改内存中的内容,如int和double的互转
可以自动转换的类型一定能够强制转换,但是,需要强制转换的类型不一定能够自动转换。如下代码所示:
#include <stdio.h>
int main()
{
struct A
{
char a1;
int a2;
}a;
struct B
{
int b1;
char b2;
}b;
struct A *aa = &a;
struct B *bb = &b;
aa->a1 = 1;
aa->a2 = 2;
bb->b1 = 3;
bb->b2 = 4;
struct A * aCast = ( struct A *)(bb);//将B结构体的数据显性强制转化为A结构体数据
printf("%d",aCast->a1);
//若使用隐形强制类型转换,则会出错
//printf("%d",( struct A *)(bb)->a2); //error: 'struct B' has no member named 'a2'
return 0;
}
此外,
强制类型转换仅适用于基类型( "标量类型", 如char,short,int,long,float,double ,或者指针),而不允许用于struct,union等类型。