面试题1: 下面程序的结果是什么?
Char foo(void)
{
Unsignedint a=6;
Int b=-20;
Char c;
(a+b>6)?(c=1):(c=0);
Return c;
}
解析: unsignedint 类型的数据与int类型的数据相运算之后,自动转化为unsigned int类型。(int类型比unsigned少了一个符号位)。因此a+b的值不是-14,而是一个unsigned int 类型 42949667382
因此返回值是1,与实际我们想要的结果不相符合。若两个操作数都不是long型,而其中的一个是unsigned int 类型,则另一个也被转化成为unsigned int型,否则两个操作数一定要都是int。
可以定义一个int类型的数接受a+b的值,如int c=a+b;或者对相加的结果进行强制类型转化,int(a+b); 这样就没错了。
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者很少懂得这些问题。无论如何,这道无符号整形问题的答案都是>6. 原因是当表达式中存在有符号类型和无符号类型的时候,所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数。所以该表达式计算出的结果大于6.这一点对于频繁用到无符号数据类型的嵌入式系统来说是非常重要的,如果你打错了这道题,也就意味着你就到了不到这份工作的边缘。
答案 1
截图如下:
Int main()
{
Unsignedint a=6;
Intb=-20;
Charc;
(a+b>6)?(c=1):(c=0);
Cout<<int(a+b)<<endl;//-14
Cout<<a+b<<end;//42949667382
Return0;
}
扩展知识
C++定义了一组内置类型对象之间的标准转换,在必要的时候他们被编译器隐式地应用到对象上。隐式类型转换发生在下列这些典型的情况之下:
1. 在混合类型的算术表达式中:
在这种情况下,最宽的数据类型成为目标转换类型。这也被成为算术转换(ArithmeticConversion),例如:
Int ival=3;
doubledval=3.14159;
//ival 被提升为double类型:3.0 int 被提升为double
Ival+dval;
2. 用一种类型的表达式赋值给另一种类型的表达式
在这种情况下目标转换类型是被赋值对象的类型。例如在下面第一个赋值中文字常量0的类型是int。他被转化为int*型的指针表示空地址。在第二个赋值中double型的值被取成int型的值。
Int*pi=0;
Ival=dval;
3. 把一个表达式传递给一个函数,调用表达式的类型与形式参数的类型不相同
Externdouble sqrt(double);
Cout<<”Thesquare root of 2 is ”<<sqrt(2)<<endl;
2被提升为double类型:2.0
4. 从一个函数返回一个表达式的类型与返回类型不相同,在这种情况下返回的表达式的类型自动转化为函数返回类型。例如:
Doubledifference(int ival1,int ival2)
{returnival1-ival2;}
算术转换保证了二元操作符,如加法或者乘法的两个操作数被提升为共同的类型,然后再用它表示的结果的类型。两个通用的原则如下:
1. 为了防止精度损失,如果有必要的话,类型总是被提升为较宽的类型
例如 int 提升为double
2. 所有含有小于整数的有序类型的算术表达式在计算之前其类型都会被转换成整数。
规则的定义如上所述。这些规则定义了一个类型转换层次结构。我们从最宽的类型longdouble开始。
如果一个操作数的类型是long double, 那么另外一个操作数无论什么类型都要被转化成为long double。例如在下面的表达式中,字符常量小写字母a将被提升为long double,它的ASCII为97,然后再加到long double 型的文字常量上。
3.14159L+’a’
如果两个操作数都不是long double类型,那么若其中一个操作数是double型,则另外一个就将被转换为double型。例如:
Intival;
Floatfval;
Double dval;
//在计算加法之前 fval和ival都被转化为double
Dvaf+ival+fval;
类似的,如果两个操作数都不是double型别,而其中一个操作数是float型,则另外一个被转换为float 型,例如:
Char cval;
Int ival;
Float fval;
在计算加法之前,ival和cval都被转换成为double
Cval+fval+ival;
否则如果两个操作数都不是3中浮点类型之一,他们一定是某种类型的整数。在确定共同的目标提升类型之前,编译器将在所有小于int的整值类型之上施加一个被成为整值提升(integral promotion)的过程。 例如 char enum类型要事先被提升为int
在进行整值提升时类型char,signed char,unsigned char和short int 都被提升为类型int。如果机器上的类型空间足够表示所有unsigned short型的值,这通常发生在short用半个字而int用一个字表示的情况,则unsigned short int也可以被转换为int(在VS2008中满足这个情况)。否则他会提升为unsignedint. wchar_t和枚举类型也被提升为能够表示其底层类型(underlying type)所有值的最小整数类型。例如一直如下的枚举类型:
Enumstates{bad,ok};
相关联的值为0和1.这两个值可以但不是必须存放在char类型的表示中的。当这些值实际上被用作char类型来存储的时候,char代表了枚举的底层类型,然后status的整值提升将它的底层类型转换为int。
在下面的表达式中:
Char cval;
Bool found;
Enummumble{m1,m2,m3} mval;
Unsigned longulong;
Cval+ulong;ulong+found;mval+ulong;
在确定两个操作数被提升的公共类型之前,cval,found和mval 都被提升为int类型。
一旦整值提升执行完毕,类型比较就又一次开始。如果一个操作数是unsigned long型,则第二个也被转换为unsigned long型。上面的例子中所有被加到ulong上的三个对象都被提升为unsigned long型。如果两个操作数的类型都不是unsigned long而其中一个操作数是long型,则另一个也被转换为long型。例如
Char cval;
Long lval;
Cval+1024+lval;在计算加法之前,cval和1024都被提升为long型。
Long类型的一般转换有一个例外。若一个操作数是long型而另外一个是unsigned int 型,那么只有机器上的long型的长度足以存放unsignedint的所有值(一般来说,在32位操作系统中long型和int型都用一个字长表示,所以不满足这里的假设条件),unsigned int 才会被转化为long型别,否则两个操作数都被提升为unsigned long型。若两个操作数都不是long型而其中一个是unsigned int 型别,则另外一个也被转换为unsigned int型别,否则两个操作数一定都是int型。
尽管算术转换的这些规则带给你的困扰可能多于启发,但是一般的思想是尽可能地保留多类型表达式中涉及到的值的精度。这正是通过吧不同的类型提升到当前出现的最宽类型来实现的。