Go语言的指针

认识指针地址和指针类型:

Go语言中使用“&”操作符放在变量前面对变量进行“取地址”操作

格式如下:

ptr := &v        //v的类型为T

其中v代表被取地址的变量,被取地址的v使用ptr变量进行接收,ptr的类型就为“*T”,称做T的指针类型。“*”代表指针。

代码如下:

package main

import(
  "fmt"
)

func main(){
  var cat int=1
  var str string ="banana"
  fmt.Printf("%p %p\n", &cat, &str)
}

结果如下:

0xc000018060 0xc000010230

使用fmt.Printf的动词“%p”输出cat和str变量取地址后的指针值,指针值带有“0x”的十六进制前缀。

从指针获取指针指向的值:

代码如下:

package main

import(
  "fmt"
)

func main(){
  //准备一个字符串类型
  var house ="Malibu Point 10880,90265"
  
  //字符串取地址,ptr类型为*string
  ptr:=&house
  
  //打印ptr的类型
  fmt.Printf("ptr type: %T\n",ptr)
  
  //打印ptr的指针类型
  fmt.Printf("address: %p\n",ptr)
  
  //对指针进行取值操作
  value:=*ptr
  
  //取值后的类型
  fmt.Printf("value type: %T\n",value)
  
  //指针取值后就是指向变量的值。
  fmt.Printf("value: %s\n",value)
}

结果如下:

ptr type: *string
address: 0xc000010230
value type: string
value: Malibu Point 10880,90265

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

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

●对变量进行取地址(&)操作,可以获得这个变量的指针变量。

●指针变量的值是指针地址。

●对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。

使用指针修改值:

代码如下:

package main

import(
  "fmt"
)
//交换函数
func swap(a,b *int){
  //取a指针的值,赋给临时变量t
  t:=*a
  //取b指针的值,赋给a指针指向的变量
  *a=*b
  //将a指针的值赋给b指针指向的变量
  *b=t
}
func main(){
  //准备两个变量,赋值1和2
  x,y:=1,2
  //交换变量值
  swap(&x,&y)
  //输出变量值
  fmt.Println(x,y)
}

运行结果如下:

2 1

代码如下:

package main

import(
  "fmt"
)
func swap(a,b *int){
  b,a=a,b
}
func main(){
  x,y:=1,2
  swap(&x,&y)
  fmt.Println(x,y)
}

运行结果如下:

1 2

 使用指针变量获取命令行的输入信息:(不能理解,没看懂)

代码如下:

package main

import(
  "fmt"
  "flag"
)
var mode = flag.String("mode","","process mode")
func main(){
  flag.Parse()
  fmt.Println(*mode)
}

在终端输入:

go run main.go --mode=fast

结果如下:

fast

创建指针的另一个方法----new()函数

格式如下:

new (类型)

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

指针变量都是一个内存位置,每个内存位置都有其定义的地址,可以使&运算符来访问它,这个运算符表示内存中的地址。

打印变量a的地址,代码如下:

package main

import (
	"fmt"
)

func main() {
	a := 10
	fmt.Printf("%x\n", &a)
}

运行结果如下:

c000018070

指针是一个变量,其值是另一个变量的地址,即存储器位置的直接地址。

指针变量声明的一般形式是:

var name *type

使用指针基本上是三个步骤:定义一个指针变量,将一个变量的地址赋值给一个指针,最后访问指针变量中可用地址的值。

代码如下:

package main

import (
	"fmt"
)

func main() {
	a := 20
	ap := &a
	fmt.Printf("a的地址:%x\n", &a)
	fmt.Printf("ap的地址:%x\n", ap)
	fmt.Printf("*ap的地址:%x\n", *ap)
}

运行结果如下:

a的地址:c000018070
ap的地址:c000018070
*ap的地址:14

Go语言指针不支持指针运算,也不支持->运算符。

1、nil指针

Go语言编译器为指针变量分配一个nil值,以防指针没有确切的地址分配,这是在变量声明的时候完成的。

代码如下:

package main

import (
	"fmt"
)

func main() {
	var ptr *int
	fmt.Printf("ptr的值是:%x\n", ptr)
}

运行结果如下:

ptr的值是:0

要检查是否为nil指针,可以使用if语句,如下所示:

if(ptr!=nil)
if(ptr==nil)

2、指针的指针

指针的指针声明方式,代码如下:

package main

import (
	"fmt"
)

func main() {
	var a *int
	aP := &a
	fmt.Printf("a-->nil: %x\n", a)
	fmt.Printf("aP-->a: %x\n", aP)
	fmt.Printf("aP-->a-->nil(指针aP指向的指针a的内存地址): %x\n", *aP)
	fmt.Printf("&aP-->aP(表示aP在内存中的地址): %x\n", &aP)
}

