Go语言学习-指针、结构体struct、切片slice、range和映射


本文参考:

Go语言之旅
Go By Example

指针

指针的操作类似cpp

  • 指针类型声明 *T 是指向 T 类型值的指针,这和cpp声明有点差别。其零值为 nil
  • & 操作符会生成一个指向其操作数的指针。
  • * 操作符解引用得到指向的底层值。
package main

import "fmt"

func main() {
	//声明 *int类型的指针e
	var e *int //零值nil
	fmt.Println(e)

	i, j := 42, 1000

	p := &i         // 取得i的内存地址
	fmt.Println(*p) // 解引用读取 i 的值
	*p = 21         // 解引用修改 i 的值
	fmt.Println(i)

	p = &j       // p重定向,指向j的地址
	*p = *p / 10 // 通过指针对 j 进行除法运算修改值
	fmt.Println(j)
}

输出:

<nil>
42
21
100

结构体

  • 使用 Name:value语法可以仅给结构体部分字段赋值
  • 结构体字段使用.访问
  • 结构体指针的访问(*p).X 简化成隐式间接引用p.X
package main

import "fmt"

type Node struct {
	X int
	Y int
}

var (
	// 创建一个 Node 类型的结构体
	v1 = Node{1, 1}
	// 使用 Name:语法可以仅给结构体部分字段赋值, X:0 被隐式地赋予
	v2 = Node{Y: 1}
	//
	v3 Node
	// 创建一个 *Node 类型的结构体(指针)
	p = &Node{1, 2}
)

func main() {
	// 类似cpp,声明的空结构体能直接使用.访问
	v3.X = 777
	v3.Y = 666
	fmt.Println(v3)
	fmt.Println(v1, v2, p)
	
	// 结构体指针的访问,(*p).X 简化成隐式间接引用p.X
	p.X = 1e9
	fmt.Println(p)

}

输出:

{777 666}
{1 1} {0 1} &{1 2}
3
&{1000000000 2}

数组

  • 类型 [n] T 表示拥有 n 个 T 类型的值的数组,先声明再往里面赋值,用法和cpp类似,不同于用Java要new出空间
  • 数组的长度是其类型的一部分,因此数组不能改变大小
package main

import "fmt"

func main() {
	//先声明再赋值
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)
	
	//声明的同时初始化
	primes := [6]int{2, 3, 5, 7, 11, 13}
	fmt.Println(primes)
}

Go中数组类型默认是值传递,在方法中修改的话不会影响原来的值。但是可以通过传递指针使其变为引用传递。

// 值传递
func toValueList(val [3]int){
	val[0]=4
	val[1]=5
	val[2]=5
}

func main(){
	c := [3]int{1,2,3}
	toValueList(c)
	fmt.Println(c)
} // [1,2,3]

// 引用传递
func toValueList(val *[3]int){
	val[0]=4
	val[1]=5
	val[2]=6
}

func main(){
	c := [3]int{1,2,3}
	toValueList(&c)
	fmt.Println(c)
} // [4,5,6]

之前在写程序中碰到的一个疑惑点可以拿出来,和下面的切片有关:

// 定义了一个切片的数组
d := [3][]string{{"abc", "def"}, {"111", "222"}}

q := d[0] // 传递了数组的一个元素为切片
d[0] = nil
fmt.Println(q) //[abc def] d[0]为nil并没有影响传递的数组元素
fmt.Println(d) // [[] [111 222] []]

//如果像下面这样
q := d[0]
d[0][1] = "x" // 改变了切片里的一个元素
fmt.Println(q) // [abc x] // 改变了底层切片的元素
fmt.Println(d) // [[abc x] [111 222] []]

切片

  • 切片类似于没有长度的数组,可以动态伸缩改变,类型[]T表示一个元素类型为 T 的切片。
  • 切片的操作类似于Python的切片操作。
  • 切片像是数组的引用,不存储数据,只是描述了底层的一段。这点需要重点理解,是精髓。
    – 如果更改切片元素,会通过引用将底层的也改变,与它共享底层数组的切片都会观测到这些修改。
    – 在后面创建了固定容量的切片以后,我们通过切片操作进行长度截断,但是底层数组还在,可以在原先截断的基础上用“超越其长度”的切片进行扩容(理论上不能超过原容量)。
package main

import (
	"fmt"
)

func main() {
	//切片类似于没有长度的数组,类型 []T 表示一个元素类型为 T 的切片。
	q := []int{2, 3, 5, 7, 11}
	fmt.Println(q)

	r := []bool{true, false, true, true, false, true}
	fmt.Println(r)

	s := []struct {
		i int
		b bool
	}{
		{2, true},
		{3, false},
		{5, true},
		{7, true},
		{11, false},
		{13, true},
	}
	fmt.Println(s)

	// 可以创建二维切片
	board := [][]string{
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
	}
	fmt.Println(board)

	// 可以从数组中创建切片
	primes := [6]int{1, 2, 3, 4, 5, 6}
	// 切片操作,下界默认为0,上界默认为切片长度,类似于python切片
	var a = primes[1:4]
	fmt.Println(a) //[2,3,4]
	var b = primes[2:]
	fmt.Println(b) //[3,4,5,6]

	a = a[:2]
	fmt.Println(a) //[2,3]

	a = a[1:]
	fmt.Println(a) //[3]

	// 切片像是数组的引用,不存储数据,只是描述了底层的一段
	// 如果更改切片元素,会通过引用将底层的也改变,与它共享底层数组的切片都会观测到这些修改
	names := []string{"jxz1", "jxz2", "jxz3", "jxz4"}
	var k1 = names[0:2]
	var k2 = names[1:3]
	fmt.Println(k1, k2) // [jxz1 jxz2] [jxz2 jxz3]

	// 修改切片元素k2
	k2[0] = "XXX"
	// 其他切片和底层都会被改变
	fmt.Println(k1, k2) // [jxz1 XXX] [XXX jxz3]
	fmt.Println(names)  // [jxz1 XXX jxz3 jxz4]
}

