zoukankan      html  css  js  c++  java
  • 【go语言学习】指针pointer

    一、指针的概念

    1、什么是指针

    指针是存储另一个变量的内存地址的变量。
    变量是一种使用方便的占位符,用于引用计算机内存地址。
    一个指针变量可以指向任何一个值的内存地址。

    2、获取变量的地址

    Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var a int = 1
    	fmt.Println("变量a的内存地址是:", &a)
    }
    

    运行结果

    变量a的内存地址是: 0xc000012090
    
    3、声明指针
    var ptr *type
    

    type 为指针类型,ptr 为指针变量名,* 用于指定变量是作为一个指针。

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var a int = 1
    	fmt.Println("变量a的内存地址是:", &a)
    	var ptr *int
    	ptr = &a
    	fmt.Println("变量ptr的内存地址是:", &ptr)
    	fmt.Println("变量ptr的值为:", ptr)
    }
    

    运行结果

    变量a的内存地址是: 0xc000012090
    变量ptr的内存地址是: 0xc000006030
    变量ptr的值为: 0xc000012090
    
    4、空指针

    当一个指针被定义后没有分配到任何变量时,它的值为 nil。
    nil 指针也称为空指针。
    nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var ptr *int
    	fmt.Println("变量ptr的内存地址是:", &ptr)
    	fmt.Println("变量ptr的值为:", ptr)
    	fmt.Println(ptr == nil)
    }
    

    运行结果

    变量ptr的内存地址是: 0xc000006028
    变量ptr的值为: <nil>
    true
    

    二、指针的使用

    1、获取指针的值

    *ptr 访问指针变量指向的原变量的值

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var a int = 1
    	var ptr *int
    	ptr = &a
    	fmt.Println("a的内存地址是", ptr)
    	fmt.Println("a的值是", *ptr)
    }
    

    运行结果

    a的内存地址是 0xc000012090
    a的值是 1
    

    取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。

    变量指针地址指针变量取地址取值的相互关系和特性如下:

    • 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
    • 指针变量的值是指针地址。
    • 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。
    2、操作指针改变变量的值
    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var a int = 1
    	var ptr *int
    	ptr = &a
    	fmt.Println("a的内存地址是", ptr)
    	fmt.Println("a的值是", *ptr)
    
    	*ptr = 2
    	fmt.Println("a的值为", a)
    }
    

    运行结果

    a的内存地址是 0xc000012090
    a的值是 1
    a的值为 2
    
    3、指针数组和数组指针
    • 数组指针
      首先是一个指针,存放的是一个数组的地址
    *[len]type
    
    package main
    
    import "fmt"
    
    func main() {
    	arr := [4]int{1, 2, 3, 5}
    	fmt.Println(arr)
    	var ptr *[4]int
    	ptr = &arr
    	fmt.Println(ptr)
    	fmt.Printf("%p
    ", ptr) //数组arr的地址
    	fmt.Printf("%p
    ", &ptr) //指针变量ptr的地址
    	(*ptr)[0] = 100 //通过指针操作数组
    	fmt.Println(arr)
    	ptr[0] = 200 //简化写法
    	fmt.Println(arr)
    }
    

    运行结果

    [1 2 3 5]
    &[1 2 3 5]
    0xc0000103e0
    0xc000006030
    [100 2 3 5]
    [200 2 3 5]
    
    • 指针数组
      首先是一个数组,存储的数据类型是指针
    [len]*type
    
    package main
    
    import "fmt"
    
    func main() {
    	a, b, c, d := 1, 2, 3, 4
    	var arr [4]int
    	var ptr [4]*int
    	arr = [4]int{a, b, c, d}
    	ptr = [4]*int{&a, &b, &c, &d}
    	fmt.Println(arr)
    	fmt.Println(ptr)
    	*ptr[0] = 100   
    	fmt.Println(arr) // 数组中存储的是a的值1,所以改变a变量与arr无关
    	fmt.Println(a)
    	arr[0] = 200
    	fmt.Println(ptr)
    	fmt.Println(a) // 数组中存储的是a的值1,所以改变arr[0]与变量a无关
    }
    

    运行结果

    [1 2 3 4]
    [0xc000012090 0xc000012098 0xc0000120a0 0xc0000120a8]
    [1 2 3 4]
    100
    [0xc000012090 0xc000012098 0xc0000120a0 0xc0000120a8]
    100
    
    4、函数指针和指针函数
    • 函数指针
      一个指针,指向了一个函数
      go语言中,函数默认看做一个指针,没有*,引用类型的数据如map,slice,function都是如此。
    package main
    
    import "fmt"
    
    func main() {
    	var a func()
    	a = fun
    	a()
    }
    
    func fun() {
    	fmt.Println("hello world")
    }
    

    运行结果

    hello world
    
    • 指针函数
      一个函数,返回值是一个指针
    package main
    
    import "fmt"
    
    func main() {
    	arr := fun1()
    	fmt.Printf("%T, %p, %v
    ", arr, &arr, arr)
    	ptr := fun2()
    	fmt.Printf("%T, %p, %v
    ", ptr, &ptr, ptr)
    }
    
    func fun1() [4]int {
    	arr := [4]int{1, 2, 3, 4}
    	return arr
    }
    
    func fun2() *[4]int {
    	arr := [4]int{1, 2, 3, 4}
    	return &arr
    }
    

    运行结果

    [4]int, 0xc0000103e0, [1 2 3 4]
    *[4]int, 0xc000006030, &[1 2 3 4]
    
    5、指针传参

    Go 语言允许向函数传递指针,只需要在函数定义的参数上设置为指针类型即可。

    package main
    
    import "fmt"
    
    func main() {
    	arr := [4]int{1, 2, 3, 4}
    	fmt.Println("函数调用前arr:", arr)
    	fun1(arr)
    	fmt.Println("函数fun1调用后arr:", arr)
    
    	fun2(&arr)
    	fmt.Println("函数fun2调用后arr:", arr)
    }
    
    func fun1(arr [4]int) {
    	fmt.Println("fun1中数组的值:", arr)
    	arr[0] = 100
    	fmt.Println("fun1中数组的值:", arr)
    }
    
    func fun2(arr *[4]int) {
    	fmt.Println("fun2中数组的值:", arr)
    	arr[0] = 200
    	fmt.Println("fun2中数组的值:", arr)
    }
    

    运行结果

    函数调用前arr: [1 2 3 4]
    fun1中数组的值: [1 2 3 4]
    fun1中数组的值: [100 2 3 4]
    函数fun1调用后arr: [1 2 3 4]
    fun2中数组的值: &[1 2 3 4]
    fun2中数组的值: &[200 2 3 4]
    函数fun2调用后arr: [200 2 3 4]
    
    6、什么情况下使用指针
    • 推荐在方法上使用指针
    • 当结构体较大的时候使用指针会更高效,可以避免内存拷贝
    • 如果要修改结构体内部的数据或状态必须使用指针
    • 如果方法的receiver是map、slice 、channel等引用类型不要使用指针
    • 小数据类型如 bool、int 等没必要使用指针传递
    • 如果该函数会修改receiver或变量等,使用指针

    三、make和new

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	var a *int
            b := 10
    	*a = b  // 报错,a声明但是没有初始化,是nil,所以不能指向b。
            // a = &b //不报错,这里是把b的内存地址赋值给a,在赋值的同时给a分配内存空间
    	fmt.Println(a)
    }
    

    运行结果

    panic: runtime error: invalid memory address or nil pointer dereference
    

    执行上面的代码会引发panic,为什么呢?
    Go语言中对于引用类型的变量,在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。
    而对于值类型的变量不需要主动分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。
    Go语言中newmake是内建的两个函数,主要用来分配内存。

    new

    ptr := new(type)
    

    new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

    package main
    
    import (
    	"fmt"
    )
    
    func main() {
    	a := new(int)
    	b := new(bool)
    	fmt.Printf("a的类型是%T
    ", a)
    	fmt.Printf("b的类型是%T
    ", b)
    	fmt.Println(*a)
    	fmt.Println(*b)
    }
    

    运行结果

    a的类型是*int
    b的类型是*bool
    0
    false
    

    make

    make也是用于内存分配的,区别于new,它只用于slice、map以及chan的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。

    make(T, args)
    

    new与make的区别

    • 二者都是用来做内存分配的。
    • make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
    • new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
  • 相关阅读:
    CDH 2、Cloudera Manager的安装
    204 01 Android 零基础入门 03 Java常用工具类 04 Java集合 04 Map集合 01 Map概述
    203 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 07 删除宠物猫信息数据(引入泛型知识点)
    202 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 06 查找宠物猫信息数据
    201 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 05 添加重复的宠物猫信息数据
    200 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 04 添加和显式宠物猫信息
    199 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 03 宠物猫信息管理概述
    198 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 02 案例:在集合中插入字符串
    197 01 Android 零基础入门 03 Java常用工具类 04 Java集合 03 Set集合 01 Set概述
    196 01 Android 零基础入门 03 Java常用工具类 04 Java集合 02 List集合 05 案例:公告的删除和修改
  • 原文地址:https://www.cnblogs.com/everydawn/p/13903900.html
Copyright © 2011-2022 走看看