运行结果如下:

a-->nil: 0
aP-->a: c00012c018
aP-->a-->nil(指针aP指向的指针a的内存地址): 0
&aP-->aP(表示aP在内存中的地址): c00012c020

代码如下:

package main

import (
	"fmt"
)

func main() {
	a := 10
	aP := &a
	aPP := &aP
	fmt.Printf("a: %d\n", a)
	fmt.Printf("aP: %x\n", aP)
	fmt.Printf("*aP: %d\n", *aP)
	fmt.Printf("aPP: %x\n", aPP)
	fmt.Printf("*aPP: %x\n", *aPP)
	fmt.Printf("**aPP: %d\n", **aPP)
}

运行结果如下:

a: 10
aP: c000018070
*aP: 10
aPP: c00000e028
*aPP: c000018070
**aPP: 10

3、指针数组

代码如下:

package main

import (
	"fmt"
)

func main() {
	const MAX int = 3
	a := []int{10, 100, 200}
	var ptr [MAX]*int
	for i := 0; i < MAX; i++ {
		ptr[i] = &a[i]
		fmt.Printf("a[%d]的地址:%d\n", i, ptr[i])
	}
	for i := 0; i < MAX; i++ {
		fmt.Printf("a[%d]的地址:%d\n", i, *ptr[i])
	}
}

运行结果如下:

a[0]的地址:824633843904
a[1]的地址:824633843912
a[2]的地址:824633843920
a[0]的地址:10
a[1]的地址:100
a[2]的地址:200

4、传递给函数

Go语言允许传递指针到函数中,只需将函数的参数声明为指针类型。

代码如下:

package main

import (
	"fmt"
)

func main() {
	a := 100
	b := 200
	fmt.Printf("交换之前a的值为:%d\n", a)
	fmt.Printf("交换之前b的值为:%d\n", b)

	swap(&a, &b)

	fmt.Printf("交换之后a的值为:%d\n", a)
	fmt.Printf("交换之后b的值为:%d\n", b)
}

func swap(x *int, y *int) {
	var temp int
	temp = *x
	*x = *y
	*y = temp
}

运行结果如下:

交换之前a的值为:100
交换之前b的值为:200
交换之后a的值为:200
交换之后b的值为:100

new()和make()的区别

new()用于值类型的内存分配,并且轩为零值。

make()只用于切片、字典以及通道这三种引用数据类型的内存分配和初始化。

new(T)分配类型T的零值并返回其地址,也就是指向类型T的指针。

make(T)返回类型T的值(不是*T)。

代码如下:

package main

import (
	"log"
	"runtime"
	"time"
)

type Person struct {
	Name string
	Age  int
}

func (p *Person) Close() {
	p.Name = "NewName"
	log.Println(p)
	log.Println("Close")
}

func (p *Person) NewOpen() {
	log.Println("Init")
	runtime.SetFinalizer(p, (*Person).Close)
}

func Tt(p *Person) {
	p.Name = "NewName"
	log.Println(p)
	log.Println("Tt")
}

// 查看内存情况
func Mem(m *runtime.MemStats) {
	runtime.ReadMemStats(m)
	log.Printf("%d Kb\n", m.Alloc/1024)
}

func main() {
	var m runtime.MemStats
	Mem(&m)

	var p *Person = &Person{Name: "lee", Age: 4}
	p.NewOpen()
	log.Println("Gc完成第一次")
	log.Println("p:", p)
	runtime.GC()
	time.Sleep(time.Second * 5)
	Mem(&m)

	var p1 *Person = &Person{Name: "Goo", Age: 9}
	runtime.SetFinalizer(p1, Tt)
	log.Println("Gc完成第二次")
	time.Sleep(time.Second * 2)
	runtime.GC()
	time.Sleep(time.Second * 2)
	Mem(&m)

}

运行结果如下:

PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go> go run main.go
2022/08/19 14:10:15 108 Kb
2022/08/19 14:10:15 Init
2022/08/19 14:10:15 Gc完成第一次
2022/08/19 14:10:15 p: &{lee 4}
2022/08/19 14:10:15 &{NewName 4}
2022/08/19 14:10:15 Close
2022/08/19 14:10:20 99 Kb
2022/08/19 14:10:20 Gc完成第二次
2022/08/19 14:10:23 &{NewName 9}
2022/08/19 14:10:23 Tt
2022/08/19 14:10:25 99 Kb
PS C:\Users\a-xiaobodou\OneDrive - Microsoft\Projects\Go>

你可能感兴趣的:(Go语言,golang,开发语言,后端)