爱城 最新下载地址:C/C++深层探索(十五)
来源:百度文库 编辑:偶看新闻 时间:2024/05/02 19:29:43
简单类型的转换
C90内置的简单类型只有两大类:整数类型和浮点数类型。前者包括:
char
short
int
long
后者包括:
float
double
其中整数类型又可以分为两种:带符号的(signed)和无符号的(unsigned)。
而浮点数还有一个long double类型,不过相当多的系统不支持超过64位的浮点数,在这些系统上,double和long double是相同的(都是64位)。IA的浮点单元支持80位的浮点运算,所以我们的实验平台的确存在比double更高精度的long double类型,但由于很少用到,这里不列入讨论范围(其实道理还是一样的)。
一般地,在32位系统中这些类型的长度如下:
类型
长度(以字节计算)
char
1
short
2
int
4
long
4
float
4
double
8
其实,C语言标准并没有规定short、int和long各占多少字节,只是要求它们满足下列关系(除了这个关系,数值范围还有规定):
short占用的空间≤int占用的空间≤long占用的空间
而按照约定,int的大小一般刚好占用一个机器字(word),所以有上面的结果。
这样便产生一个问题:如果我们对某种类型的变量赋予其他类型的值,C语言将怎样处理?例如把一个int值赋给一个char变量等。
上面的问题可以进一步分成两个小问题:
#1 相同种类变量(同是整型或同是浮点型)之间的相互赋值
先讲浮点型变量。由于浮点型只有两种:float和double,情形比较简单。把一个float型值赋给double变量总是安全的,因为double是64位,占用空间比float的32位多出一倍,自然精度更高,可以保存的数也更大。相反则不然,把一个double型值赋给float变量就有点冒险了。精度可能会有损失,并且数值一旦超过float所能保存的最大值时就会发生错误。值得注意的是,编译器可能不会提示你这一点,例如:
float f = 1.0E+100;
这条语句把一个double型常数1.0E+100用作float变量f的初始值,而1.0E+100显然已经远远超过float能够存储的最大值,但是编译器依然一声不吭,它仅仅试图帮你进行转换,至于成功与否却是另一回事(是否做出警告要看具体的编译器和编译选项,不过对于整数的溢出,编译器通常都会做出警告)。
不过有时候我们还是需要类似的转换,如果我们心里百分之百清楚某个浮点数绝对可以用float来存储,但C语言默认浮点常数是double型的,这时可以指明浮点数的类型或进行强制转换:
float f = 1.0E+10; //明确指出1.0E+10是float型常数
或:
double d = 12.345;
float f = (float)d; //把d的值强制转换为float型值赋给f
其实即使我们不明确指出,编译器同样会进行强制转换,“白纸黑字”写出来是为了提高代码的可维护性,让其他人清楚地知道:这里存在一个转换。
现在来讲讲整型变量之间的转换问题,情形稍微复杂一点。因为整型变量即使占用同样大小的空间也存在一个“阴暗角落”—signed还是unsigned?
大家都知道,把char值赋给int变量,数值不会发生溢出错误,因为int比char“大”。但要小心,绝大多数系统的C语言实现都是把char区分为signed与unsigned,就是说,你写:
char c;
就相当于写:
signed char c;
于是编译器把c看成是(占一个字节的)带符号整数。看看下面的例子:
char c = 0x80;
int i = c;
你猜猜i的值是什么?
--“
因为c是signed char,而0x80(用二进制表示就是:10000000)的最高位是“
如果这样写:
unsigned char c = 0x80;
int i = c;
那么i的值就是128,因为c经过(无符号)扩展后的数值是0x00000080。
大家可以察觉出整型变量扩展的关键是对“被扩展变量”的符号解释。如果被扩展变量是带符号的(signed),那么扩展就是带符号的扩展;如果被扩展变量是无符号的(unsigned),则扩展就是无符号的扩展。
再来看看:
int i = 5;
int u = i;
有问题吗?
--绝对没有!
好,这样呢:
int i = 5;
unsigned int u = i;
--别犹豫,当然可以。编译器仅仅是把i的值原封不动的赋给u,除此之外没有任何多余的动作。
如果这样呢:
int i = -5;
unsigned int u = i;
让我们仔细看一下过程:
首先,编译器把-5转换成32位数值0xFFFFFFFB赋给i,然后把i的值复制到u,所以最后u仍然等于0xFFFFFFFB。值还是那个值,但怎么解释就是另一回事。0xFFFFFFFB对于i(带符号整数)来说是-5,对于u(无符号整数)来说是4294967291。
再极端一点的例子:
unsigned int u = -1;
这好像有点前后矛盾的味道。但编译器依然“我行我素”,-1就是0xFFFFFFFF,于是u的值还是如假包换的0xFFFFFFFF。只不过由于你把u作为无符号整数来使用,所以,(根据上下文)u就被解释为4294967295(即232-1)。
接下来,我们再讨论一下“裁剪”。
int i = 4;
char c = i;
这样的代码意味什么?
答案更简单:编译器把4(即0x00000004)赋给i,它知道char占一个字节,那么,把i的低8位赋给c,于是,c等于4。
如果这样呢:
unsigned int u = 1000000;
short s = u;
同样道理,1000000是0x
再来看:
unsigned int u = 0xFFFF00000000;
猜猜u是多少?
--没错,等于“
#2 不同种类变量(整型与浮点型)之间的相互赋值
无论是float还是double,它们所能存储的最大值都大于4294967295(即232-1),最小值都小于-2147483648(即-231),你可能会认为把一个整型值转换为浮点值一定是安全的。不过,事实并非这么简单,因为浮点型存储的数值范围虽然大但却受精度(有效数字)的限制。例如:
unsigned int u = 4294967295;
float f = u;
你猜猜结果是什么?
f的值变成了4294967296.000000!
因为如果要把一个数值转换成float再转换回来并且转换回来的数值要和原先的一模一样则要求这个数值只能有6位有效数字,超过6位有效数字就会发生一些精度的损失。所以,若非实在必要,最好不要把整数转换成浮点数处理。
反过来,如果把浮点数转换成整数呢?
我们可以预料到会发生什么事,那就是小数部分被舍掉了。只是有一点必须要注意,转换的第一步一定要先转换成以下其中一种类型:
int、long、long long、unsigned、unsigned long、unsigned long long
例如:
float f = 23456.789;
int i = f;
short s = i;
这样,i是23456,s也是23456。千万不要以为short可以放下23456就一步登天:
float f = 23456.789;
short s = f;
上面的代码得出的s值根本不是23456。因为C语言的数值运算(包括不同数值类型间的转换)遵从国际标准LIA。而LIA只规定了浮点数转换为正文提到的那几种整数类型的规则,因此,类似float转换成short这样的行为的结果是不确定的。
最后别忘了:浮点数能够转换成整数的前提是(去掉小数部分)数值能够用对应得整数类型表达,如果数值太大(或太小),那转换的结果也就没有意义,例如:
double d = 1234567876.543;
int i = d; //都溢出了
你说,i应该等于多少?
发表于: 2006-06-19 ,修改于: 2006-06-19 17:06,已浏览1043次