zoukankan      html  css  js  c++  java
  • Golang的高级数据类型-指针(Pointer)实战篇

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

                                   作者:尹正杰

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

     

     

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

      

    一.变量地址和指针

    1>.定义指针变量

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        /**
        在Go语言中,一旦定义了一个变量就会在内存中开辟空间。因此,在Go语言中为了避免空间的浪费,定义变量若未使用就会编译报错。
        */
        var age uint8 = 18
        /**
        我们可以使用取地址运算符("&")来获取数据的内存地址。
        %p:
            是一个占位符,表示输出一个十六进制地址格式。
        
    :
            表示换行符。
        */
        fmt.Printf("age的值是:[%d],age的内存地址是:[%p]
    ", 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
    ", &nameList)
        fmt.Printf("nameList数组中第一个元素的地址为%p
    ", &nameList[0])
        fmt.Printf("nameList数组中第二个元素的地址为%p
    ", &nameList[1])
        fmt.Printf("nameList数组中第三个元素的地址为%p
    
    ", &nameList[2])
    
        //定义一个整型数组
        var age_list [56]int
        fmt.Printf("age_list数组的内存地址为%p
    ", &age_list)
        fmt.Printf("age_list数组中第一个元素的地址为%p
    ", &age_list[0])
        fmt.Printf("age_list数组中第二个元素的地址为%p
    ", &age_list[1])
        fmt.Printf("age_list数组中第三个元素的地址为%p
    
    ", &age_list[2])
    
        //定义一个整型切片
        age_slice := make([]int, 56)
        /**
        age_slice切片变量保存的是存储切片容器的地址。他并不像数组那样直接指向数组的首元素地址,它类似于一个二级指针。
        当我们使用取地址运算符对age_slice变量取地址时并不是取的切片真正存储切片容器的地址,而是存储切片容器地址变量本身的地址哟~
        */
        fmt.Printf("age_slice切片变量本身的内存地址为%p
    ", &age_slice)
    
        /**
        通过下标访问第一个元素其实访问的是age_slice变量中保存的内存地址对应的下标。
        */
        fmt.Printf("age_slice切片中第一个元素的地址为%p
    ", &age_slice[0])
        fmt.Printf("age_slice切片中第二个元素的地址为%p
    ", &age_slice[1])
        fmt.Printf("age_slice切片中第三个元素的地址为%p
    
    ", &age_slice[2])
    }

    二.指针的使用

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

    package main
    
    import (
        "fmt"
    )
    
    func main() {
    
        //使用自动推导类型创建一个age变量
        age := 18
    
        //我们将age变量的内存地址赋值给指针变量p1
        p1 := &age
    
        /**
        我们可以使用取值运算符("*")来获取指针变量的值。
        %p:
            是一个占位符,表示输出一个十六进制地址格式。
        %d:
            是一个占位符,表示输出一个整型。
        
    :
            表示换行符。
        *:
            取值运算符,可以将指针变量中的保存的数据取出来。换句话说,可以将一段内存地址保存的值取出来。
        */
        fmt.Printf("age的内存地址是:%p,age的值是:%d
    ", p1, *p1)
    
        //我们可以通过指针间接修改变量的值
        *p1 = 27
        fmt.Printf("age的内存地址是:%p,age的值是:%d
    ", 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
    ", 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
    ", p1, *p1)
    
        /**
        方案二:
            使用new函数,比如"new(int)"表示创建一个int大小的内存空间,返回为*int
        */
        p2 = new(int)
        fmt.Printf("p2的内存地址是:%p,p2的值是:%d
    ", p2, *p2)
        *p2 = 2020 //为指针变量赋值
        fmt.Printf("p2的内存地址是:%p,p2的值是:%d
    ", 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]
    ", a, b)
    
        /*
            指针变量作为函数参数传递的时候是引用传递
        */
        swapPointer(&a, &b)
        fmt.Printf("a = [%d], b = [%d]
    ", 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]
    ", s1.Name, s1.Age)
        /**
        结构体对象是值传递,但一般函数传递的都是结构体对象指针
        */
        addAgePointer(&s1)
        fmt.Printf("姓名:[%s],年龄[%d]
    ", s1.Name, s1.Age)
    }

     

  • 相关阅读:
    【bzoj2500】幸福的道路 树形dp+单调队列
    【ARC069F】Flags 2-sat+线段树优化建图+二分
    【bzoj2437】[Noi2011]兔兔与蛋蛋 二分图最大匹配+博弈论
    剑指offer——树的子结构
    剑指offer——反转链表
    腾讯算法岗面试算法题——计数排序
    作业帮面试题
    剑指offer——重建二叉树
    剑指offer——二维数组中的查找
    删除链表中重复的结点
  • 原文地址:https://www.cnblogs.com/yinzhengjie2020/p/12334229.html
Copyright © 2011-2022 走看看