C-类型转换(陷阱)

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

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

时间: 2024-08-30 11:17:00

C-类型转换(陷阱)的相关文章

More Effective C++

条款一:指针与引用的区别 指针与引用看上去完全不同(指针用操作符'*'和'->',引用使用操作符'.'),但是它们似乎有相同的功能.指针与引用都是让你间接引用其他对象.你如何决定在什么时候使用指针,在什么时候使用引用呢? 首先,要认识到在任何情况下都不能用指向空值的引用.一个引用必须总是指向某些对象.因此如果你使用一个变量并让它指向一个对象,但是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量.相反,如果变量肯定指向一个对象,例如你的设计不允许变量为

C/C++ 自动类型转换的陷阱

我们先来看一段实现KMP的代码: 1 void getNext(int * next,string str){ 2 int i=0,j=-1; 3 next[0]=-1; 4 while(i < str.length()-1){ 5 if(j==-1 || str[i]==str[j]){ 6 i++; 7 j++; 8 next[i]=j; 9 } 10 else{ 11 j=next[j]; 12 } 13 } 14 } 15 int KMPMatch(string buffer,strin

C# 类型转换is和as 以及性能陷阱

   1.在C#2.0之前,as只能用于引用类型.而在C#2.0之后,它也可以用于可空类型.其结果为可空类型的某个值---空值或者一个有意义的值.示例: 1 static void Main(string[] args) 2 { 3 PrintValueInt32(5); 4 PrintValueInt32("some thing!"); 5 } 6 7 static void PrintValueInt32(object o) 8 { 9 int? nullable = o as i

C语言陷阱——类型转换

以下例子取自<深入理解计算机系统>. 考虑如下的C语言代码: 1 #include<stdio.h> 2 3 typedef unsigned char* byte_pointer; 4 5 void show_bytes(byte_pointer pointer, int size){ 6 int i = 0; 7 for (i = 0; i < size; ++i){ 8 printf("%.2x", pointer[i]); 9 } 10 } 11

java之表达式陷阱

String str1 = "Hello Java的长度:10"; String str2 = "Hello Java的长度:10"; String str3 = "Hello Java的长度:"+"Hello Java".length; String str4 = "Hello "+"Java的长度:10"; System.out.print(str1 == str2);    输出:

我的《C陷阱与缺陷》读书笔记

第一章 词法"陷阱" 1. =不同于== if(x = y) break; 实际上是将y赋给x,再检查x是否为0. 如果真的是这样预期,那么应该改为: if((x = y) != 0) break; 2. &和| 不同于 && 和 ||   3.词法分析中的"贪心法" 编译器将程序分解成符号的方法是:从左到有一个一个字符的读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符床是否可能是一个符号的组成部分:如

Qt入门之小心read()给你设下的陷阱

最近帮学长写了个Qt Tcpsocket客户端的应用程序,被一个问题困扰了许久.因为以前没有用Qt 写过socket的客户端程序 加上时间比较紧,都没能好好的查看些资料就急忙的写代码,看了一些别人的代码,差不多都是相同的,但是我就出现了收数据 不全的问题.. 最后查出原因是我在接受服务器端的数据时使用了read()这个函数,接收的是个结构体,这里顺便说下怎么在Qt 下收发结构体 发: clientsocket->write((char *)&CardInfo, sizeof(CardInfo

JavaScript的陷阱

这本来是翻译Estelle Weyl的<15 JavaScript Gotchas>,里面介绍的都是在JavaScript编程实践中平时容易出错或需要注意的地方,并提供避开这些陷阱的方法,总体上讲,就是在认清事物本质的基础样要坚持好的编程习惯,其实这就是Douglas Crockford很久以前提出的JavaScript风格要素问题了,有些内容直接是相同的,具体请看<Javascript风格要素(1)>和<Javascript风格要素(2)>.在翻译的过程中,我又看到了

嵌入式 Linux C语言(九)——C语言的安全问题和指针陷阱

嵌入式 Linux C语言(九)--C语言的安全问题和指针陷阱 C语言是灵活度和自由度较大的编程语言,作为C语言核心的指针更是让C语言程序员可以越过安全的栅栏,对某些内存区域进行破坏性访问,引发安全风险.很多安全问题都能追根溯源到指针的误用.本文将从指针的角度解读C语言常见的安全问题和指针陷阱. 一.指针的声明和初始化 1.不恰当的指针声明 int* ptr1, ptr2;//声明ptr1为int指针,ptr2为整型 int *ptr1, *ptr2;//ptr1,ptr2都声明为指针 #def

【JavaScript】JavaScript中的陷阱大集合

本文主要介绍怪异的Javascript,毋庸置疑,它绝对有怪异的一面.当软件开发者开始使用世界上使用最广泛的语言编写代码时,他们会在这个过 程中发现很多有趣的“特性”.即便是老练的Javascript开发者也可以在本文找到一些有趣的新陷阱,请留意这些陷阱,当然也可以尽情享受由这些陷阱 带来的“乐趣”! AD: 本文主要介绍怪异的Javascript,毋庸置疑,它绝对有怪异的一面.当软件开发者开始使用世界上使用最广泛的语言编写代码时,他们会在这个过 程中发现很多有趣的“特性”.即便是老练的Java