C-类型转换(陷阱)

getchar() 返回值为int类型

1.自动类型转换(运算符两边变量类型不同时)

  1).两个变量类型自动转换成一样的类型(会根据参数类型自动转换, 而不是直接位转换), 且运算结果也是转换后的类型

  2).当较低类型的数据转换为较高类型时, 一般只是形式上有所改变, 而不影响数据的实质内容, 而较高类型的数据转换为较低类型时则可能有些数据丢失

  3).在进行自动类型转换的时候, 如果原来的数是无符号数, 那么在扩展的时候, 高位填充的是0, 如果是有符号数, 那么高位填充的时符号位

#include <stdio.h>

int main() {
    unsigned int a = 8;
    if (a > -1) printf("8>-1\n");
    else printf("8<=-1\n");
    return 0;
}

  输出结果是8<-1, 明明赋值为8的变量i, 结果被程序判定比-1还小, 这是怎么回事?

  问题根源在于变量i定义中的unsigned; 我们都知道, int/short/char等类型分signed和unsigned, C的表达式中signed和unsigned混合运算有三种情况:

    a.操作数全为signed

    b.操作数全为unsigned

    c.操作数混合了signed和unsigned

  前两种情况,相同符号操作没什么问题, 可情形c.涉及不同符号间的混合计算就要注意, 编译器会自动对操作数进行规整化:只要表达式中存在一个无符号数, 所有操作数都被转化为无符号数, 运算按相应无符号操作符进行, 计算结果也是一个无符号数(结论:运算时变量会自动转换, 且计算结果同转换后的类型)

  为防止意外转换, 一方面要显式指定类型, 不要用int, char这种缺少提示的中性表示法, 这种含糊的表示用多了, 会本能地忽略和回避符号问题; 另一方面要谨慎选用unsigned型, 不要仅仅因为无符号数没有负值就用它表示数量, 比如有人喜欢用unsigned int定义for/while循环计数量, 这很不安全, 一不小心在循环内和负数做比较, 就会发生逻辑错误或死循环; 最后, 如果非要让unsigned型参与计算, 可以用强制类型转换保证中间操作数和结果为signed

2.赋值中的类型转换, 当赋值运算符两边的运算对象类型不同时, 将要发生类型转换, 转换的规则是: 把赋值运算符右侧表达式的类型转换为左侧变量的类型, 具体的转换如下:

  1).浮点型与整型 

    将浮点数(单双精度)转换为整数时, 将舍弃浮点数的小数部分, 只保留整数部分

    将整型值赋给浮点型变量, 数值不变, 只将形式改为浮点形式, 即小数点后带若干个0

    注意:赋值时的类型转换实际上是强制的

  2).单、双精度浮点型

    由于C语言中的浮点值总是用双精度表示的, 所以:

    float型数据只是在尾部加0延长为doub1e型数据参加运算, 然后直接赋值

    doub1e型数据转换为float型时, 通过截尾数来实现, 截断前要进行四舍五入操作

  3).char型与int型

    int型数值赋给char型变量时, 只保留其最低8位, 高位部分舍弃

    char型数值赋给int型变量时, 一些编译程序不管其值大小都作正数处理, 而另一些编译程序在转换时, 若char型数据值大于127, 就作为负数处理; 对于使用者来讲, 如果原来char型数据取正值, 转换后仍为正值;如果原来char型值可正可负, 则转换后也仍然保持原值, 只是数据的内部表示形式有所不同

  4)int型与long型

    long型数据赋给int型变量时, 将低16位值送给int型变量, 而将高16 位截断舍弃(这里假定int型占两个字节)

    int型数据送给long型变量时, 其外部值保持不变, 而内部形式有所改变

  5)无符号整数

    将一个unsigned 型数据赋给一个占据同样长度存储单元的整型变量时(如:unsigned→int、unsigned long→long,unsigned short→short), 原值照赋, 内部的存储方式不变, 但外部值却可能改变

    将一个非unsigned整型数据赋给长度相同的unsigned型变量时, 内部存储形式不变, 但外部表示时总是无符号的

    计算机中数据用补码表示, int型量最高位是符号位, 为1时表示负值, 为0时表示正值; 如果一个无符号数的值小于32768则最高位为0, 赋给int型变量后、得到正值; 如果无符号数大于等于32768, 则最高位为1, 赋给整型变量后就得到一个负整数值; 反之, 当一个负整数赋给unsigned 型变量时, 得到的无符号值是一个大于32768的值, (这里假定int型占两个字节)

    C语言这种赋值时的类型转换形式可能会使人感到不精密和不严格, 因为不管表达式的值怎样, 系统都自动将其转为赋值运算符左部变量的类型, 而转变后数据可能有所不同, 在不加注意时就可能带来错误, 这确实是个缺点, 也遭到许多人们批评, 但不应忘记的是:C面言最初是为了替代汇编语言而设计的, 所以类型变换比较随意, 当然, 用强制类型转换是一个好习惯, 这样, 至少从程序上可以看出想干什么

