• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Go语言的指针,结构体,数组,slice,range

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

写在前面

  • 从这部分开始,学习的难度开始增加
  • 不像很多编程语言,Go作为新语言并没有抛弃指针
  • slice作为一种较新的数据类型,让人感觉很迷
  • 这一部分,有c++基础的会比较好理解,涉及到“引用”,“地址”,“深复制和浅复制”,但官方教程对此没作深入理解
  • 这段时间接触这份教程,觉得它教给初学者的是感性认识,作为入门的确合适

指针(pointer)

  • 教程原话:A pointer holds the memory address of a value.
  • 也就是说指针存储了值的内存地址
  • 指针的使用参见以下代码:
//一般定义用到了符号*,默认初始值为nil
var p *int      //p才是指针

//获取某个变量的地址用到了符号&
v := 22
p = &v

//获取指针所指向内存地址的值也用到符号*
//官方教程也提到了一般称这个行为作提领(dereferencing)或者indirecting
fmt.Println(*p)     // *p代表的是指针所存储内存地址里的内容
  • 注意,Go中指针不像C或C++,它没有指针算术,这意味着它不能加减

结构体(struct)

  • 结构体,可以理解为多种数据类型的集合体,它的用法如下:
//如下定义一个名为Cube的结构体
type Cube struct {
    X int
    Y int
    Z int
}

//初始化一个立方体,使用了初始化符号:=和大括号{}
//依次赋值
square := Cube{2, 2, 2}
/*
还可以通过field:value的方式初始化,这样可以任意顺序
square := Cube{X:2, Y:2, Z:2}
*/

//访问立方体的长宽高(用英文的句号访问)
square.X = 6
square.Y = 3
square.Z = 5
fmt.Println(square.X, square.Y, square.Z)

//同样的,可以定义某个结构体的指针类型
p := &square
//特别地,以下两种写法等价
(*p).X = 8
p.X = 8
  • 结构体中的特殊语法 name:
type Cube struct {
    X, Y, Z int
}
s1 := Cube{X:1}   //X的值为1,其他默认为0
s2 := Cube{}     //X Y Z都为0
s3 := Cube{X:1, Z:2}   //X为1, Y为0,Z为2

数组(Array)

  • 类型 [n]T 表示,有n个T类型的数组
  • 数组是静态的,n必须为常量,一经定义长度将无法再改变
var my_array [10]int
my_array2 := [3]int{1, 2, 3}
fmt.Println(my_array)    // [0 0 0 0 0 0 0 0 0 0]
fmt.Println(my_array2)    // [1 2 3]
fmt.Println(my_array2[2])   // 3

slice

  • 关于slice常被翻译为切片之类的,我这里觉得有点不妥,干脆不翻译
  • 如果读者去过菜市场,那么买冬瓜的时候,通常商家都会准备好一大条冬瓜,问你要多少,
  • 然后你可能会用手比画,大概一根手指的长度吧,于是商家一刀切下去,你就得到了冬瓜中一根手指长度的部分(有点流水帐。。。)
  • 而slice,就相当于这一刀切下去,得到的一根手指长的部分,冬瓜就是数组(这个比喻不算严谨,但能说明slice并非一片)
//slice的定义和数组相似,它的类型写法是 []T
//slice是一种动态类型,它可以“截取”数组的一部分
//从数组中截取第low位到第high-1位的数据(这个操作其实叫做slicing):array[low:high]
a := [6]int{2,3,5,7,11,13}
var s []int = a[2:5]    //此时s为{5,7,11}    可以理解为数学中区间,左闭右开
  • int a[] 和 int* a在C/C++中可以说等价的,而Go中的slice是否也可以理解为特殊的指针呢?
a := [6]int{2,3,5,7,11,13}
var s []int = a[2:5] 
fmt.Println(a)    // {2, 3, 5, 7, 11, 13}
fmt.Println(s)    // {5, 7, 11}

