整型

Go 语言的整型,主要用来表示现实世界中整型数量,比如:人的年龄、班级人数等。它可以分为平台无关整型和平台相关整型这两种,它们的区别主要就在,这些整数类型在不同 CPU 架构或操作系统下面,它们的长度是否是一致的。

go语言常用数据(Go的基本数据类型)(1)

你可以看到,这些平台无关的整型也可以分成两类:有符号整型(int8~int64)和无符号整型(uint8~uint64)。两者的本质差别在于最高二进制位(bit 位)是否被解释为符号位,这点会影响到无符号整型与有符号整型的取值范围。

我们以下图中的这个 8 比特(一个字节)的整型值为例,当它被解释为无符号整型 uint8 时,和它被解释为有符号整型 int8 时表示的值是不同的:

go语言常用数据(Go的基本数据类型)(2)

在同样的比特位表示下,当最高比特位被解释为符号位时,它代表一个有符号整型(int8),它表示的值为 -127;当最高比特位不被解释为符号位时,它代表一个无符号整型 (uint8),它表示的值为 129。 Go 采用 2 的补码(Two’s Complement)作为整型的比特位编码方法。因此,我们不能简单地将最高比特位看成负号,把其余比特位表示的值看成负号后面的数值。Go 的补码是通过原码逐位取反后再加 1 得到的。

平台相关整型,它们的长度会根据运行平台的改变而改变。Go 语言原生提供了三个平台相关整型,它们是 int、uint 与 uintptr,我同样也列了一张表:

go语言常用数据(Go的基本数据类型)(3)

在这里我们要特别注意一点,由于这三个类型的长度是平台相关的,所以我们在编写有移植性要求的代码时,千万不要强依赖这些类型的长度。

浮点型

和使用广泛的整型相比,浮点型的使用场景就相对聚焦了,主要集中在科学数值计算、图形图像处理和仿真、多媒体游戏以及人工智能等领域。

浮点型的二进制表示要想知道 Go 语言中的浮点类型的二进制表示是怎样的,我们首先要来了解IEEE 754 标准。

IEEE 754 是 IEEE 制定的二进制浮点数算术标准,它是 20 世纪 80 年代以来最广泛使用的浮点数运算标准,被许多 CPU 与浮点运算器采用。现存的大部分主流编程语言,包括 Go 语言,都提供了符合 IEEE 754 标准的浮点数格式与算术运算。IEEE 754 标准规定了四种表示浮点数值的方式:单精度(32 位)、双精度(64 位)、扩展单精度(43 比特以上)与扩展双精度(79 比特以上,通常以 80 位实现)。后两种其实很少使用,我们重点关注前面两个就好了。

Go 语言提供了 float32 与 float64 两种浮点类型,它们分别对应的就是 IEEE 754 中的单精度与双精度浮点数值类型。不过,这里要注意,Go 语言中没有提供 float 类型。这不像整型那样,Go 既提供了 int16、int32 等类型,又有 int 类型。换句话说,Go 提供的浮点类型都是平台无关的。

无论是 float32 还是 float64,它们的变量的默认值都为 0.0,不同的是它们占用的内存空间大小是不一样的,可以表示的浮点数的范围与精度也不同。

浮点数在内存中的二进制表示(Bit Representation)要比整型复杂得多,IEEE 754 规范给出了在内存中存储和表示一个浮点数的标准形式,见下图:

go语言常用数据(Go的基本数据类型)(4)

我们看到浮点数在内存中的二进制表示分三个部分:符号位、阶码(即经过换算的指数),以及尾数。这样表示的一个浮点数,它的值等于:其中浮点值的符号由符号位决定:当符号位为 1 时,浮点值为负值;当符号位为 0 时,浮点值为正值。

单精度(float32)与双精度(float64)浮点数比较:

go语言常用数据(Go的基本数据类型)(5)

单精度浮点类型(float32)为符号位分配了 1 个 bit,为阶码分配了 8 个 bit,剩下的 23 个 bit 分给了尾数。而双精度浮点类型,除了符号位的长度与单精度一样之外,其余两个部分的长度都要远大于单精度浮点型,阶码可用的 bit 位数量为 11,尾数则更是拥有了 52 个 bit 位。

复数类型

数学课本上将形如 z=a bi(a、b 均为实数,a 称为实部,b 称为虚部)的数称为复数,这里我们也可以这么理解。相比 C 语言直到采用 C99 标准,才在 complex.h 中引入了对复数类型的支持,Go 语言则原生支持复数类型。不过,和整型、浮点型相比,复数类型在 Go 中的应用就更为局限和小众,主要用于专业领域的计算,比如矢量计算等。

