Go

Go语言学习笔记(五)-其他类型(指针、结构体、、)

2017-03-21  本文已影响153人  EvansChang

指针

在Go语言中也存在指针,且指针保存了变量的地址,初始值为nil。定义指针与定义变量相似,不同的是在类型前面指针需要加*例如:

var p *int   //此处定义了一个int类型的指针

指针也可以通过已有变量获取,通过&操作符便可,与C语言相似Go中*指针名指向变量底层,但是不同的是Go中没有指针运算。示例代码如下:

package main
import "fmt"
func main(){
    i,j := 10,1000
    var p *int //定义一个int指针
    p = &i  //从i变量中获取指向i的指针并赋值给p
    fmt.Println(i)
    *p = 21  //修改p指针指向的底层存储的数据
    fmt.Println(i)
    p = &j
    *p = *p/10
    fmt.Println(j)
}

结构体

结构体就是一些字段的集合,通过type来声明定义类型,并用struct来声明结构体。具体实现如下:

package main
import "fmt"
type TestType struct{
    x int
    y int
}
func main(){
    p := TestType{1,2}
    fmt.Println(p)
    p.x = 4
    fmt.Println(p)
}

从上述示例代码中可以看出结构体的字段通过点号来访问。
结构体还可以通过指针来访问,若一个结构体指针为p那么我们可以通过(&p).x来调用结构体的字段或者使用隐式间接引用,直接写p.x就可以。结构体文法可以通过直接列出字段的值来新分配一个结构体,也可以通过Name:语法列出部分字段。

数组

类型[n]T表示拥有n个T类型的值得数组。数组的长度是数组的一部分,所以数组不能改变大小。
为了解决这个限制,Go推出了切片这一类型,切片为数组元素提了供动态大小,灵活的视角,比数组更实用。切片通过[]T来定义。切片不存储任何数据,它只是描述了底层数组中的一段,或者我们也可以理解为切片其实相当于对数据中的一段的每一个元素都获取了一个指针对象存放到另一数组中。创建数组与切片的方法如下:

package main
import "fmt"
func main(){
    //创建一个数组并设置初始值Go自动推导类型
    myArray := [10]int{1,2,3,4,5,6,7,8,9,10}
    fmt.Println(myArray)
    //创建一个数组设置初始值
    var myArray1 [5]int = [5]int{6,5,4,3,2}
    fmt.Println(myArray1)
    //创建一个切片初始元素个数为5,默认值为0,预留10个存储空间
    mySlice := make([]int,5,10)
    fmt.Println(mySlice)
    //从数组上直接创建一个切片,初始元素个数与数组相同
    mySlice2 := []int{1,2,3,4,5}
    fmt.Println(mySlice2)
    //穿件一个切片元素个数为6默认值为0
    mySlice3 := make([]int,6)
    fmt.Println(mySlice3)
}

通过make方式来创建的切片也是创建动态数组的方法。
在进行切片时,可以利用它的默认行为来忽略上下界,切片的下界默认为0,上界为该切片的长度。例如:

package main
import "fmt"
func main(){
    var a [10]int = {1,2,3,4,5,6,7,8,9,10}
    fmt.Println(a[0:10])
    fmt.Println(a[:10])
    fmt.Println(a[:10])
    fmt.Println(a[:])
}

以上四个切片是等价的

切片拥有容量与长度两个属性,长度就是包含的元素个数,容量就是从第一个元素开始到底层数组元素的末尾的个数,长度与容量分别用len(s)和cap(s)来获取。切片的零值为nil,nil切片的容量与长度为0,且没有底层数组。
切片可以包含任何类型,包括切片。
切片可以通过append来追加元素,该函数为自建函数,使用方式如下:

package main
import "fmt"
func main(){
    var s []int
    printSlice(s)
    //输出len=0 cap=0 []
    //追加一个元素
    s = append(s,0)
    printSlice(s)
    //输出len=0 cap=0 []
    //追加多个元素
    s = append(s,2,3,4,5)
    printSlice(s)
    //输出len=1 cap=1 [0]
}
func printSlice(s []int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

上述代码展示了append的两种使用方式,添加一个元素与添加多个元素

映射(map)

映射就是将键映射到值,映射的零值为nil,nil既没有键也不能添加键,make函数会返回给定类型的映射,并将其初始化备用。映射的文法与结构体相似,但是映射必须要有键名。若顶级类型只有一种类型,那么可以在文法元素中忽略。

package main

import "fmt"

type Vertex struct {
    Lat, Long float64
}

var m = map[string]Vertex{
    "Bell Labs": {40.68433, -74.39967},
    "Google":    {37.42202, -122.08408},
}

func main() {
    fmt.Println(m)
    //输出map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]

    //修改Bell Labs对应的元素
    m["Bell Labs"] = Vertex{2000,-74.39967}
    fmt.Println(m)
    //输出map[Bell Labs:{2000 -74.39967} Google:{37.42202 -122.08408}]
    
    //删除Bell Labs对应的元素
    delete(m,"Bell Labs")
    fmt.Println(m)
    //输出map[Google:{37.42202 -122.08408}]

    //通过双赋值来判断键是否存在
    v, ok := m["Google"]
    fmt.Println("The value:", v, "Google?", ok)
    //输出The value: {37.42202 -122.08408} Google? true
}

range

for循环的range形式可以遍历切片也可以遍历映射,for循环遍历切片时,每次遍历都会返回两个值,一个是元素的下标一个是元素的副本,通过_可以将下标或者值忽略,当只需要下标时可以直接去掉,value部分。

package main

import (
    "golang.org/x/tour/wc"
    "strings"
)

func WordCount(s string) map[string]int {
    res := make(map[string]int)
    strs := strings.Fields(s)
    for _,v:= range strs{
        res[v]++
    }
    return res
}

func main() {
    wc.Test(WordCount)
}

函数值与闭包

Go中函数也是值,也可以作为参数或者返回值。Go函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被“绑定”在了这些变量上。

package main

import "fmt"

func main() {
    var j int = 5
    //此处创建了一个匿名函数,返回值为函数值,并通过匿名函数后跟()来执行这个匿名函数将返回的函数值赋值给a变量
    a := func()(func()){
        var i int = 10
        return func(){
            fmt.Printf("i,j: %d , %d\n",i,j)
        }
    }()
    a()
    j *= 2
    a()
}
上一篇 下一篇

猜你喜欢

热点阅读