第 5 讲:数据类型(二):字面量和数据类型的关系
Part 1 定义
什么是字面量
在这里示例里,25 这个数字是我们直接写到程序代码里的数据信息。这个 25 我们就称为整数字面量。
在 C# 的世界里,我们使用的字面量都对应一个“默认的”数据类型。换句话说,即使我们左侧的变量不给出数据类型的名称(不管是 BCL 名称还是关键字),我们总可以通过字面量本身确定变量的类型。
2-1 正整数字面量
我们可以总结出如下两点:
如果是一个整数字面量的话,那么它默认就是
int
类型的数据;如果这个数据可以用范围更小的数据类型表达出来,那么这个数值也可以直接自动赋值给这个类型的变量。
下面我们来看一下,这个解释是什么意思。首先
这个赋值语句下,25 是默认 int
类型的。大家都知道,int
类型和 byte
类型来比较的话,显然 int
范围更大;如果将一个能表示出范围更大的数据类型赋值给一个表示范围较小的数据类型,就可能产生错误,比如说一个 byte
类型的变量,接受了 300 这个数,显然就是不合理的。但是我们通过第二点可以得到,显然例子里的 25 是属于 byte
类型的数据范围的,所以这样的赋值语句,编译器根本不应该告诉你,这是错误的赋值。所以,编译器允许这么赋值。
因此,我通过这则示例告诉你,25 是 int
类型的;而 age
是 byte
类型的,编译器也允许这么赋值。
当然,25 也可以写成 +25,因为数学上的正号加不加都没关系,因此 C# 也是一样。
2-2 负整数字面量
除了正整数,还有负整数的字面量。和数学上的表达完全一致,我们使用减号来表示负数:
这里的 -13
就是负整数字面量。很有趣的一点是,因为 int
类型的取值范围是包含负数的,因此实际上负整数字面量的默认数据类型依然是 int
。而且,它依旧遵循前文提到的“范围最小赋值原则”(说白了就是前面那一节内容里面的那个第二点)。
2-3 长整数字面量
C# 的语法是严谨的。由于前文的整数字面量是默认 int
类型的,那么我们就无法直接写一个超出 int
范围的整数字面量。但是 long
类型必须要赋一个初始数值的话,怎么办呢?
我们使用后缀字母 L
(或者小写 l
)来告诉编译器,这个字面量是 long
类型的。
显然,这里的 78 亿是超出了 int
的取值范围的(int
最大才到 2147483647),因此如果我们直接写上 7800000000000 的话,就会出现编译器错误。因此,我们需要强制追加后缀 L
来告诉编译器,这里的这个数字是 long
同理,如果数字为负数,依旧添加负号即可:long p = -1000000000000L
。
另外,这么赋值也是没问题的:long p = 13L
,但是反过来就不对了:如果右侧是 long
的字面量,而左边是 int
类型,即使范围合适,依然不允许赋值(即错误赋值):int p = 13L
。这是因为,既然 13 是正常的 int
字面量,那么为什么要追加 L
后缀,然后再赋值给 int
类型呢?这不是就绕了一步吗?因此编译器不允许我们这么搞。
2-4 无符号整数字面量
我们知道,一个整数字面量是没办法确保是不是正整数的。为了确保字面量本身就可以表示无符号类型,那么就产生了这样的字面量。
比如,这样。我们使用 U
字母(或小写 u
)来表示和暗示这个字面量是 uint
类型的(即无符号整数)。
int
类型,而这里的无符号的整数字面量默认是 uint
类型的。正是因为这个区别,因此我们如下的四种组合里,有一种就是不正确的:
int age = 25;
(正确)int age = 25U;
(错误)uint age = 25;
(正确)uint age = 25U;
(正确)
这里的 int age = 25U
是错误的赋值。因为 int
类型和 uint
类型并不能说“范围完全兼容”,uint
类型只包含正整数,因此范围比 int
类型多了一截;而 int
可表示负数,所以 int
可以表达负数。
显然,这样的数据类型是无法完整兼容的,因此我们无法通过字面量来把 uint
赋值给 int
类型。
另外,由于无符号字面量本身就是不可负的,因此无符号字面量的前面是不能加负号的。换句话说,-25U
是错误的写法;但是,带正号可以:+25U
。
最后,U
后缀和 L
后缀是可以混用的。不论你的 U
字母和 L
字母的顺序如何,大小写如何,只要写在一起作为字面量后缀,就可以表示一个字面量是 ulong
类型的。
同样地,这里的带 UL
后缀的字面量也无法赋值给范围较小类型的其它整数类型。比如 uint age = 25UL
2-5 浮点数字面量
浮点数类型有三种,所以有三种不同的字面量类型。
使用
f
或F
后缀,表示一个小数是float
类型的;使用
d
或D
后缀,表示一个小数是double
类型的;使用
m
或M
后缀,表示一个小数是decimal
类型的;如果任何后缀都没有的小数表达式,默认就是
double
类型的。
从精度上和数据取值范围上来说,double
都比起 float
类型要大,因此 float
类型的字面量可以赋值给 double
,但反过来不行:
这样的赋值里,、g2
和 g3
的赋值是错误的。因为 float
类型范围较小,却接受了一个 double
类型的字面量。
另外,这里简单说一下 decimal
类型字面量。decimal
类型的取值范围比较小,但精度特别高(能精确到 29 位),所以它和 float
还有 double
范围上并不能直接兼容。和 uint
还有 int
的赋值逻辑一样,我们不能将 decimal
字面量赋值给 float
或 double
类型的变量;反过来把 float
或 double
类型的字面量赋值给 decimal
类型的变量也不可以。
这里,后面四种赋值全部都是错误的。
0.37
可以写成.37
;但是小数部分就算为 0,也不能省略:30.0
不能写30.
,这个写法 C 语言允许,但 C# 里不行。
2-6 整数和浮点数
前面我们一直说的是整数和小数的字面量,但它们没有互相赋值。
思考一下,decimal d = 30U;
会不会成功呢?C# 里规定,所有前文提到的字面量全部都可以通过赋值的方式赋值给任何浮点数类型,但反过来的话,任何浮点数类型的数据都不可以赋值给任何整数的数据类型。因为浮点数的范围我们没有在前文里提及,因此我们无法介绍这一点。
大概来说,
float
这个最大可以到大约 10 的 38 次方(再怎么说都超出ulong
最大的整数的数据取值范围了),double
能达到大约 10 的 308 次方,而decimal
的最大值大约是 。所以范围上来说,最小范围的float
类型,也可以完美覆盖任何整数数据类型的数据,因此,用浮点数类型的变量接受整数类型的字面量,是怎么都允许的;但反过来因为数据的取值范围不兼容的缘故,这样就不行了。稍微啰嗦一下,30 和 30.0 的区别是,在 C# 里,30 是整数(
int
字面量),而 30.0 是浮点数(double
字面量)。因此,double d = 30
、double d = 30.0
和double d = 30D
的赋值意义不太一样。后面两种是同样的模式(因为都是double
类型字面量),而第一个则是通过“浮点数接受整数类型的变量”这个规则而赋值的。
2-7 科学计数法字面量
为了保证数据能够简单表达,C# 允许使用科学计数法类似的写法来表达一个浮点数。举个例子:
在这里例子里,1E5
称为科学计数法。科学计数法的表达规则是这样的:如果一个数学上的科学计数法表达是 a * 10 的 b 次方
部分使用字母
E
或e
代替;a 和 b 则按照科学计数法的基本写法即可。
比如 1E5 就是 10 的 5 次方。当然,如果 b 是负数的话,科学计数法里这样表达的数据就是一个小数了。比如 1E-3
就是 10 的 -3 次方,即 1/1000(0.001)。
2-8 字符和字符串的字面量
因为这一个内容牵扯到字符串的使用,因此这里我们不作介绍,我们在下一节就会介绍它们。
2-9 布尔型字面量
最后还剩下布尔型数据的字面量没有说了。因为布尔型只用来表示对错,所以只有真(True)和假(False)两个结果。在 C# 里,直接采用 true
和 false
这两个关键字就可以充当了。
就是这样的。前面的文章其实就已经说过了,不过这里再说一下。这里的 true
和 false
是布尔型里唯有的两个字面量。
另请注意,因为它只表示对错,因此跟整数、浮点数完全没有关联,因为我们无法把布尔型数据赋值给任何浮点数数据类型和任何整数数据类型;反之亦然。
这两种赋值都是错的。bool
字面量只能赋值给 bool
Part 3 总结
我相信你根本记不住前面的东西。所以我这里做一个简要的回顾和归纳。
3-1 字面量默认类型
整数字面量默认是
int
类型的(不论带不带正负号,都是int
类型的);带
L
后缀(或l
)的整数字面量是long
类型的(不论带不带正负号);带
U
后缀(或u
)的整数字面量是uint
类型的(这样的话,只能带正号,或者不要符号);同时带有
U
和L
后缀的整数字面量是ulong
类型的(这两个后缀记号不需要考虑先后顺序,也不需要考虑大小写);带
F
或f
后缀的字面量是float
类型的;带
D
或d
后缀(或不带后缀的小数)的字面量是double
类型的;带
M
或m
后缀的字面量是decimal
类型的;科学计数法字面量默认是
double
类型的;bool
字面量只有两个:true
和false
,且均使用关键字表示。
3-2 字面量赋值关系
所有字面量的默认类型,可以直接赋值给这个类型的变量;
不带后缀的整数字面量是可以允许在兼容范围的时候,赋值给较小数据类型的变量;
带后缀的整数字面量,不能赋值给范围较小的变量(不论是否兼容取值范围);
double
类型的变量能接受float
字面量和double
类型的字面量的赋值,但反之不然;decimal
类型的变量只能接受decimal
字面量,其它的浮点数字面量都不可以;任何整数类型的字面量均可以直接赋值给任何浮点数类型,但反之不然;
由于科学计数法是
double
类型的字面量,所以它的赋值规则和double
字面量的规则是一样的;bool
字面量不能赋值给其它任何类型的变量,反之亦然。