Golang的高级数据类型-指针(Pointer)实战篇

          Golang的高级数据类型-指针(Pointer)实战篇

                               作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

 

 

  前面分享过存储数据的方式,可以通过变量,或者复合类型中的数组,切片,Map,结构体等进行存储。今天我们来分享一下Go语言中的指针操作。

  

一.变量地址和指针

1>.定义指针变量

package main

import (
    "fmt"
)

func main() {
    /**
    在Go语言中,一旦定义了一个变量就会在内存中开辟空间。因此,在Go语言中为了避免空间的浪费,定义变量若未使用就会编译报错。
    */
    var age uint8 = 18
    /**
    我们可以使用取地址运算符("&")来获取数据的内存地址。
    %p:
        是一个占位符,表示输出一个十六进制地址格式。
    \n:
        表示换行符。
    */
    fmt.Printf("age的值是:[%d],age的内存地址是:[%p]\n", age, &age)

    /*
        接下来我们定义一个uint8类型的指针变量p1,其实指针变量也是变量,只不过指针变量指向了变量的内存地址。
    */
    var p1 *uint8

    /*
        对age变量取地址并将结果赋值给指针变量。
    */
    p1 = &age
    fmt.Println(p1)
}

2>.数组和切片指针的区别

package main

import (
    "fmt"
)

func main() {
    //定义一个字符串数组
    var nameList [56]string
    fmt.Printf("nameLists数组的内存地址为%p\n", &nameList)
    fmt.Printf("nameList数组中第一个元素的地址为%p\n", &nameList[0])
    fmt.Printf("nameList数组中第二个元素的地址为%p\n", &nameList[1])
    fmt.Printf("nameList数组中第三个元素的地址为%p\n\n", &nameList[2])

    //定义一个整型数组
    var age_list [56]int
    fmt.Printf("age_list数组的内存地址为%p\n", &age_list)
    fmt.Printf("age_list数组中第一个元素的地址为%p\n", &age_list[0])
    fmt.Printf("age_list数组中第二个元素的地址为%p\n", &age_list[1])
    fmt.Printf("age_list数组中第三个元素的地址为%p\n\n", &age_list[2])

    //定义一个整型切片
    age_slice := make([]int, 56)
    /**
    age_slice切片变量保存的是存储切片容器的地址。他并不像数组那样直接指向数组的首元素地址,它类似于一个二级指针。
    当我们使用取地址运算符对age_slice变量取地址时并不是取的切片真正存储切片容器的地址,而是存储切片容器地址变量本身的地址哟~
    */
    fmt.Printf("age_slice切片变量本身的内存地址为%p\n", &age_slice)

    /**
    通过下标访问第一个元素其实访问的是age_slice变量中保存的内存地址对应的下标。
    */
    fmt.Printf("age_slice切片中第一个元素的地址为%p\n", &age_slice[0])
    fmt.Printf("age_slice切片中第二个元素的地址为%p\n", &age_slice[1])
    fmt.Printf("age_slice切片中第三个元素的地址为%p\n\n", &age_slice[2])
}

 

二.指针的使用

1>.通过指针间接修改变量的值

package main

import (
    "fmt"
)

func main() {

    //使用自动推导类型创建一个age变量
    age := 18

    //我们将age变量的内存地址赋值给指针变量p1
    p1 := &age

    /**
    我们可以使用取值运算符("*")来获取指针变量的值。
    %p:
        是一个占位符,表示输出一个十六进制地址格式。
    %d:
        是一个占位符,表示输出一个整型。
    \n:
        表示换行符。
    *:
        取值运算符,可以将指针变量中的保存的数据取出来。换句话说,可以将一段内存地址保存的值取出来。
    */
    fmt.Printf("age的内存地址是:%p,age的值是:%d\n", p1, *p1)

    //我们可以通过指针间接修改变量的值
    *p1 = 27
    fmt.Printf("age的内存地址是:%p,age的值是:%d\n", p1, age)
}

2>.使用指针注意事项

package main

import (
    "fmt"
)

func main() {

    /**
    温馨提示:
        在使用指针变量时,要注意定义指针默认值为nil,如果直接擦欧总指向nil的内存地址会报错;
        在其它语言中,比如c++,指针是允许运算的,但是在Go语言中,指针只有取地址运算符和取值运算符,不允许指针参与运算哟~
    */
    var p *int

    /**
    错误代码:
        由于p指针变量默认值是nil,因此会报错"invalid memory address or nil pointer dereference"
    */
    //*p = 123

    age := 18
    p = &age //所以,在使用指针变量时,一定要让指针变量有正确的指向。
    fmt.Printf("age的内存地址是:%p,age的值是:%d\n", p, *p)

    /**
    错误代码:
        在Go语言中,指针只有取地址运算符和取值运算符。
    */
    //p2 := p + 100
}

 

三.指针变量赋值

package main

import (
    "fmt"
)

func main() {
    /**
    为指针赋值有两种方法:
        1>.把同类型的变量地址赋值给指针;
        2>.使用new函数,就是申请一片内存空间,返回地址;
    */
    var (
        p1 *int
        p2 *int
    )
    fmt.Println("p1的默认值是:", p1)
    fmt.Println("p2的默认值是:", p2)

    /**
    方案一:
        定义一个同类型的变量,然后取地址赋值给指针变量。
    */
    age := 18
    p1 = &age
    fmt.Printf("p1的内存地址是:%p,p1的值是:%d\n", p1, *p1)

    /**
    方案二:
        使用new函数,比如"new(int)"表示创建一个int大小的内存空间,返回为*int
    */
    p2 = new(int)
    fmt.Printf("p2的内存地址是:%p,p2的值是:%d\n", p2, *p2)
    *p2 = 2020 //为指针变量赋值
    fmt.Printf("p2的内存地址是:%p,p2的值是:%d\n", p2, *p2)
}

 

四.指针的应用场景(指针变量作为函数参数传递的时候是引用传递)

1>.交换两个变量的值案例

package main

import "fmt"

/*
    定义一个函数交换2个变量的值,接收的是2个int变量
*/
func swap(x int, y int) {
    x, y = y, x
}

/*
    定义一个函数交换2个变量的值,接收的是2个int指针变量
*/
func swapPointer(x *int, y *int) {
    *x, *y = *y, *x //需要改变的是地址上存储的内容
}

func main() {
    /*
        定义2个变量,一会用于在函数中交换两个变量的值
    */
    var (
        a = 100
        b = 200
    )

    /*
        值传递,不能通过函数内部修改影响外部变量。
    */
    swap(a, b)
    fmt.Printf("a = [%d], b = [%d]\n", a, b)

    /*
        指针变量作为函数参数传递的时候是引用传递
    */
    swapPointer(&a, &b)
    fmt.Printf("a = [%d], b = [%d]\n", a, b)
}

2>.结构体传参案例

package main

import (
    "fmt"
)

type Student struct {
    Name string
    Age  int
}

func addAge(s Student) {
    s.Age += 1
}

/**
在实际开发中,一般传递的结构体对象都是指针传递
*/
func addAgePointer(s *Student) {
    s.Age += 2
}

func main() {

    s1 := Student{"Jason Yin", 18}
    addAge(s1)
    fmt.Printf("姓名:[%s],年龄[%d]\n", s1.Name, s1.Age)
    /**
    结构体对象是值传递,但一般函数传递的都是结构体对象指针
    */
    addAgePointer(&s1)
    fmt.Printf("姓名:[%s],年龄[%d]\n", s1.Name, s1.Age)
}

 

 

posted @ 2020-02-20 07:00  JasonYin2020  阅读(1210)  评论(0编辑  收藏  举报