Skip to main content

pointer

前言

每个变量除了有变量名 变量值 还有内存地址

指针本质就是内存地址

指针在C, C++, C#是非常重要的概念,由于指针可以进行运算( ++ -- 平移),利用指针我们可以用非常简短的代码完成很多骚操作

pointer in golang

golang中的指针并没有C那么强大,它不能进行算术运算

取地址,变量前加&即可

package main

import "fmt"

func main() {
name:="飞雪无情"
nameP:=&name //取地址
fmt.Println("name变量的值为:",name)
fmt.Println("name变量的地址为:",nameP)
}

下图演示指针变量 普通变量 内存地址 和 内存之间的关系:

指针是非常廉价的,占用的资源非常少

在golang中使用类型名称前加*的方式即可表示一个对应的指针类型,eg: *string,*int

不同指针类型无法相互赋值:比如你不能对一个string类型的变量取地址,然后赋值给*int的指针类型,这时编译器会告诉你无法进行类型转换

name := "scott"
var intP *int
intP = &name //指针类型不同,无法赋值

需要注意的是用var关键字来定义指针变量的时候是不能直接赋值和取值的,因为这时它仅仅是一个变量,还没有对应的内存地址,它的值为nil

和普通类型不一样的是,指针类型还可以通过new关键字来声明,如下代码所示:

intP:= new(int)

内置的new函数,它有个参数,你可以传递一个类型给它,它返回的就是一个对应的指针类型

指针的操作

  • 获取指针指向的值
  • 修改指针指向的值
package main

import "fmt"

func main() {
name:="Scott"
var p = new(string)
p = &name
fmt.Println("获取指针变量的值:",*p)
*p = "scott123"
fmt.Println("修改指针变量的值:",*p)
}

指针参数

我们来看一个例子:

package main

import "fmt"

func main() {
age := 18
modifyAge(age)
fmt.Println("age:",age) //18
}

func modifyAge(age int) {
age = 20
}

为什么age修改不了,因为age实参只是拷贝而已,就是值传递

如果你想修改age,则需要传入指针

改造代码,再试,发现修改成功了

package main

import "fmt"

func main() {
age := 18
modifyAge(&age)
fmt.Println("age:",age) //20
}

func modifyAge(age *int) {
*age = 20
}

在函数中,通过形参去改变实参值的时候,你就需要指针类型的参数

指针的接收者

对于是否使用指针类型作为接收者,有以下几点参考

因为内存的拷贝很廉价,效率会比较高

指针注意事项

在golang中使用指针,需要注意以下细节:

1,可以通过指针改变指向值

package main

import (
"fmt"
)

func main() {
var a int = 10
fmt.Println(a) //10

var ptr *int = &a
*ptr++
fmt.Println(a) //11

*ptr = 100
fmt.Println(a) //100
}

2,指针变量一定接受的是地址值

3,指针变量的类型不可以不匹配

4,基本数据类型(又叫值类型),都有对应的指针类型,形式为*数据类型: 比如int对应的指针就是*int,float32对应的指针就是*float32,以此类推

指针在golang中的好处:

  1. 修改指向数据的值
  2. 在变量赋值,参数传递的时候节约内存

go语言对指针做了诸多限制,eg 不能对指针进行运算,不能对常量使用指针

什么情况下使用指针

指针通过.是可以直接访问成员变量的

举例如下:

type A struct{
b string
c int
}

a := &A{}
fmt.Println(a.b) //这里是不会报错的

指针在访问内部成员时,编译器会自动帮我们转化为值类型,所以你写成a.b程序不会报错, 但是在C C++中,你必须写成a->b才行