作为新生的,不用背负任何历史包袱的全新语言,仓颉充分吸收了过去数十年来众多编程语言之长,原生智能化、天生全场景、高性能、强安全。作为华为鸿蒙系统的平台语言,仓颉拥有良好的发展潜力和前景,极有可能成为第一款形成世界级影响的国产编程语言。青少年现在学习仓颉,就是在投资未来 !

版权声明

作者:重庆大学 陈波

本文可以在互联网上自由转载,但必须注明出处(作者:海洋饼干叔叔)并包含指向本页面的链接。本文不可以以纸质出版为目的进行改编、摘抄。

2.3 整数类型及其字面量(第1部分)

⛵ 知所以然

  计算机里的一切都是基于二进制的,一个整数在计算机里的存储也是二进制的。显然,一个整数类型包含的二进制比特越多,它能表达的整数的范围就越大。

image-20241007191645817

图2-4 Int8/UInt8的存储结构

  一个Int8或者UInt8类型的变量/对象(object)占据8个比特,即1个字节的存储空间,如图2-4所示。这8个比特编上序号,分别为第0位至第7位,其中第7位为最高位。每个比特可以存储一个为0或1的二进制位,故1个字节总共可以表达28共256种不同的状态。

🚸 注意——

  计算机里的一切,变量、函数都可以称作对象(object)。

  为了表示整数的正负,我们需要使用其中的1个比特作为符号位,剩下的7位则用于存储数的绝对值。因此,一个Int8类型的整数对象的储值范围为-27 ~ +27-1,即-128 ~ + 127。诸如Int8这种可以存储正负数的类型称为有符号(signed)类型。

  作为无符号(unsigned)整数类型,UInt8将全部8个比特用于存储数的绝对值,因此它只能存储非负整数,其储值范围为0 ~ 28-1,即0 ~ 255。

image-20241007191823541

图2-5 Int32/UInt32的存储结构

  将多个字节组合在一起,可以增大整数的储值范围。Int16/UInt16占据2个字节共16个比特的存储空间,Int32/UInt32则占据4个字节共32个比特的存储空间,Int64/Int64占据的存储空间则为8个字节共64个比特。

  我们以Int32/UInt32为例,展示了这些多字节整数的存储结构,见图2-5。Int32/UInt32包含由相邻的4个字节组成的32个比特,位权从低到高为第0位至第31位。由于有符号的Int32需要使用1位表示正负,其储值范围为-231 ~ +231-1;无符号的UInt32将全部32位用于存储数的绝对值,其储值范围为0 ~ 232-1。

🌌 见微知著

  在Intel x86以及ARM计算机上,低位字节位于低地址,高位字节位于高地址。对照图2-5,存储第0 ~ 7位的低位字节的地址如果是X的话,则存储第8 ~ 15位、第16 ~ 23位、第24 ~ 31位的高位字节依次位于地址X+1、X+2和X+3。

  表2-1列出了仓颉中主要的整数类型。

表2-1 仓颉中的整数类型
数据类型 含义 储值范围
Int8 1字节8位有符号整数 -27 ~ +27-1
UInt8 1字节8位无符号整数 0 ~ +28-1
Int16 2字节16位有符号整数 -215 ~ +215-1
UInt16 2字节16位无符号整数 0 ~ 216-1
Int32 4字节32位有符号整数 -231 ~ + 231-1
UInt32 4字节32位无符号整数 0 ~ +232-1
Int64 8字节64位有符号整数 -263 ~ + 263-1
UInt64 8字节64位无符号整数 0 ~ +264-1

🚠 物见其然

  下述程序演示了在不同的整数类型对象之间进行加减乘除运算的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Project - Intergers1
package Integers1

/* 程序说明:
Int8是由8个二进制比特组成的有符号整数类型。
UInt16则是由16个二进制比特组成的无符号整数类型。
*/
main(): Int64 {
var a:Int8 = -7
var b:UInt16 = 10
var c:Int32 = Int32(a) + Int32(b) //运算操作数必须具备相同的类型
println("${a} + ${b} = ${c}")
var d:Int32 = c * 20
println("${c} * 20 = ${d}")
var e = (d-1) / 5 //整数除以整数的结果仍为整数
print("${d-1} / 5 = ${e}")
return 0
}