s[2] = 8
fmt.Println(a)  // {2, 3, 5, 7, 8, 13}
fmt.Println(s)  // {5, 7, 8}

//这部分代码的输出结果,基本可以断定,slice的实质是指针,且它有自己配套的运算方法

  • 给出一个比较复杂的完整例子
package main

import "fmt"

func main() {
	q := []int{2, 3, 5, 7, 11, 13}
	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)
}
  • 在进行slicing时(即array[low:high]),可以利用low和high的默认值
my_array := [6]int{2,3,5,7,11,13}
s0 := my_array[2:4]   //  {5,7}
s1 := my_array[:2]   // {2,3}
s2 := my_array[4:]   //{11,13}
  • slice有两个重要属性:len和cap
  • len:The length of a slice is the number of elements it contains.
  • len就是slice中可访问(为什么是翻译为可访问?)的元素个数
  • cap: The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
  • cap是指slice背后指向的数组中的容量吗?错。slice每次都要从array中获取low到high - 1位置的访问方式,而cap只是获取其中low到该数组array最后部分的容量
  • 对于slice s而言,len和cap可以这样获取:len(s) cap(s)
  • 下面是例子
s := []int{2, 3, 5, 7, 11, 13}     //s为指向数组{2, 3, 5, 7, 11, 13}的slice
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)       //len=6,cap=6,{2, 3, 5, 7, 11, 13}

s = s[:0]    // {}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)   //len=0,cap=6,{}

s = s[:4]      //{2,3,5,7}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)   //len=4,cap=6,{2,3,5,7}

s = s[2:]     //{5,7},这时候的low开始改变了!!
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)   //len=2,cap=4,{5,7}
  • 和指针一样,当slice指向为空,那么slice将为nil值
var s []int
fmt.Println(len(s), cap(s), s)
if s == nil {
    fmt.println("nil!")
}
  • 还可以使用make函数来创建slice
  • 第一个参数是类型
  • 第二个参数是len,可以是变量
  • 第三个参数时cap,可省略,省略后cap和len相同,可以是变量
s := make([]int, 5)       //{0,0,0,0,0}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)    //len=5, cap=5, {0,0,0,0,0}

s2 := make([]int, 5, 10)    //{0,0,0,0,0}
fmt.Printf("len=%d cap=%d %v\n", len(s2), cap(s2), s2)     //len=5, cap=10, {0,0,0,0,0}
  • slice还能包含slice,也就是二维slice
  • 以下直接给出官方完整例子
package main

import (
	"fmt"
	"strings"
)

func main() {
	// Create a tic-tac-toe board.
	board := [][]string{
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
		[]string{"_", "_", "_"},
	}

	// The players take turns.
	board[0][0] = "X"
	board[2][2] = "O"
	board[1][2] = "X"
	board[1][0] = "O"
	board[0][2] = "X"

	for i := 0; i < len(board); i++ {
		fmt.Printf("%s\n", strings.Join(board[i], " "))
	}
}
  • 还可以用append方法来为slice添加元素
var s []int
s = append(s, 0)
s = append(s, 1, 2)
s = append(s, 3, 4, 5, 6)
  • 关于slice的更多内容,还是建议看看https://blog.golang.org/go-slices-usage-and-internals

range

  • range一般中文翻译作“范围”
  • 会在for语句中使用,要结合for循环来理解
  • 每次for循环迭代时,range 数组或者slice,会往后跑一位,返回位置和数值
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

for i, v := range pow {
	fmt.Printf("2**%d = %d\n", i, v)
}
  • 位置或数值是可以省略的,省略的方式有点不同
pow := make([]int, 10)
for i := range pow {
	pow[i] = 1 << uint(i) // == 2**i
}
for _, value := range pow {
	fmt.Printf("%d\n", value)
}

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Go语言环境安装发布时间:2022-07-10
下一篇:
ROS2GO 与WIN10 双系统安装发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap