1.1 基础类型
1.1.1 布尔类型
go语言的布尔类型与其他的语言基本上无差别,可以通过var flag bool
来定义一个布尔变量。布尔类型变量可以支持的位运算包括:与、或、非。
bool类型在内存中占用一个字节的空间,也不支持其他类型强制转换为bool类型。
isOn := true // 直接赋值
isOff := false
isEqual := (1 == 2) // 也可以表达式的结果计算后赋值
1.1.2 整型
整型在所有语言中的差异不明显,理解了一种语言的整型就可以举一反三的理解其他语言。go语言中整型变量的申明为:var a int
,其中var
是申请变量的标识,a
为变量,int
为类型。
go语言中int按照长度和有无符号可以分为以下几类:
有符号 | 长度 | 无符号 | 长度 |
---|---|---|---|
int8 | 1个字节 | uint8 | 1个字节 |
int16 | 2个字节 | uint16 | 2个字节 |
int32 | 4个字节 | uint32 | 4个字节 |
int64 | 8个字节 | uint64 | 8个字节 |
int | 32位机器:4个字节 64位机器:8个字节 |
uint | 32位机器:4个字节 64位机器:8个字节 |
除此之外,还有一个特殊类型的整型uintptr
专门用于存储变量的内存地址,在后面的章节中进行介绍。
整型的二进制、八进制、十六进制的表示方法和打印如下:
v := 0b0101 // 二进制
fmt.Printf("%b \n", v) // 0101
v := 0o555 // 八进制
fmt.Printf("%o \n", v) // 555
v := 0x12F // 十六进制
fmt.Printf("%x \n", v) // 12f
fmt.Printf("%X \n", v) // 12F
整型之间可以进行类型强制转换,但是需要注意类型表示的数值范围,防止出现长类型转换为短类型后出现截断错误。
var a int = 10
var b uint32
b = uint32(a) // int类型强制转为uint32类型
1.1.3 浮点型
Go使用两种浮点型变量来存储小数,分别为float32和float64,其采用IEEE-754标准。 float32占用4个字节,其中1位表示符号,8位表示指数,23位表示尾数。小数部分能包含7位,精确6位。 float64占用8个字节,其中1位表示符号,11位表示指数,52位表示尾数。小数部分能包含16位,精确15位。
1. 表示方法
我们可以通过var p float64
来定义一个浮点型变量,浮点型的赋值可以分为下面两种方式:
p1 := .1415 // 相当于0.1415
p2 := 1. // 相当于1.0,小数点一定要加上,不然会被推导为整型
那么p2的类型是什么呢?go语言会将p2自动推导为float64类型,而不是float32类型,这一点需要注意。
浮点类型还可以通过科学计数法来表示很大或者很小的数,通过e
或者E
来指定指数部分,如:
const Planck = 6.62606957e-34 // 普朗克常数
浮点型打印通过使用%f
来格式化输出,且可以控制打印结果的小数点位数。
fmt.Printf("%f\n", math.Pi) // 打印所有小数
fmt.Printf("%.2f\n", math.Pi) // 保留两位小数
2. 数值比较
因为浮点数在计算机内部存储并不是精确的,比如0.3+0.6
的结果并不是0.9,而是趋近于0.9。这个可以去自行研究IEEE-754标准中关于浮点数存储的知识,本文基础部分不进行介绍。
func main() {
var p1 float64 = 0.3
var p2 float64 = 0.6
fmt.Println(p1+p2)
}
// 输出:0.8999999999999999
因为浮点类型有这样一个属性,那么对于两个浮点类型就不能直接使用==
来比较大小了,可能会产生错误的结果。一般是由我们自己定义一个精度,如果两个数差值在精度内,则认为两数相等。
func isEqual(p1, p2 float64){
if p1 - p2 < 0.000001{
return true
}
}
最后,优先使用float64类型,其精度更高。
1.1.4 复数类型
在数学上,复数由两个部分组成,一个是实部,一个是虚部。比如1+i
表示实部为1,虚部也为1的复数。
在go语言中,使用两个浮点型来表示一个复数,其中complex64由两个float32来分别表示实部和虚部,complex则是由两个float64来表示实部和虚部的。go也是为数不多内置复数的语言。
复数的声明、实部虚部获取方法:
func complex(r, i FloatType) ComplexType // 生成一个复数
func real(c ComplexType) FloatType // 获取复数的实部
func imag(c ComplexType) FloatType // 获取复数的虚部
// exapmle
b := 1.0 + 2.0i // b是complex128类型的
Println(real(b)) // 输出+1.000000e+000
Println(imag(b)) // 输出+2.000000e+000
c := complex(1.0, 2.0) // c是complex128类型的
Println(real(c)) // 输出+1.000000e+000
Println(imag(c)) // 输出+2.000000e+000
由于复数由浮点型来表示,因此复数也继承了浮点型的一些特点,如无法精确表示运算结果,最好不用等号比较大小等。此外,math/cmplx
库提供了许多复数操作的函数。
1.1.5 字符串类型
字符串类型也是一种基本类型,底层使用一个数组存储字符串的值。声明一个字符串类型的变量使用var str string
,字符串的定义和字符元素的获取:
str := "are you ok" // 字符串的声明和赋值
first := str[0] // 获取字符串的第一个字符
last := str[len(str)-1] // 获取字符串的最后一个字符
fmt.Printf("%s, %c, %c", str, first, last)
也支持定义多行字符串,使用`进行修饰,被修饰的内容,转义字符会失效。如:
str := `a
b
c
d
`
fmt.Println(str)
// 打印输出
a
b
c
d
字符串不支持修改,编译会报错。字符串是只读有很多好处,比如并发安全,方便共享内存。
如果想要修改一个定义的字符串,可以先将字符串转换为切片,然后修改切片后再转为字符串,此时的字符串是一个新的字符串。
1. 基本操作
字符串支持直接进行比较,支持的比较运算符包含==,!=,<,<=,>=,>,举例如下:
if str1 > str2{
return true
}
字符串的拼接可以直接使用'+',比如:s := str1 + str2
,也可以使用格式化输出的方式,比如
s := fmt.Sprintf("%s%s", str1, str2)
不同的拼接方式,性能开销是不一样的,在后续章节中将会介绍这一点。
2. 字符串遍历
字符串支持utf-8和unicode两种编码格式,遍历字符串有多种方式,分别如下:
str := "hello, 世界"
fmt.Println(len(str)) // 长度13,其中1个中文占用3个字符
// 1. utf-8方式遍历
for i := range str{
fmt.Println(str[i]) // str[i]类型是byte
}
// 2. utf-8方式遍历
for i := 0; i < len(str); i++{
fmt.Println(str[i])
}
// 3. unicode方式遍历
for i, c := range str{
// i是下标
// c是rune类型,rune底层其实是uint32类型
fmt.Println(c)
}