3.强制类型转换

  对于从高到低的强制转换, 实质上就是一个截断的操作, 只把低端需要的部分保留, 其余的部分直接扔掉了

2.printf 就是按照什么类型去打印一串010101010111(把数据转换成可显示的形式), 不会进行类型转换, 如果类型不同则会显示垃圾值(float, double都是用%f, %e, %a, 因为当它们向那些未在原型中显式说明参数类型的函数(比如printf)传递参数时, C自动将float类型参数转为double类型(8个字节, 所以用%f打印int, 后面四个字节是随机的))

#include <stdio.h>

int main() {
        float n1 = 3.0;
        float n2 = 3.0;
        long n3 = 2000000000;
        long n4 = 1234567890;
        printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
        return 0;
}

  以上程序输出0 1074266112 0 1074266112

4.scanf 类型不一致 可以得到数据 但是不一定正确

5.函数参数中的类型转换, 相当于赋值操作的自动转换

  看下没有用函数原型的参数类型的例子:

#include <stdio.h>
int imax();int main(void) { printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3)); printf("The maximum of %d and %d is %d.\n", 3, 5, imax(3.0, 5.0)); return 0; } int imax(int m, int n) { return m > n ? m : n; }

  输出

  The maximum of 3 and 5 is 1245120.

  The maximum of 3 and 5 is 1074266112.

  调用函数首先把参数放在一个称为堆栈的临时存储区, 然后被调函数从堆栈中读取这些参数, 但是这两个过程没有相互协调进行, 调用函数根据调用过程中的实际参数类型确定需要传递的数值类型, 但是被调函数时根据 其形式参数的类型进行读取数据的, 因此, 函数imax(3)把一个整数放在堆栈中, 当函数imax()开始执行时, 它会从对战中读取两个整数, 而实际上只有一个需要的数值被存储在堆栈中, 所以第二个独处的数据就是当时恰好在堆栈中的其他数值
  第二次使用函数imax()时, 传递的是float类型的数值, 这时两个double类型的数值就被放在对战中, 而系统中意味着两个64位的数值, 即共128位的数据存储在堆栈中, 因为这个系统中的int类型是32位, 所以当imax()从对战中读取两个int类型的数值时, 它会读出对战中前面64位的数据, 把这些数据对应于两个整数

  更正:把int imax()原型声明改为int imax(int a, int b);

  使用函数原型, 编译器就可以检查函数调用语句是否和其原型声明相一致, 比如检查参数个数是否正确, 类型是否匹配, 如果有一个参数类型不匹配但都是数值类型, 编译器会把实际参数值转换成和形式参数类型相同的数值

6.函数return中的类型转换, 相当于赋值操作的自动转换

  如果实际返回值与函数定义的返回类型不同, 则会自动转换成函数定义的返回类型, 然后再返回

7.

  1).类型自动提升 2).多种类型参加运算, 类型自动转换 3).赋值转换 参考:http://blog.csdn.net/zhuyi2654715/article/details/7782872

1. 如果有一边的类型是long double, 则把另一边也转成long double 
2. 否则, 如果有一边的类型是double, 则把另一边也转成double 
3. 否则, 如果有一边的类型是float , 则把另一边也转成float  
4. 否则, 两边应该都是整数类型, 首先按上一小节讲过的规则对a和b做Integer Promotion, 然后如果类型仍不相同, 则需要继续转换 首先规定char 、shortintlonglong long 的转换级别(Integer Conversion Rank)一个比一个高, 同一类型的有符号和无符号数具有相同的Rank, 然后有如下转换规则:
a. 如果两边都是有符号数, 或者都是无符号数, 那么较低Rank的类型转换成较高Rank的类型 例如unsigned int 和unsigned long 做算术运算时都转成unsigned long  
b. 否则, 如果一边是无符号数另一边是有符号数, 无符号数的Rank不低于有符号数的Rank, 则把有符号数转成另一边的无符号类型 例如unsigned long 和int 做算术运算时都转成unsigned long , unsigned long 和long 做算术运算时也都转成unsigned long  
c. 剩下的情况就是:一边是无符号数另一边是有符号数, 并且无符号数的Rank低于有符号数的Rank 这时又分为两种情况, 如果这个有符号数类型能够覆盖这个无符号数类型的取值范围, 则把无符号数转成另一边的有符号类型 例如遵循LP64的平台上unsigned int 和long 在做算术运算时都转成long  
d. 否则, 也就是这个符号数类型不足以覆盖这个无符号数类型的取值范围, 则把两边都转成两者之中较高Rank的无符号类型 例如遵循ILP32的平台上unsigned int 和long 在做算术运算时都转成unsigned long  

 

posted on 2015-04-29 15:22  John_ABC  阅读(3254)  评论(0编辑  收藏  举报

导航