Go数据类型
数据类型概览
基础数据类型
类型 | 长度(字节) | 默认值 | 说明 |
---|---|---|---|
bool | 1 | false | |
byte | 1 | 0 | uint8,取值范围[0,255] |
rune | 4 | 0 | Unicode Code Point, int32 |
int, uint | 4或8 | 0 | 32 或 64 位,取决于操作系统 |
int8, uint8 | 1 | 0 | -128 ~ 127, 0 ~ 255 |
int16, uint16 | 2 | 0 | -32768 ~ 32767, 0 ~ 65535 |
int32, uint32 | 4 | 0 | -21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名 |
int64, uint64 | 8 | 0 | |
float32 | 4 | 0.0 | |
float64 | 8 | 0.0 | |
complex64 | 8 | ||
complex128 | 16 | ||
uintptr | 4或8 | 以存储指针的 uint32 或 uint64 整数 |
1 | fmt.Printf("os arch %s, int size %d\n", runtime.GOARCH, strconv.IntSize) //int是4字节还是8字节,取决于操作系统是32位还是64位 |
数值型变量的默认值是0,字符串的默认值是空字符串,布尔型变量的默认值是false,引用类型、函数、指针、接口的默认值是nil。数组的默认值取每个元素对应类型的默认值,结构体的默认值取每个成员变量对应类型的默认值。
1 | var a int |
复合数据类型
类型 | 默认值 | 说明 |
---|---|---|
array | 取每个元素对应类型的默认值 | 值类型 |
struct | 取每个成员变量对应类型的默认值 | 值类型 |
string | “” | UTF-8 字符串 |
slice | nil | 引用类型 |
map | nil | 引用类型 |
channel | nil | 引用类型 |
interface | nil | 接口 |
function | nil | 函数 |
自定义类型
类型别名
1 | type byte = uint8 |
自定义类型
1 | type user struct {name string;age int} //用分号把多行代码隔开 |
数组
数组是块连续的内存空间,在声明的时候必须指定长度,且长度不能改变。所以数组在声明的时候就可以把内存空间分配好,并赋上默认值,即完成了初始化。
一维数组初始化
1 | var arr1 [5]int = [5]int{} //数组必须指定长度和类型,且长度和类型指定后不可改变 |
二维数组初始化
1 | //5行3列,只给前2行赋值,且前2行的所有列还没有赋满 |
访问数组里的元素
- 通过index访问
- 首元素 arr[0]
- 末元素 arr[len(arr)-1]
- 访问二维数组里的元素
- 位于第三行第四列的元素 arr[2][3]
遍历数组
1 | //遍历数组里的元素 |
通过for range遍历数组时取得的是数组里每一个元素的拷贝。
1 | arr := [...]int{1, 2, 3} |
在数组上调用cap()函数表示capacity容量,即给数组分配的内存空间可以容纳多少个元素;len()函数代表length长度,即目前数组里有几个元素。由于数组初始化之后长度不会改变,不需要给它预留内存空间,所以len(arr)==cap(arr)。对于多维数组,其cap和len指第一维的长度。
数组的长度和类型都是数组类型的一部分,函数传递数组类型时这两部分都必须吻合。go语言没有按引用传参,全都是按值传参,即传递数组实际上传的是数组的拷贝,当数组的长度很大时,仅传参开销都很大。如果想修改函数外部的数组,就把它的指针(数组在内存里的地址)传进来。
1 | //参数必须是长度为5的int型数组(注意长度必须是5) |
切片
切片是一个结构体,包含三个成员变量,array指向一块连续的内存空间,cap表示这块内存的大小,len表示目前该内存里存储了多少元素。
1 | type slice struct { |
切片的初始化
1 | var s []int //切片声明,len=cap=0 |
切片相对于数组最大的特点就是可以追加元素,可以自动扩容。追加的元素放到预留的内存空间里,同时len加1。如果预留空间已用完,则会重新申请一块更大的内存空间,capacity大约变成之前的2倍(cap<1024)或1.25倍(cap>1024)。把原内存空间的数据拷贝过来,在新内存空间上执行append操作。
1 | s := make([]int, 3, 5) |
1 | //探究capacity扩容规律 |
1 | arr := make([]int, 3, 5) |
通过指定起止下标,可以从大切片中截取一个子切片。
1 | s := make([]int, 3, 5) //len=3, cap=5 |
刚开始,子切片和母切片共享底层的内存空间,修改子切片会反映到母切片上,在子切片上执行append会把新元素放到母切片预留的内存空间上。当子切片不断执行append,耗完了母切片预留的内存空间,子切片跟母切片就会发生内存分离,此后两个切片没有任何关系。
1 | func sub_slice() { |
go语言函数传参,传的都是值,即传切片会把切片的{arrayPointer, len, cap}这3个字段拷贝一份传进来。由于传的是底层数组的指针,所以可以直接修改底层数组里的元素。
1 | func update_slice(s []int) { |
1 | s := make([]int, 2, 3) |
获取切片的地址用&s;获取切片底层数组的地址用&s[0],或直接把s当地址打印。
字符串
字符串里可以包含任意Unicode字符。
1 | s := " My name is 张朝阳☻" |
字符串里可以包含转义字符。
1 | s := "He say:\"I'm fine.\" \n\\Thank\tyou.\\" |
字符串也可以用反引号来定义,反引号里的转义字符无效。反引号里的内容原封不动地输出,包括空白符和换行符。
1 | s := `here is first line. |
字符串常用操作
方法 | 介绍 |
---|---|
len(str) | 求长度 |
strings.Split | 分割 |
strings.Contains | 判断是否包含 |
strings.HasPrefix,strings.HasSuffix | 前缀/后缀判断 |
strings.Index(),strings.LastIndex() | 子串出现的位置 |
1 | s := "born to win, born to die." |
把多个字符串拼接成一个长的字符串有多种方式。
- 加号连接。
- func fmt.Sprintf(format string, a …interface{}) string
- func strings.Join(elems []string, sep string) string
- 当有大量的string需要拼接时,用strings.Builder效率最高
1 | s1 := "Hello" |
string中每个元素叫“字符”,字符有两种:
- byte:1个字节, 代表ASCII码的一个字符。
- rune:4个字节,代表一个UTF-8字符,一个汉字可用一个rune表示。
string是常量,不能修改其中的字符。
string可以转换为[]byte或[]rune类型。
string底层是byte数组,string的长度就是该byte数组的长度, UTF-8编码下一个汉字占3个byte,即一个汉字占3个长度。
1 | s1 := "My name is 张朝阳" |
数据类型转换
强制类型转换的基本方法就是把目标类型放在变量前面,把变量括起来。
1 | var i int = 9 |
- 低精度向高精度转换没问题,高精度向低精度转换会丢失位数。
- 无符号向有符号转换,最高位是符号位。
- byte和int可以互相转换。
- float和int可以互相转换,小数位会丢失。
- bool和int不能相互转换。
- 不同长度的int或float之间可以相互转换。
1 | //高精度向低精度转换,数字很小时这种转换没问题 |
string和其他数据类型互转。
1 | var err error |
map
go map的底层实现是hash table,根据key查找value的时间复杂度是O(1)。
map的初始化
1 | var m map[string]int //声明map,指定key和value的数据类型 |
添加和删除key
1 | m["英语"] = 59 //往map里添加key-value对 |
len(m)获取map的长度,go不支持对map上执行cap函数。
读取key对应的value时,如果key不存在,则返回value类型的默认值,所以强烈建议先判断key是否存在。
1 | if value, exists := m["语文"]; exists { |
遍历map
1 | //遍历map |
map中的key可以是任意能够用==操作符比较的类型,不能是函数、map、切片,以及包含上述3中类型成员变量的的struct。map的value可以是任意类型。
1 | type f func(int) bool |
channel
channel(管道)底层是一个环形队列(先进先出),send(插入)和recv(取走)从同一个位置沿同一个方向顺序执行。sendx表示最后一次插入元素的位置,recvx表示最后一次取走元素的位置。
1 | var ch chan int //管道的声明 |
1 | read_only := make (<-chan int) //定义只读的channel |
定义只读和只写的channel意义不大,一般用于在参数传递中。
1 | //只能向channel里写数据 |
可以通过for range遍历并取走管道里的元素,当管道为空且被close后,for循环退出。
1 | close(ch) |
slice、map和channel是go语言里的3种引用类型,都可以通过make函数来进行初始化(申请内存分配)。因为它们都包含一个指向底层数据结构的指针,所以称之为“引用”类型。引用类型未初始化时都是nil,可以对它们执行len()函数,返回0。