C和C++中的类型转换

来新部门后被安排的第一个涉及代码的工作就是修改lint,这是熟悉项目代码风格的绝佳途径。而这期间最难搞也是最多的问题就是类型转换问题,像什么整型溢出、符号丢失。

而近期正在看C专家编程,里面讲到了ANSI C规定的算术转换规则,也算是解开了我心中的疑惑。

第6.2.1.1节 字符和整型(整型升级)

char,short或者int型位段(bit-field),包括他们的有、无符号变型,以及枚举类型,可以使用在需要int或者unsigned int的表达式中。如果int可以完整表示源类型的所有值(ANSI规定,short<=int<=long,但是具体多大没有规定),那么该元类型的值就转换为int,否则转换为unsigned int。这称为整形升级

第6.2.1.5节 寻常算术转换

定义:许多操作数类型为算术类型的双目运算符会引发转换,并以类似的方式产生结果类型。它的目的是产生一个普通类型,同时也是运算结果的类型。这个模式称为“寻常算术转换”。

规则:

针对于浮点数

  • 首先,如果其中一个操作数是long double,那么另一个就会被转换成long double;
  • 其次,如果其中一个操作数是double,那么另一个就会被转换成double;
  • 再次,如果float。。。

针对于整型

  • 如果其中一个操作数的类型是unsigned long,那么另一个就会被转成unsigned long;(unsigned long最大,没得选)。
  • 如果其中一个操作数类型是long,而另一个操作数的类型是unsigned int。

    • 如果long能够完整的表达unsigned int的所有值,那么unsigned int会被转换成long。(像更长的类型转移)
    • 如果long不能够完整的表达unsigned int的所有值,那么两个操作数都被转换成unsigned long。(更长的类型还不行,就再转无符号)
  • 如果其中一个操作数A类型是long ,而另一个操作数B的类型是int或者更小类型,那么B就会被转换成long。
  • 如果其中一个操作数A类型是unsigned int,而另一个操作数B的类型是int或者更小类型,那么B就会被转换成unsigned int。
  • 其他的情况,两个操作数都会被转换成int。

总结

采用通俗的话总结上面的规则:

当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高、长度更长的方向转换,整形术如果转换为signed不会丢失信息,就转换成signed,否则转换成unsigned。

看一个例子

#include 

int array[] = {1, 2, 3, 4, 5, 6, 7};
#define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0]))


int main() {
    int d = -1;
    int x = 0;

    if (d <= TOTAL_ELEMENTS - 2) {
        printf("array len more than 2\n");
    }

    return 0;
}

首先代码的原意是希望判断array的长度是否大于等于2,期望可以printf的内容可以打印出来。但是根据上面的规则来看,TOTAL_ELEMENTS的结果是unsigned int(因为sizeof()的返回值),那么就会导致d从-1转换成unsigned int,那会是一个巨大的正整数。所以表达式的结果为假。

这段代码以及变种在平时的工作代码中几乎到处都是,所以根据我司的编程规范,需要注意一下几点:

  • 通用规范建议6.1 尽可能使用函数代替函数式宏,尽量用函数或者内联函数。
  • 安全规范规则4.1 整数之间运算时必须严格检查,确保不会出现溢出、反转、除0。
  • 安全规范规则4.2 整型表达式比较或赋值为一种更大类型之间必须用这种更大类型对他进行求值。
  • 安全规范规则4.3 禁止对有符号整数进行为操作符运算。(有例外)

还有一点我在规范里没找到,但是个人觉得有意义:

运算之前(比较、加减乘除等),手工确保两个操作数的类型一致。既然写C,那么就让一切都在你自己的掌握中,而不是依赖于复杂的、缥缈的编译器潜规则。

你可能感兴趣的:(c++)