1.类型系统简介
我们认识事务时总喜欢先分个类,然后研究每个各类的特性.在编程语言里面把也会把所有基本内容分门别类.
你首先想到的自然是int,long等基本数据类型,然后就是自定义的各种类型(class).这样分门别类有蛮多的好处.一是可以针对不同种类的数据在内存中分配不同大小的内存块,二是可以减少错误.因为不同类型的数据会具有不同的特性,可以进行不同的操作.比如会检查函数参数类型,如果类型不匹配在编译时期(静态类型),或运行时期(动态类型)可以给你报错.或者两者的结合. 另外还可以起注释的作用,基础类型可能不太明显,但自定义类型就明显了啊,通过不同的类型名字和函数名字就可以多少理解到一些逻辑上的意义.
不同的类型也是对底层01字节的抽象与封装,在机器层面看来不存在任何类型的概念,全部是0101.
强类型(弱类型) , 静态类型(动态类型)
估计很多人都被这几个概念搞晕了.我也弄得晕乎乎.
首先要明白的是这4个概念本身不是特别专业,特别明确的术语.然后大家的使用的时候也不是严格按照某个标准来用.各种编程语言的实现也可能是混合使用了这几个概念.所以我们要说某个语言是属于强类型或静态类型的话其实不一定准确的.
强类型与弱类型:
所谓强类型嘛严格的说就是定义了一个变量类型后它就只能是这个类型了,不能再变成其他的,与强类型相反的就是弱类型了.比如你的身份证号定了后肯定不能再换一个的.从这个最严格的意义上讲C++,C#都不能算强类型语言了.但一般我们也讲它们是.反正都是在模糊的使用这个术语.C++,C#中各种数值间可以任意转换(当然有些是隐式转换,有时得显式转换).当然大部分时候我们只从隐式转换的角度来讲,如果啥类型都可以隐式转了才认为是弱类型.比如 a = "3"; b = 2 + a; //如果b能得到结果5则一般认为这样的类型就是弱类型了.
静态类型与动态类型
静态类型指在编译期就去检查类型,所以每个变量都必须用一个类型去指定.比如int num;而动态类型在编译的时候不会去检查类型,在运行的时候才确定.比如直接来个num = 9;
num = "99".很容易看出静态类型的优势是在编译期间就可以给你去做些类型检查,限制可以根据类型信息做一些优化.而动态类型在编译期间根本看不到类型信息,自然不会做啥类型检查和优化之类的了.
2.基本数据类型
C++ 基本数据类型
变量命名规范
c++变量名中可包含字母,下划线,数字.但第一个字符不能是数字.(在VS中能用$,在其他编译器中可能不支持)
C#变量名中可包含字母,下划线,@,数字.但第一个字符不能是数字.如果用@出现在变量名中只能在最开头.并且只能出现一次
c++,C#变量名都是区分大小写的,VB是不区分大小写的.
******************************************************************************************************
类 型 字节数
bool 1
char 1
wchar_t 2 (宽字符类型,存储Unicode代码值.用法wchart_t letter = L'a')
short 2
unsigned short 2 (unsigned 表示无符号,只能取非负数.unsigned short num = 123U ; //数字后面的U可加可不加)
int 4 (整形默认为int,long型需在后加L,如long lNumber = 123L; //L可加可不加)
unsigned int 4 (可简写为unsigned)
long 4 (貌似不同的编译器中不一样,有时会是8,偶也不太确定)
__int64 8 (貌似不是标准的,vs扩充的)
unsigned long 4
float 4
double 8
long double 8 (有些地方貌似是16)
******************************************************************************************************
实际上如果没有特殊需要只需要4种类型就行了:bool, char,int , double.其中
bool表示逻辑运算结果
char表示字符,如果需要考虑非英文字符则可以用wchar_t
int表示整数,如果数值范围比较小可以用short (是short int的简称),范围较大可以用long (是long int的缩写). 当然另外如果要全是非负数可以加unsigned,默认是singed
double表示小数.如果范围小可以用float
vs有几个扩展的类型,和基本类型对应关系是这样
__int8 对应 char
__int16 对应 short
__int32 对应 int
__64没有对应的
C#基本数据类型
******************************************************************************************************
类 型 字节数
bool 1
char 2 (相当于C++中的wchar_t)
short 2
ushort 2
int 4 (整形默认为int,long型需在后加L,如long lNumber = 123L)
uint 4
long 8
ulong 8
float 4
double 8 (浮点型默认为double,声明float型要加F,如float fNumber = 12.3F)
byte 1 (只能取非负数)
sbyte 1 (可以取负数)
decimal 16
******************************************************************************************************
详细解释
布尔类型
C++中的布尔类型实际是是个整数值.所以其他整数也可以当成bool值来用.不过它们之间的转换有点特殊.
true 转换成整数是1,false是0.
所有非0整数转换成bool是true.所有0转换成false (这样的规则在使用指针时最方便.可以直接if(ptr)来判断指针是不是空值)
所以if( true )与if( 123 ) , if( false )与if( 0 ) 是等价的.
bool num = 123;
int no = num + 1; //结果是2,因为num会被先转换成1
由于其他数字能转换成bool值,所以很容易犯的一个错误就是在判断语句变成赋值语句了.
比如: int num = 123;
if ( num = 100) //本来是想写成num == 100的.结果写错了变成赋值语句.此时不会出错.这个语句变成等价于if ( true ).此时虽没出错,但表达的意思完全不一样了
为了不再出现这样的错误,C#做了一些改进.bool类型不再是整数值,不能和其他整数进行运算和转换.
所以在C#中 :int num = 123; if( num = 100 )这样的语句不会再存在,会当成语法错误.
当然如果要出现这样的情况 bool check = false; if ( check = true ) //这就没办法了,当然最好的办法是如果某个值是布尔值时直接当条件去判断,不用再去和true ,false比较.写成if ( check )就行.
另外一个常用的避免判断变赋值的方式,是把变量放后面.比如if (100 ==num) 或者if( check == true).不过这样看起来总有点别扭,不符合一般的人思维习惯啊.像看着某个人衣服穿反了一样
字符类型
C#相当于去掉了C++中的char类型.只保留wchar_t类型,而C#中还是用char这个名字.
整型
首先是C#把C++中的unsigned 简化成u,写起来方便多了.ushort就等价于unsigned short.
其次C#中多出了一个类型byte,占用空间为一个字节.其实这可以和c++中的char对应,刚好也是一个字节,并且实际上也是个整数
另外C#中的long型占8个字节,而且C++中的long貌似根据编译器不同会不一样,有时4字节有时8
浮点型
C#和C++浮点型都有float,double没啥太大区别.不过有一个书写的小区别.
在整型中c#,c++赋值时无符号,或者long型,数字后面的后缀U,L可写可不写.
但在C#浮点型中float num = 123.12F; //float赋值必须加后缀F,当然小写f是一样的
double eno = 123d; //后缀d可写可不写
C++中float,double是没有后缀的.
在C#中之所有比较注重右值(字面值)的后缀书写问题,我觉得应该是因为C#中有个类型推断的特性.就是可以这样命名变量: var somevalue = 123f;
关键字var可以替代任何变量类型,但C#不像javascript,它可是静态类型的.所以编译时就必须确定具体类型是啥,所以必须通过右值推断出变量具体是啥类型.
此时后缀就非常有用了,因为123d, 123f, 123是三种不同的类型.分别代表double,float,int类型.
在数字没有后缀的情况下,C#默认是这样判断的,如果有小数,比如123.11就当成double,整数的话124这样的就默认为int.
在float num = 123.12f;//这样的赋值中如果写成123.12会出错.因为123.12默认是当成double型.而double型不能隐式转换成float型的.
而像long lo = 123;//是因为整数123能隐式转换成long
float fl = 123; //整数123能隐式转换成float
另外C#中多了个浮点类型,decimal ,占16个字节,表示小数可以很精确,主要应用在金融领域,计算跟有钱相关的数字啊.因为差几个小数那就是白花花的银子啊,所以是能精确就精确啊.decimal定义变量时是 decimal money = 123.12m; //加后缀m.当然也可以省略
不过C++中也有个long double.有些编译器中是8字节,有些是12,精确度还是没decimal高
3.值溢出问题
在c++中,如果赋值时值溢出了,不会报错,只是得到个错误的值.比如short sh = 65534; //此值超出了short的范围,但不出错,只是sh得到的值为-2.
而在C#中如果这样赋值 short sh = 65534;//编译不会通过,当成错误来处理
不过在进行类型转换时,C#对值溢出做了些特殊处理.
默认情况下C#跟C++一样,对溢出值不报错,直接给个错误结果完事.
比如int num = 65534;
short sh = (short) num; //sh的值为-2;
但是如果要做溢出检查的话必须得加关键字checked.用法很简单,写上关键字checked,然后加个中括号,中括号里面所有代码在做类型转换时要检查溢出.
checked
{
int num = 65534;
short sh = (short) num;
}
这样的话编译时不会出错,但运行时会有个overflowexception异常.如果你代码中有用checked去做溢出检查那最好要用try catch这样的异常处理去捕捉那异常.
另外如果你在checked括起来的代码中有一部分又不想要溢出检查就再加个unchecked关键字,然后把那一部分代码中括号括起来就行了.