上述程序的执行结果为:

1
2
3
-7 + 10 = 3
3 * 20 = 60
59 / 5 = 11

▶第4 ~ 7行:以“/*”开头,以“*/”结尾的内容为多行注释。连同本程序的第1行,这些注释内容会被仓颉编译器忽略。

▶第9行:定义Int8类型的整数变量a。Int8为8比特有符号类型,其可以存储负整数。

▶第10行:定义UInt16类型的整数变量b。UInt16为16比特无符号类型,只能存储非负整数。

▶第11行:定义Int32类型的整数变量c,并将其赋值为a与b的和。由于仓颉中不同数据类型对象之间不可以直接进行算术运算和赋值,故本行先将变量a、b转换为Int32类型。再次强调,这种转换并不会改变对象a、b的类型,而仅是产生值相同,类型为Int32的临时副本。

▶第12行:使用字符串插值输出a+b的结果。本行代码的输出对应执行结果的第1行。

▶第13行:定义Int32类型的整数变量d,并将其复制为c与20的积。此处,c是Int32类型,字面量(literal)20也可视为Int32类型,两者的乘积自然也是Int32类型,故本行无需进行类型转换。

▶第14行:打印输出c*20的结果,见执行结果第2行。

▶第15行:定义新变量e,并将其赋值为(d-1)除以5的商。此处的“/”为除法运算符,在仓颉中,m/n表示m除以n的商。本行代码的执行过程可以分解为如下几步:1). 仓颉里的计算表达式也遵循“先乘除后加减,有括号的优先计算”的规则,因此d-1首先被计算,由于d是Int32类型,故d-1的差也为Int32类型;2). Int32类型的d-1之差再除以5,商仍为Int32类型;3).商被赋值给变量e。

  第15行代码未显式指定变量e的类型,其类型由编译器根据等号右边的表达式结果类型推断而得,故仍为Int32。

🌌 见微知著

  d等于60,d-1等于59。数学上,59除以5的结果为实数11.8。但在仓颉里,整数除以整数的结果仍为整数,商中的小数部分会舍弃掉。从执行结果的第3行可见,由于d是Int32类型,故(d-1)/5即59/5的实际结果为整数11,商的类型仍为Int32。

▶第16行:使用print()函数而不是println()函数打印(d-1)/5的计算结果。同println()函数不同,print()函数在输出完内容后不会补充输出换行符。本上述程序的输出在第3行末尾结束,未产生“第4行的空行”。

⛵ 知所以然

  将超出对象储值范围的值存入对象,比如将987赋值给Int8类型的整数变量,将产生错误结果。这种情况被称之为溢出(overflow)或者超范围(out of range)。

image-20241007192433844

图2-6 编译时溢出错误

  仓颉编译器会在编译时(compile time)尽可能地识别溢出错误。图2-6中代码第4行被编译器识别为错误,因为987超出了Int8类型变量a的储值范围。

🚸 注意——

  仓颉编译器在编译时的努力并不能排除所有的溢出错误,因为变量的实际取值很可能发生在运行时(run time)。假设有一个程序负责从天文望远镜的照片中识别并统计恒星的数量并存储至一个Int32类型的变量中,在编译时刻,恒星的数量并不确定。等到了程序运行时,如果统计的恒星数量超过了Int32类型的上限(+231-1),就会发生溢出,程序最终将报告一个错误的统计结果。

  选择合适的变量类型,既尽量避免存储器的使用浪费,又确保程序在运行时不会发生溢出,是程序员的职责。

海洋饼干叔叔的仓颉语言学习笔记将持续更新… 敬请期待。


欢迎支持海洋饼干叔叔系列程序设计教材,案例、配套资源丰富,实践性强,高等教育出版社出版。

高校教学同行如果需要样书,或者索取教学支持资源, 请联系公众号或者海洋饼干叔叔本人。

《Python编程基础及应用》 《Python编程基础及应用实验教程》 《C++编程基础及应用》
book1 实验书图片 Cpp小尺寸