2.3 数据类型
Go语言是一门静态类型的编程语言,在程序编译期间会检查数据类型的正确性,这就要求编译器在编译期间确定程序中每个值数据类型。静态类型的编程语言借助编译器的类型检查功能来减少潜在的内存分配异常和bug。另外,编译器会根据优化规则对编写的代码进行合理优化,从而达到提高程序执行速度的目的。
Go语言的类型系统可以让IDE或者编辑器更好地帮助我们进行代码的开发,同时也更有利于IDE或者编辑器对代码进行智能提示和语法检查。
Go语言中的数据类型主要包含:
· 字符串类型(string)。
· 数值类型(int16、int、float32、float64等)。
· 布尔类型(bool)。
· 派生类型(指针类型、数组类型、切片类型、结构类型、信道类型、接口类型等)。
不同的数据类型所需的内存大小是不一样的,合理的数据类型可以更好地分配内存、提高内存使用率,同时也会提高变量使用效率。
注意
有些数据类型在计算机的内部表示和编译代码对应的计算机体系结构有关。例如,一个int类型在64位操作系统上占用8字节,而在32位上占用4字节。
在Go语言中,字符串类型、数值类型和布尔类型是内置的数据类型,也称为原始数据类型。原始数据类型的值在进行操作时会复制一个副本,因此字符串类型、数值类型和布尔类型的值在函数或者方法间进行传递的时候传递的是值的副本。
注意
原始数据类型在执行效率上也是非常高的。因此,在编写程序时,用原始的数据类型能够解决的问题就应该尽量用原始的数据类型。
学过C语言的人都知道,数据类型分为值类型和引用类型。字符串类型、数值类型和布尔类型和数组类型在Go语言中是值类型,切片(slice)、信道(channel)、接口(interface)、函数(func)和映射(map)属于引用类型。
Go语言中的结构体类型(struct)可以描述一组不同类型的值,这一组值本质上既可以是引用类型也可以是值类型。
下面分别对字符串类型、数值类型、布尔类型和派生类型进行详细说明。
2.3.1 字符串类型
在Go语言中,字符串就是一串固定长度的字符连接起来的字符序列,是由单个字节连接起来的,使用UTF-8编码。字符串在Go语言的内存模型中用一个2字节长的数据结构来表示,它包含一个指向字符串存储数据的指针和一个长度数据。字符串是一种值类型,它的值不可变。
字符串是只读的字符片段。用单引号(')括起来的是字符,单个字符采用int32表示。例如,'a'默认输出为97。用双引号(")或者反单引号(`)括起来的表示字符串。其中,双引号(")中包含的特殊字符会被转义,比如"I'm here \n"中的\n表示换行;反单引号(`)括起来的字符串不会被转义,而是按照原语输出,比如`Hello world \n`中的\n不会进行换行的转义。
为了直观地了解Go语言中字符串类型的基本用法,下面给出字符串类型用法的示例程序2-8。
示例程序2-8 字符串类型的基本用法:chapter02\code07\str.go
在示例程序2-8中,第05行用关键词var声明了一个变量msg(变量声明在后文会详细介绍),并把双引号(")括起来的字符串内容进行赋值。第08行用单引号(`)对声明的变量msg2进行赋值。第13行只声明了一个string类型的变量msgCopy,但是并未赋值。
第14~19行调用标准库fmt包中的Printf格式化打印函数。其中,Printf格式化输出的通用占位符主要有:
· %v:值的默认格式。
· %+v:添加字段名(主要用于结构体struct)。
· %#v:相应值的Go语法表示。
· %T:相应值数据类型的Go语法表示(例如string)。
· %%:字面上的百分号%,并非值的占位符。
· %s:字符串。
· %d:数字以十进制表示。
· %f:浮点数形式。
· %e:科学计数法的形式。
· %b:字符的二进制表示,可以是字符('a'),也可以是数字(97)。
· %x:数字以十六进制表示,字符串则打印每一个字符的ASCII码。
· %t:布尔(bool)值表示。
· %c:字符对应的ASCII码。
· %p:输出指针(内存地址)的值,例如0xc72f51790a。
因此,第14行fmt.Printf("类型%T,值%#v\n", msg, msg)语句会输出msg的数据类型和值的Go语法表示法。在目录go.introduce\chapter02\code07中打开“命令提示符”窗口,执行命令go run .\str.go,输出结果如图2.9所示。
图2.9 str.go字符串示例运行结果
注意
string类型的零值为空字符串"",获取字符串中某个字符的地址是非法的,比如&msg[0]。
2.3.2 数值类型
Go语言中的数值类型包括整数类型(int8、int16、int32、int64)、浮点类型(float32、float64)和复数类型(complex64和complex128),其中整数类型包括一种无符号整数类型(uint8、uint16、uint32、uint64)。在Go语言中,数值类型是按值传递的,因此当把一个数值类型的变量赋值给另一个变量时,其实赋值的是原变量数值的副本。
注意
int类型在32位操作系统上是int32,而在64位操作系统上是int64。同样的,float类型在32位操作系统上是float32,而在64位操作系统上是float64。
为了直观地了解Go语言中数值类型的基本用法,下面给出数值用法的示例程序2-9。
示例程序2-9 数组类型基本用法:chapter02\code07\num.go
在示例程序2-9中,第06行用关键词var声明了一个变量a,并赋值10.2,Go编译器会根据赋值来推断变量a的数据类型为float。第14行声明变量d的时候指定了具体的整数类型int,因此变量d为整数类型。第15行声明了一个float32类型的变量f,但是并未赋值。第16~20行调用标准库fmt包中的Printf格式化打印函数输出声明的变量。在目录go.introduce\chapter02\code07中打开“命令提示符”窗口,执行命令go run .\num.go,输出结果如图2.10所示。
图2.10 num.go示例运行结果
注意
在Go语言中,整数类型和浮点类型的变量不能直接进行四则运算,否则会报类型不兼容的错误。
如果想查看数值类型的具体值的范围,可以导入math包,其中有内置数值类型的最大值和最小值,比如math.MaxInt32和math.MinInt32。这些值在本质上都是常量,感兴趣的读者可以实际打印输出这些值。
2.3.3 布尔类型
在Go语言中,布尔类型(bool)的值只能是true或者false。其中,true代表真,false代表假。布尔类型是按值传递的,因此当把一个布尔类型的变量赋值给另一个变量时,其实赋值的是原变量布尔值的副本。请参考一个简单的Go语言布尔类型基本用法的示例程序2-10。
示例程序2-10 布尔类型基本用法:chapter02\code07\bool.go
在示例程序2-10中,第05行声明了一个变量a,并赋值为true,此时编译器会推断变量a为布尔类型。第07行声明了一个布尔类型的变量b,但并未赋值,此时变量b的零值为false。第08~09行分别用Printf格式化输出变量的类型,其中占位符为%t。Go语言中不允许将整数类型强制转换为布尔类型。
注意
布尔类型无法参与数值运算,也无法与其他类型进行转换。
2.3.4 派生类型
Go语言中除了基本的数据类型外,还有派生类型,包括指针类型(pointer)、数组类型(array)、结构类型(struct)、信道类型(channel)、函数类型(func)、切片类型(slice)、接口类型(interface)和字典类型(map)。
上述的派生类型会在后续章节进行详细说明,这里暂不介绍。当我们声明一个变量但并未赋值时,Go编译器会自动给变量类型初始化一个零值。Go官方文档中提到,nil是预定义的一种标识符,代表指针、信道、函数、接口、映射或切片的零值。nil不能赋值给字符串、数值和布尔类型,否则会引发panic类型的错误。下面给出每种类型对应的零值:
· 字符串类型:""。
· 数值类型:0。
· 布尔类型:false。
· 指针类型:nil。
· 数组类型:每个数组元素类型对应的零值。
· 信道类型:nil。
· 函数类型:nil。
· 切片类型:nil。
· 接口类型:nil。
· 字典类型:nil。
注意
Go语言中结构体的零值是由构成它的基础数据类型的零值决定的。