安静的改变——隐式类型提升

C语言中有一些隐晦的地方也是容易造成bug的根源。

先说最常见的一种类型提升:

 1 #include<stdio.h>
 2
 3 int main(void)
 4 {
 5     int m=-1;
 6     unsigned int n=1;
 7     m>n?printf("-1>1\n"):printf("-1<=1\n");
 8
 9     return 0;
10 }

在还是新手的时候,对于这样的现象很纳闷,也是自己写代码的时候很容易出bug的地方。这里的知识点是,当int和unsigned int比较时,int会被提升成无符号的,这样无符号的-1提升成一个很大的正整数,肯定大于1,那么运行结果也就不足为奇了。但是,既然知道了有这种提升存在,那么我们就应该知道c标准到底是怎么定义这样的提升的。

再看下面一个:

 1 #include<stdio.h>
 2
 3 int main(void)
 4 {
 5     unsigned int a;
 6     int  b = -7;
 7     a=6;
 8     (a+b>6)?printf(">6\n"):printf("<=6\n");
 9     printf("%u\n",a+b);
10     a=8;
11     (a+b>8)?printf(">8\n"): printf("<=8\n");/*执行加法之后,才进行类型提升*/
12     printf("%u\n",a+b);
13     return 0;
14 }

通过结果可以知道,在进行运算之后,才进行类型提升。当a=8,b=-7时,相加等于1,输出显式1<8,正确,这也证明了,-7在和8相加前,没有提升成无符号的,而是相加之后的数是无符号的,通过a=6,b=-7相加之后为-1,然后-1提升成无符号的,结果显示>6。

但是,这并没有结束,继续:

 1 #include<stdio.h>
 2
 3 int main(void)
 4 {
 5     int a=-1;
 6     unsigned char b=1;
 7     a>b?printf(">\n"):printf("<\n");
 8
 9     return 0;
10 }

奇怪么,不是说好的无符号数和有符号数比较或者运算之后会提升成无符号的么,那这里怎么显示-1<1了啊?按道理-1转换成无符号的应该大于1啊。这个测试证明了一点,我们对ANSI C标准解释不完全。

类型提升,在ANSI C上举了很多例子,也有点晦涩,用自己的话总结就是:

在无符号和有符号的数运算中,数据类型朝着更大的类型转化,int和unsigned int,后者更大,故转化成后者,int和unsigned char,前者更大,故两者运算时,显示的类型为int,既然为int,自然可以比较-1和1的大小。但是这里地描述并不完全正确,因为还要考虑一个值溢出问题。

时间: 2024-08-13 05:08:09

安静的改变——隐式类型提升的相关文章

理解隐式类型、对象初始化程序和匿名类型

在C# 3.0中,几乎每个新特性都是为LINQ服务的.所以,本文将介绍下面几个在C# 3.0中引入的新特性: 自动实现的属性 隐式类型的局部变量 对象和集合初始化程序 隐式类型的数组 匿名类型 其实这几个特性都是比较容易理解的,对于这几个特性,编译器帮我们做了更多的事情(想想匿名方法和迭代器块),从而简化我们的代码. 自动实现的属性 在C# 3.0以前,当我们定义属性的时候,一般使用下面的代码 public class Book { private int _id; private string

.NET中那些所谓的新语法之一:自动属性、隐式类型、命名参数与自动初始化器

开篇:在日常的.NET开发学习中,我们往往会接触到一些较新的语法,它们相对以前的老语法相比,做了很多的改进,简化了很多繁杂的代码格式,也大大减少了我们这些菜鸟码农的代码量.但是,在开心欢乐之余,我们也不禁地对编译器内部到底为我们做了哪些事儿而感到好奇?于是,我们就借助反编译神器,去看看编译器到底做了啥事!其实本篇中很多都不算新语法,对于很多人来说可能都是接触了很久了,这里主要是针对.NET的老版本来说,是一个“相对”的新语法. /* 新语法索引 */ 1.自动属性 Auto-Implemente

C#隐式类型

隐式类型 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 隐式类型var { class Program { static void Main(string[] args) { //优点:不需要在左侧也加上Dictionary<string,string>代码得到简化 var dict = n

javascript中的隐式类型转化

javascript中的隐式类型转化 #隐式转换 ## "+" 字符串和数字 如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作. 如果其中一个操作数是对象(包括数组),则首先对其调用`ToPrimitive`抽象操作,该抽象操作再调用`[[DefaultValue]]`,以数字作为上下文. `[1,2]+[3,4]=='1,23,4'` 原因,因为数组的valueOf操作无法得到简单的基本类型,于是它转而调用toString.因此上栗得到的是'1,23,4'

隐式类型、对象集合初始化、匿名类型

隐式类型和对象集合初始化器是在C# 3.0中引入的. 1 隐式类型 var关键字,主要还是编译器根据变量的值来推断其类型. 1.1隐式类型的局部变量 1 class Program 2 { 3 static void Main(string[] args) 4 { 5 var stringvariable="learning hard"; 6 stringvariable=2; 7 } 8 } 其实当你把鼠标放在var上面的时候,还是可以看到其类型的. 使用隐式类型时有一些限制,包括一

C#语法糖之第一篇:自动属性&amp;隐式类型

今天给大家分享一下C#语法糖的简单的两个知识点吧. 自动属性:在 C# 4.0 和更高版本中,当属性的访问器中不需要其他逻辑时,自动实现的属性可使属性声明更加简洁. 客户端代码还可通过这些属性创建对象. get and set accessors." id="mt3">如下面的示例所示声明属性时,编译器将创建一个私有的匿名支持字段,该字段只能通过属性的 get 和 set 访问器进行访问. 我们C#4.0以前的传统方式的属性是用来封装字段的,这里我简单的对比一下这两种方

C#4.0语法糖之第一篇:自动属性&amp;隐式类型

今天给大家分享一下C#语法糖的简单的两个知识点吧. 自动属性:在 C# 4.0 和更高版本中,当属性的访问器中不需要其他逻辑时,自动实现的属性可使属性声明更加简洁. 客户端代码还可通过这些属性创建对象. get and set accessors." id="mt3">如下面的示例所示声明属性时,编译器将创建一个私有的匿名支持字段,该字段只能通过属性的 get 和 set 访问器进行访问. 我们C#4.0以前的传统方式的属性是用来封装字段的,这里我简单的对比一下这两种方

隐式类型var

隐式类型var (1)现在越来越多的项目中你可以看到var,这是什么意思呢?其实这就是C#3.0新特性提供的隐士类型var,var关键字指示编译器根据初始化语句右侧的表达式推断变量的类型. var Kencery="大家晚上好";   //定义变量 var list=new List<int>();    //定义一个List集合 var Kencery=new{ID=1,Name="HYL",age=24}  //对象初始化起定义的类型 var gen

C#隐式类型局部变量&amp;隐式类型数组

[隐式类型局部变量] 可以赋予局部变量推断“类型”var 而不是显式类型.var 关键字指示编译器根据初始化语句右侧的表达式推断变量的类型.推断类型可以是内置类型.匿名类型.用户定义类型或 .NET Framework 类库中定义的类型. // i is compiled as an int var i = 5; // s is compiled as a string var s = "Hello"; // a is compiled as int[] var a = new[] {