C语言的隐式类型提升和java的类型兼容
2014年02月12日 C

在java里,x += 1与x = x + 1相同,但是是有一点区别,只不过容易被忽略。如下例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TestCompound
{
public static void main(String[] args)
{
short x = 1;
x += 1;
System.out.println(x);
}
}
//此处x打印的值为2;
public class TestCompound2
{
public static void main(String[] args)
{
short x = 1;
x = x + 1;
System.out.println(x);
}
}

而此处的x = x + 1处就会在编译期报错,因为java编译器认为x + 1是int型的,把int型的赋给short型,类型不兼容,就会报错,要赋值就需要一个强制类型转换,那么在此,二个表达式的区别就看出来了,x += 1,这里暗含了一个自动的类型转换,它会自动把右边的值转换成左边的类型,所以在第一个例子中没有报错,而第二例子中的表达式不会自动转换类型,报错就是必然的了。java的类型转换精度小的向精度大的进行转换,字节长度小的向字节长度大的转换,这里x + 1,x是short类型,x + 1之后编译器认为可能会造成溢出编译器做一个扩宽转换(widening conversion),x + 1的结果值直接向int进行类型,结果在赋值的同时发现x是short类型,这里出现了赋值类型不匹配,造成错误的出现。

C语言中也有一个类型转换的规则在,而且在很多的时候容易引起错误的结果,就是隐式类型提升(TypePromotion),先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
int array[] = {2, 3, 4, 5, 6, 7, 8};
#define GET_LEN (sizeof(array) / sizeof(array[0]))

int main(int argc, const char *argv[])
{
int testValue = 0;
int promotion = -1;
if (promotion <= GET_LEN)
testValue = array[promotion + 1];
else
testValue = array[promotion + 2];
printf("testValue == %d\n", testValue);
return 0;
}

可能有很多像我这样的菜鸟级选手就会说,输出的是2,也就是if判定是成立的,testValue=array[0]

其实并非如此,promotion <= GET_LEN这个表达式包含一个隐式的类型提升,导致结果是testValue == 3,也就是testValue == array[1];因为sizeof的返回一个size_t类型,也就是unsigned int 类型,也就是GET_LEN宏得到的结果就是unsigned int类型,而表达式的左边promotion是一个int,那么在二者比较的时候,编译器会做一个算术的类型转换,右侧的类型unsigned和而promotion是signed的,有符号的会向无符号的类型转换,那么promotion会提升成unsigned int类型,那么编译器就会把-1解释为一个很大的正整数,也就会有testValue == 3的结果了,想要消除这个错误给GET_LEN一个强制类型转换就可以了。同样的例子还有,看代码:

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
char *str = "12345";
if (strlen(str) - 10 >= 0)
{
printf("enter forever!\n");
}
return 0;
}

strlen的返回值也是size_t类型,像上边所说的相同,if判定中左边的表达式strlen(str) - 10的结果会被隐式提升为size_t类型,也就是不会出小于0的情况,在这种情况也,if判定永远成立,这种情况下要么改成strlen(str) > 10, 要么对表达式做强制类型转换。很多时候这种错误会浪费我们很多时间,所以尽量显式的保持类型一致。