Go语言系列知识快速查看入口
本章内容
Go语言数据类型包含基础类型和复合类型两大类。
Tips:上述内容会一一回复在下面。
Go语言bool类型数据只允许取值true和false。Go中bool类型占1个字节,默认值为false。
Go语言布尔类型适用于逻辑运算,一般用于程序流程控制和条件判断。
Go语言bool类型的 true 表示条件为真,false 表示条件为假。
案例:
package main
import (
"fmt"
)
func main() {
var isDefault bool
var isTrue = true
fmt.Println("IsOk = ", isTrue, "IsOnline = ", isDefault)
}
上述使用bool定义了一个布尔变量isDefault,此时没有给该变量赋初值,则默认值为false。
我们使用bool定义了一个布尔变量isTrue,其对应的值为true。
Go语言的数字类型分为整数类型、浮点数类型、复数类型。每种数字类型都有其对应的大小以及是否支持正负号的定义。
① 有符号整数
Go语言有符号整数,既可以支持正数也可以支持负数的形式。
数据类型 | 字节 | 取值范围 |
---|---|---|
int | CPU32位 - 4字节CPU64位 - 8字节 | CPU32位:-2147483648 ~ 2147483647;CPU64位:-9223372036854775808 ~ 9223372036854775807 |
int8 | 1字节 | -128 ~ 127 |
int16 | 2字节 | -32768 ~ 32767 |
int32 | 4字节 | -2147483648 ~ 2147483647 |
int64 | 8字节 | -9223372036854775808 ~ 9223372036854775807 |
② 无符号整数
Go语言无符号整数,即只支持正数形式,不支持负数的形式。
数据类型 | 字节 | 取值范围 |
---|---|---|
uint | CPU32位 - 4字节;CPU64位 - 8字节 | CPU32位:0 ~ 4294967295;CPU64位:0 ~ 18446744073709551615 |
uint8 | 1字节 | 0 ~ 255 |
uint16 | 2字节 | 0 ~ 65535 |
uint32 | 4字节 | 0 ~ 4294967295 |
uint64 | 8字节 | 0 ~ 18446744073709551615 |
③ 案例
package main
import (
"fmt"
)
func main() {
var age uint32 = 18
var temp int64 = -12
fmt.Print("Age = ", age, " Temp = ", temp)
}
输出
Age = 18 Temp = -12
Go语言中的浮点数是用于存放小数的。Go语言浮点类型只支持有符号类型,不支持无符号类型。
数据类型 | 最大值 | 常量 |
---|---|---|
float32 | 3.4e38 | math.MaxFloat32 |
float64 | 1.8e308 | math.MaxFloat64 |
Tips:
① 一个 float32 类型的浮点数可以提供大约 6 个十进制数的精度,而 float64 则可以提供约 15 个十进制数的精度。
② 通常应该优先使用 float64 类型,因为 float32 类型的累计计算误差很容易扩散,并且 float32 能精确表示的正整数并不是很大。因为 float32 的有效 bit 位只有 23 个,其它的 bit 位用于指数和符号,所以当整数大于 23 bit 能表达的范围时,float32 的表示将出现误差。
③ Go 语言浮点型表示的数值在很小或很大的时候最好用科学计数法书写,通过 e 或 E 来指定指数部分。
案例
package main
import (
"fmt"
"math"
)
func main() {
var score float32 = -1.1
var temp float64 = 12.2e10
var float32Max = math.MaxFloat32
var float64Max = math.MaxFloat64
fmt.Print("Score = ", score, "\nTemp = ", temp)
fmt.Print("\nFloat32Max = ", float32Max, "\nFloat64Max = ", float64Max)
}
输出
Score = -1.1
Temp = 1.22e+11
Float32Max = 3.4028234663852886e+38
Float64Max = 1.7976931348623157e+308
Go语言提供了两种复数类型,complex64和complex128。
其中,complex64的实数部分和虚数部分都是float32类型,complex128的实数部分和虚数部分都是float64类型。
① 语法
complex(real,image)
② 参数
参数 | 描述 |
---|---|
complex | 定义复数类型使用的关键字。 |
real | 1.8e308 |
image | 1.8e308 |
③ 案例
package main
import (
"fmt"
)
func main() {
var score complex64 = complex(1,2)
var number complex128 = complex(23.23, 11.11)
fmt.Print("Score = ", score, " Number = ", number, "\n")
fmt.Print("Real Score = ", real(score), " Image Score = ", imag(score), "\n")
fmt.Print("Real Number = ", real(number), " Image Number = ", imag(number))
}
输出
Score = (1+2i) Number = (23.23+11.11i)
Real Score = 1 Image Score = 2
Real Number = 23.23 Image Number = 11.11
Tips:使用 real 获取复数的实部,使用 imag 获取复数的虚部。
字符串是一个不可改变的字节序列。字符串可以包含任意的数据,但是通常是用来包含可读的文本。
Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
Go语言字符串的内容(纯字节)可以通过标准索引法来获取,在中括号[ ]内写入索引,索引从0开始计数。
Go语言字符串形式详解
"hello world"
说明:使用双引号定义的字符串,会识别转移字符
`hello world`
`hello
"world"
`
说明:使用反引号定义的字符串,以字符串的原生形式输出,包括换行和特殊字符,可是实现防止攻击、输出源代码等效果。
案例
package main
import (
"fmt"
)
func main() {
var name string = "Hello \"World\""
var site = `Hello
"World"`
fmt.Println("Name = ", name)
fmt.Println("Site = ", site)
}
输出
Name = Hello "World"
Site = Hello
"World"
说明
① 我们使用 string 定义一个字符串,字符串中包含双引号,所以我们必须使用转义字符进行转义,否则会报错。
② 下面,我们使用反引号定义一个多行字符串,该字符串里面所有的字符都会原样输出,不需要进行任何转义。
Tips:更多有关字符串的操作,请参考:
Go语言的指针类型,就是一个存放地址的类型。
Go语言指针变量存放的是一个地址,这个地址指向的空间存放的才是值。
Go语言指针不支持指针的移动,这是Go语言指针与其他语言指针的区别。
① 语法:
var varname1 *type = &varname2
② 参数:
参数 | 描述 |
---|---|
var | 定义变量所使用的关键字 |
varname1 | 指针类型的变量 |
type | 指针变量的类型 |
varname2 | 需要保存地址的变量 |
③ 说明:
1)使用 & 符号可以获取一个变量的地址。
2)使用 * 符号可以获取Go语言指针类型的地址所对应的值。
3)当一个指针被定义后没有分配到任何变量时,它的值为nil,nil指针也称为空指针。
④ 案例:
package main
import (
"fmt"
)
func main() {
var score int32 = 18
// 定义一个指针变量
var pScore *int32 = &score
// 输出变量值及指针值
fmt.Println("score = ", score, "pScore = ", pScore) // score = 18 pScore = 0xc000198000
// 获取指针变量对应的地址存放的值
var scoreVal = *pScore
// 输出指针变量对应的地址存放的值
fmt.Println("scoreVal = ", scoreVal) // scoreVal = 18
// 利用指针pScore修改变量score的值
*pScore = 20
// 输出修改后的score变量的值
fmt.Println("score = ", score) // score = 20
// 定义一个空指针
var ptr *int
// 输出空指针值
fmt.Println("ptr = ", ptr)
}
⑤ 输出:
score = 18 pScore = 0xc000198000
scoreVal = 18
score = 20
ptr = <nil>
Tips:空指针判断:
if(ptr != nil) /* ptr 不是空指针 */
if(ptr == nil) /* ptr 是空指针 */
更多有关指针的内容会在之后的指针板块中更新
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。
因为数组的长度是固定的,所以在Go语言中很少直接使用数组。
和数组对应的类型是Slice(切片),Slice是可以增长和收缩的动态序列,功能也更灵活。Slice在下一回复中讲。
var 数组变量名 [元素数量]Type
package main
import (
"fmt"
)
func main() {
var a [2]int // 定义2个整数的数组
a[0] = 1 // 为数组元素赋值
a[1] = 2 // 为数组元素赋值
fmt.Println(a[1]) // 打印第2个元素
fmt.Println(len(a)) // 打印数组长度
}
输出
2
2
更多有关数组的内容会在之后的数组板块中更新
切片(Slice)是对数组的一个连续片段的引用,所以切片是一个引用类型。
切片默认指向一段连续的内存区域,可以是数组,也可以是切片本身。
slice[开始位置:结束位置]
参数 | 说明 |
---|---|
slice | 表示目标切片对象 |
开始位置 | 对应目标切片对象的索引 |
结束位置 | 对应目标切片的结束索引(结束索引的项不包括在切片内) |
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4, 5}
fmt.Println(a[1:3]) // 区间
fmt.Println(a[2:]) // 中间到尾部的所有元素
fmt.Println(a[:2]) // 开头到中间指定位置的所有元素
fmt.Println(a[:]) // 原有的切片
}
输出
[2 3]
[3 4 5]
[1 2]
[1 2 3 4 5]
var name []Type
var strList []string // 声明一个字符串切片,切片为空,且没有为其分配内存
var numListEmpty = []int{} // 声明一个整型切片,切片为空(没有为其填充),但已为其分配内存
切片是动态结构,可以使用append() 函数向切片中添加元素。
make([]Type, size, cap)
参数 | 说明 |
---|---|
Type | 切片的元素类型 |
size | 为这个类型分配多少个元素 |
cap | 预分配的元素数量,这个值设定后不影响size,只是能提前分配空间,降低多次分配空间造成的性能问题。 |
package main
import (
"fmt"
)
func main() {
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
fmt.Println(len(a), len(b))
}
输出
[0 0] [0 0]
2 2
说明
(1)其中a和b均是预分配2个元素的切片,只是b的内部存储空间已经分配了10个,但实际使用了2个元素。
(2)容量(cap)不会影响当前的元素个数,因此a和b取len都是2。
更多有关切片的内容会在之后的切片板块中更新
Go语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。
结构体是有一系列具有相同类型或不同类型的数据构成的数据集合。
type struct_variable_type struct {
member definition
member definition
...
member definition
}
variable_name := struct_variable_type {value1, value2, value3}
或
variable_name := struct_variable_type {key1: value1, key2: value2, ..., keyn: valuen}
package main
import (
"fmt"
)
type Person struct{
name string
sex string
age int
}
func main() {
fmt.Println(Person{"Bob", "男", 18})
fmt.Println(Person{name: "Alice", sex: "女", age: 18})
// 忽略的字段为0或空
fmt.Println(Person{sex:"女", age: 20})
}
输出
{Bob 男 18}
{Alice 女 18}
{ 女 20}
更多有关结构体的内容会在之后的结构体板块中更新
通道是引用类型,需要使用make进行创建。
通道实例 := make(chan 数据类型)
案例
ch1 := make(chan int) // 创建一个整型类型的通道
ch2 := make(chan interface{}) // 创建一个空接口类型的通道, 可以存放任意格式
type Equip struct{ /* 一些字段 */ }
ch2 := make(chan *Equip) // 创建Equip指针类型的通道, 可以存放*Equip
通道变量 <- 发送值(与通道变量的元素类型一致)
案例
package main
func main() {
// 创建一个整型通道
ch := make(chan int)
// 尝试将0通过通道发送
ch <- 0
}
注意:上述我们只是往通道中发送数据,并没有接收数据的实例,因此发送操作将持续阻塞。Go程序运行时能智能地发现一些永远无法发送成功的语句并作出提示,如下:
fatal error: all goroutines are asleep - deadlock!
接收值 <- 通道变量
通道的数据接收一共有以下4中写法:
① 阻塞接收数据
执行该语句时将会阻塞,直到接收到数据并赋值给data变量。
data := <-ch
② 非阻塞接收数据
使用非阻塞方式从通道接收数据时,语句不会发生阻塞。
data, ok := <-ch
data:表示接收到的数据。未接收到数据时,data为通道类型的零值。
ok:表示是否接收到数据。
③ 接收任意数据,忽略接收的数据
<-ch
执行该语句时将会发生阻塞,直到接收到数据,但接收到的数据会被忽略。
④ 循环接收
通道的数据接收可以借用 for range 语句进行多个元素的接收操作
for data := range ch {
}
通道 ch 是可以进行遍历的,遍历的结果就是接收到的数据。数据类型就是通道的数据类型。通过 for 遍历获得的变量只有一个,即上面例子中的 data。
案例
package main
import (
"fmt"
"time"
)
func main() {
// 构建一个通道
ch := make(chan int)
// 开启一个并发匿名函数
go func() {
// 从3循环到0
for i := 3; i >= 0; i-- {
// 发送3到0之间的数值
ch <- i
// 每次发送完时等待
time.Sleep(time.Second)
}
}()
// 遍历接收通道数据
for data := range ch {
// 打印通道数据
fmt.Println(data)
// 当遇到数据0时, 退出接收循环
if data == 0 {
break
}
}
}
更多有关channel的内容会在之后的channel板块中更新
Map是一种无序的键值对的集合。
Map最重要的一点是通过key来快速检索数据,key类似于索引,指向数据的值。
方式一:使用map关键字
var map_variable map[key_data_type]value_data_type
方式二:使用内建函数make
map_variable := make(map[key_data_type]value_data_type)
案例
package main
import "fmt"
func main() {
countryCapitalMap := make(map[string]string)
/* map插入key - value对,各个国家对应的首都 */
countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "罗马"
countryCapitalMap [ "Japan" ] = "东京"
/*使用键输出地图值 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [country])
}
/*查看元素在集合中是否存在 */
capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */
if (ok) {
fmt.Println("American 的首都是", capital)
} else {
fmt.Println("American 的首都不存在")
}
}
输出
France 首都是 巴黎
Italy 首都是 罗马
Japan 首都是 东京
American 的首都不存在
delete()函数用于删除集合的元素,参数为map和其对应的key。
package main
import "fmt"
func main() {
/* 创建map */
countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
fmt.Println("原始地图")
/* 打印地图 */
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [ country ])
}
/*删除元素*/ delete(countryCapitalMap, "France")
fmt.Println("法国条目被删除")
fmt.Println("删除元素后地图")
/*打印地图*/
for country := range countryCapitalMap {
fmt.Println(country, "首都是", countryCapitalMap [ country ])
}
}
输出
原始地图
France 首都是 Paris
Italy 首都是 Rome
Japan 首都是 Tokyo
India 首都是 New delhi
法国条目被删除
删除元素后地图
Italy 首都是 Rome
Japan 首都是 Tokyo
India 首都是 New delhi
更多有关map的内容会在之后的map板块中更新
在必要以及可行的情况下,一种类型的值可以被转换成另一种类型的值。
由于Go语言不存在隐式类型转换,因此所有的类型转换都必须显示的声明。
valueOfTypeB = typeB(valueOfTypeA)
类型B的值 = 类型B(类型A的值)
例如:
a := 5.0
b := int(a)
说明
① 类型转换只能在定义正确的情况下转换成功,例如从一个取值范围较小的类型转换到一个取值范围较大的类型(如 int16 转换成 int32)
② 当从一个取值范围较大的类型转换到取值范围较小的类型时(将 int32 转换为 int16 或 将float32 转换为 int),会发生精度丢失(截断)的情况。
③ 只有相同底层类型的变量之间可以相互转换(如将 int16 类型 转换成 int32 类型),不同底层类型的变量相互转换时会引发编译错误(如将 boo l类型转换为 int 类型)