Go 提供两种复数类型,它们分别是 complex64 和 complex128,complex64 的实部与虚部都是 float32 类型,而 complex128 的实部与虚部都是 float64 类型。如果一个复数没有显示赋予类型,那么它的默认类型为 complex128。

关于复数字面值的表示有三种方法:

第一种,我们可以通过复数字面值直接初始化一个复数类型变量:

var c = 5 6i var d = 0o123 .12345E 5i // 83 12345i

第二种,Go 还提供了 complex 函数,方便我们创建一个 complex128 类型值:

var c = complex(5, 6) // 5 6i var d = complex(0o123, .12345E 5) // 83 12345i

第三种,你还可以通过 Go 提供的预定义的函数 real 和 imag,来获取一个复数的实部与虚部,返回值为一个浮点类型:

var c = complex(5, 6) // 5 6i r := real(c) // 5.000000 i := imag(c) // 6.000000

字符串类型

字符串类型,是现代编程语言中最常用的数据类型之一,多数主流编程语言都提供了对这个类型的原生支持,少数没有提供原生字符串的类型的主流语言(比如 C 语言)也通过其他形式提供了对字符串的支持。

在 Go 中,字符串类型为 string。Go 语言通过 string 类型统一了对“字符串”的抽象。这样无论是字符串常量、字符串变量或是代码中出现的字符串字面值,它们的类型都被统一设置为 string,例如:

const ( GO_LOGAN = "less is more" // GO_SLOGAN是string类型常量 s1 = "hello, gopher" // s1是string类型常量 ) var s2 = "I love go" // s2是string类型变量

Go 原生支持 string带来的好处:

第一点:string 类型的数据是不可变的,提高了字符串的并发安全性和存储利用率。

Go 语言规定,字符串类型的值在它的生命周期内是不可改变的。这就是说,如果我们声明了一个字符串类型的变量,那我们是无法通过这个变量改变它对应的字符串值的,但这并不是说我们不能为一个字符串类型变量进行二次赋值。

第二点:没有结尾’\0’,而且获取长度的时间复杂度是常数时间,消除了获取字符串长度的开销。

在 C 语言中,获取一个字符串的长度可以调用标准库的 strlen 函数,这个函数的实现原理是遍历字符串中的每个字符并做计数,直到遇到字符串的结尾’\0’停止。显然这是一个线性时间复杂度的算法,执行时间与字符串中字符个数成正比。并且,它存在一个约束,那就是传入的字符串必须有结尾’\0’,结尾’\0’是字符串的结束标志。如果你使用过 C 语言,想必你也吃过字符串结尾’\0’的亏。Go 语言修正了这个缺陷,Go 字符串中没有结尾’\0’,获取字符串长度更不需要结尾’\0’作为结束标志。并且,Go 获取字符串长度是一个常数级时间复杂度,无论字符串中字符个数有多少,我们都可以快速得到字符串的长度值。至于这方面的原理,我们等会再详细说明。

第三点:原生支持“所见即所得”的原始字符串,大大降低构造多行字符串时的心智负担。

如果我们要在 C 语言中构造多行字符串,一般就是两个方法:要么使用多个字符串的自然拼接,要么需要结合续行符""。但因为有转义字符的存在,我们很难控制好格式。Go 语言就简单多了,通过一对反引号原生支持构造“所见即所得”的原始字符串(Raw String)。而且,Go 语言原始字符串中的任意转义字符都不会起到转义的作用。比如下面这段代码:

var s string = ` ,_---~~~~~----._ _,,_,*^____ _____*g*\"*,--, / __/ /' ^. / \ ^@q f [ @f | @)) | | @)) l 0 _/ \/ \~____ / __ \_____/ \ | _l__l_ I } [______] I ] | | | | ] ~ ~ | | | | |` fmt.Println(s)

你可以看到,字符串变量 s 被赋值了一个由一对反引号包裹的 Gopher 图案。这个 Gopher 图案由诸多 ASCII 字符组成,其中就包括了转义字符。这个时候,如果我们通过 Println 函数输出这个字符串,得到的图案和上面的图案并无二致。

第四点:对非 ASCII 字符提供原生支持,消除了源码在不同环境下显示乱码的可能。

Go 语言源文件默认采用的是 Unicode 字符集,Unicode 字符集是目前市面上最流行的字符集,它囊括了几乎所有主流非 ASCII 字符(包括中文字符)。Go 字符串中的每个字符都是一个 Unicode 字符,并且这些 Unicode 字符是以 UTF-8 编码格式存储在内存当中的。

布尔类型

只有两个取值true和false

,