切片的长度是它所包含的元素个数,可以通过len(s)获取
切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。

容量的改变重点在于第一个元素的位置是否发生变化,否则长度变化仍然不影响容量。

package main

import "fmt"

func main() {
	s := []int{2, 3, 5, 7, 11, 13}
	printSlice(s)

	// 截取切片使其长度为 0
	s = s[:0]
	printSlice(s)

	// 拓展其长度,底层的容量够,是允许它改变长度,引用到原来的s的
	s = s[:4]
	printSlice(s)

	// 舍弃前两个值
	s = s[2:]
	printSlice(s)
	
	// 切片零值为nil
	var p []int
	fmt.Println(p, len(p), cap(p))
	if p == nil {
		fmt.Println("nil!")
	}
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

输出:

len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 [] //长度改变,容量不变
len=4 cap=6 [2 3 5 7] // 长度改变,容量不变
len=2 cap=4 [5 7] //长度改变,第一个元素改变,容量改变
[] 0 0 //切片零值
nil!

用make创建切片:

package main

import "fmt"

func main() {
	// 创建容量为5,长度为5的切片,元素为零值
	a := make([]int, 5)
	printSlice("a", a)

	// 创建容量为0,长度为5的切片,元素为零值
	b := make([]int, 0, 5)
	printSlice("b", b)

	c := b[:2]
	printSlice("c", c)

	d := c[2:5]
	printSlice("d", d)
}

func printSlice(s string, x []int) {
	fmt.Printf("%s len=%d cap=%d %v\n",
		s, len(x), cap(x), x)
}

输出:

a len=5 cap=5 [0 0 0 0 0]
b len=0 cap=5 []
c len=2 cap=5 [0 0]
d len=3 cap=3 [0 0 0]

向切片中添加元素

package main

import "fmt"

func main() {
	// 创建空切片
	var s []int
	printSlice(s)

	// 添加0
	s = append(s, 0)
	printSlice(s)

	// 添加1
	s = append(s, 1)
	printSlice(s)

	// 可以一次性添加多个元素,容量更大
	s = append(s, 2, 3, 4)
	printSlice(s)
}

func printSlice(s []int) {
	fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

输出:

len=0 cap=0 []
len=1 cap=1 [0]
len=2 cap=2 [0 1]
len=5 cap=6 [0 1 2 3 4]

range

for循环的range形式可遍历切片或者映射,可返回索引和元素值。

package main

import "fmt"

func main() {
	var s = []int{11, 12, 13, 14, 15, 16, 17, 18}
	//遍历切片
	//只遍历索引,索引从0开始
	for i := range s {
		fmt.Print(i, " ")
	}
	fmt.Println()
	//遍历索引和值,索引从0开始
	for j, k := range s {
		fmt.Printf("%d %d \n", j, k)
	}
	//只遍历值
	for _, v := range s {
		fmt.Print(v, " ")
	}
	fmt.Println()

	// 遍历map
	mapt := make(map[int32]string) // New empty set
	mapt[1] = "a"                  // Add
	mapt[2] = "b"
	mapt[3] = "c"
	mapt[4] = "d"
	delete(mapt, 2)
	// 输出key,value
	for k, v := range mapt {
		fmt.Println("k:", k, "v:", v)
	}
	// 只输出key
	for k := range mapt { // Loop
		fmt.Println(k)
	}
}

输出:

0 1 2 3 4 5 6 7  //只输出索引
0 11 // 输出索引和值
1 12 
2 13 
3 14 
4 15 
5 16 
6 17 
7 18 
11 12 13 14 15 16 17 18 //只输出值
k: 1 v: a // 输出key,value
k: 3 v: c
k: 4 v: d
1 //只输出key
3
4

映射

映射的类型 map[key]value

package main

import "fmt"

type Vertex struct {
	Lat, Long float64
}

//定义string-Vertex的映射切片
var m = map[string]Vertex{
	"jxz1": {1, 1},
	"jxz2": {2, 2},
}

func main() {
	fmt.Println(m)

	// 定义string-int的映射
	m := make(map[string]int, 10)

	//插入元素
	m["a"] = 1
	fmt.Println("The value:", m["a"]) //1
	//修改元素
	m["a"] = 2
	fmt.Println("The value:", m["a"]) //2
	//删除元素
	delete(m, "a")
	fmt.Println("The value:", m["a"]) // 0 键不存在,返回零值
	//通过双赋值检测键是否存在, v返回0值,ok=false
	v, ok := m["a"]
	fmt.Println("The value:", v, "Exist?", ok) // 0 false
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

互联网民工蒋大钊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值