本篇内容:指针、数组、切片、map、结构体
复合类型
指针pointer
默认值:nil(nil相当于Java中的null)
内存和指针的区别
内存:内存的内容
指针:内存的编号,也叫地址
package main
import "fmt"
func main() {
// 声明方式1:取地址
var i int = 1
var p1 *int = &i // 指向一个合法内存,"*int"是指针类型,"&"取地址
fmt.Printf("i = %v, p1 = %v\n", i, p1)
*p1 = 100 // "*pointer"操作的是引用
fmt.Printf("i = %v\n", i) // 100
// 声明方式2:new函数
var p2 *int = new(int) // *int类型的p2指针指向匿名的int变量
fmt.Printf("p2 = %v\n", p2)
}
变量作为函数参数是值传递,指针是引用传递
package main
import "fmt"
func change(i int) {
i = 100
fmt.Println("change i = ", i)
}
func changeInMemory(p *int) {
*p = 100
fmt.Println("changeInMemory i = ", *p)
}
func main() {
var i int = 1
// 变量做函数参数是值传递,指针是引用传递
change(i)
fmt.Println("i = ", i)
changeInMemory(&i)
fmt.Println("i = ", i)
}
输出结果
数组
声明和初始化
package main
import "fmt"
func main() {
// 声明
var arr1 [3]int // 声明一个长度为3的数组
fmt.Printf("arr1长度 = %d, arr1容量 = %d\n",
len(arr1), cap(arr1)) // 数组的长度与容量相等
// 初始化
arr2 := [3]int{1, 2, 3}
fmt.Println("arr2 = ", arr2)
arr3 := [...]int{1, 2, 3} // 自动确定数组长度
fmt.Println("arr3 = ", arr3)
arr4 := [3]int{1} // 部分初始化,其余元素以0补全
fmt.Println("arr4 = ", arr4)
arr5 := [3]int{1: 1} // 通过下标初始化,其余元素以0补全
fmt.Println("arr5 = ", arr5)
}
数组作为函数参数是值传递
package main
import "fmt"
func change(arr [3]int) { // [3]int和[4]int是不同类型
arr[1] = 0
fmt.Println("change arr =", arr)
}
func changeByPointer(p *[3]int) {
(*p)[1] = 0
fmt.Println("changeByPointer arr =", *p)
}
func main() {
arr := [3]int{1, 2, 3}
change(arr) // 值传递
fmt.Println("arr =", arr)
changeByPointer(&arr) // 引用传递
fmt.Println("arr =", arr)
}
切片slice
不定长度数组
切片通过声明或截取数组or切片创建
声明
package main
import "fmt"
func main() {
// 切片声明方式1
var s1 []int // 相比数组少了长度
fmt.Println("s1 =", s1)
s2 := []int{}
fmt.Println("s2 =", s2)
// 切片声明方式2:make函数:make(切片类型, 长度, 容量),容量可以省略,与长度的值相等
var s3 = make([]int, 3, 3)
fmt.Println("s3 =", s3)
s4 := make([]int, 3, 3)
fmt.Println("s4 =", s4)
// 声明并初始化
s5 := []int{1, 2, 3}
fmt.Printf("切片s5长度 = %d, 容量 = %d\n", len(s5), cap(s5)) // 长度与容量并不一定相等
}
截取
package main
import "fmt"
func main() {
var arr [5]int = [5]int{1, 2, 3, 4, 5}
/**
截取数组,格式:arr[a:b:c],
从下标a开始截取数组arr直到下标b,不包括b,是一个左开右闭区间,
a可以省略默认值为0,b可以省略默认值为数组arr的长度,c可以省略与b值相等
切片s的长度是b-a,容量是c-a
*/
var s1 = arr[1:3:5]
fmt.Println("s1 =", s1)
fmt.Printf("切片s1长度 = %d, 容量 = %d\n", len(s1), cap(s1))
var s2 = arr[:]
fmt.Println("s2 =", s2)
fmt.Printf("切片s2长度 = %d, 容量 = %d\n", len(s2), cap(s2))
}
切片最终指向的还是底层的数组
修改切片,原数组也会跟着改变
package main
import "fmt"
func main() {
var arr [5]int = [5]int{1, 2, 3, 4, 5}
var s = arr[1:3:5]
s[1] = 100 // 与数组一样通过下标取元素
fmt.Println("s =", s) // [2 100]
fmt.Println("arr =", arr) // 原数组被修改[1 2 100 4 5]
}
切片作为函数参数是引用传递
这个很好理解,因为它指向的是底层数组
append:append(slice, x)
package main
import "fmt"
func main() {
var arr [3]int = [3]int{1, 2, 3}
var s = arr[:]
fmt.Printf("切片s长度 = %d, 容量 = %d\n", len(s), cap(s)) // 切片s长度 = 3, 容量 = 3
/**
append(slice, x),往切片末尾追加"x",返回新的切片s
当元素的数量达到切片容量的上限,通常以两倍容量扩容
*/
s = append(s, 4)
fmt.Printf("切片s长度 = %d, 容量 = %d\n", len(s), cap(s)) // 切片s长度 = 4, 容量 = 6
}
copy:copy(target, source)
package main
import "fmt"
func main() {
var s1 = []int{100, 200, 300, 400, 500}
var s2 = []int{1, 2, 3}
copy(s1, s2) // copy(target, source),将source覆盖粘贴到target
fmt.Println("s1 =", s1) // [1 2 3 400 500]
}
map(映射、字典)
map[keyType]valueType{}
package main
import "fmt"
func main() {
// map
var m = map[string]int{}
m["xiaoming"] = 100
fmt.Println("m =", m)
fmt.Println("len(m) =", len(m)) // 有len()无cap()
// 判断是否存在key: value, ok := map[key]
value, ok := m["xiaoming"]
fmt.Printf("value = %d, ok = %v\n", value, ok)
// 删除key,内置函数delete(map, key)
delete(m, "xiaoming")
fmt.Println("m =", m)
// map作为函数参数时是值传递...
}
结构体
type 结构体名称 struct {}
package main
import "fmt"
// 自定义结构体User
type User struct {
id int // 结构体成员变量,不需要通过var声明,普通变量需要
username string
password string
}
func main() {
// 结构体初始化方式1:顺序初始化,每个成员都必须初始化
var user1 User = User{1, "yw", "123"}
fmt.Println("user1 =", user1)
// 结构体初始化方式2:指定成员初始化,未指定成员默认值
user2 := User{id: 2, username: "hh"}
fmt.Println("user2 =", user2)
// 声明→赋值成员变量
var user3 = User{}
user3.id = 3
user3.username = "ym"
fmt.Println("user3 =", user3)
// 结构体可以通过==或!=比较
fmt.Println("user3 = user2 ok?", user3 == user2)
// 结构体指针声明方式1:指向有效内存
var user4P = &User{id: 4}
user4P.username = "xx" // 可以和变量一样访问成员
(*user4P).password = "123"
fmt.Println("user4P =", user4P)
// 结构体指针声明方式2:通过new申请
var user5P = new(User)
user5P.id = 5
fmt.Println("user5P =", user5P)
// 结构体变量作为函数参数是值传递...
}
可见性
符号首字母大写相当于public修饰,小写相当于private。
不当之处,请予指正。