GO指针、值类型的区别整理

定义:
A方法传一个参数p 至B方法后,当B方法改变了p的值

  1. A方法中p值也改变了就了传指针
  2. A方法中p值没改变就是传值

值类型的变量:
值类型的变量是直接指向内存中的值,值都是存储在栈中,当用等号进行赋值时,实际上是进行了一次拷贝, 如 i = j 实际上是进行了一次拷贝。
GO中值类型有int ,float,bool,string等,除了数组、切片、结构体、channel外都是值类型

& 操作符会生成一个指向其操作数的指针。
* 操作符表示指针指向的底层值。

package main

import (
	"fmt"
)
func main() {
	fmt.Println("#### 基本类型实验")
	i := "4333qw"
	fmt.Println("i = ",i)
	fmt.Println("&i = ",&i)
	fmt.Println("*&i = ",*&i)
}

运行结果:

#### 基本类型实验
i =  9999yt
&i =  0xc00000e1e0
*&i =  9999yt

基本类型赋值
实验内容:两个基本类型之间赋值后,其中一个修改不影响另一个,他们的指针地址是不同的,是存在两个地址空间

package main

import (
	"fmt"
)

func main() {
	fmt.Println("#### 基本类型赋值后的实验")
	i := "4333qw"
	j := "9999yt"

	i = j

	fmt.Println("i = ",i)
	fmt.Println("j = ",j)

	fmt.Println("&i = ",&i)
	fmt.Println("&j = ",&j)
}

引用类型赋值
实验内容:两个引用类型之间赋值后,改变其中一个值另一个值也会跟着改变,这是指针赋值,两个指针指向的是同一块内存地址
从实验可以看出 user1 = user2 的结果是user1和user2指向相同的成员变量空间,如下面代码运行结果显示,user1,user2指向的name,age的存储空间地址是相同的,所以user1.name改变后user2.name也会根着变

package main
import (
	"fmt"
)
func main() {
	fmt.Println()
	fmt.Println("#### 引用类型实验")
	user1 := new(User)
	user1.name = "wq"
	user1.age = 33
	user2 := new(User)
	user2.name = "lq"
	user2.age = 22

	user1 = user2
	user1.name = "zs"
	fmt.Println("user1.name ",user1)
	fmt.Println("user2.name ",user2)

	fmt.Println("&user1 ",&user1)
	fmt.Println("&user2 ",&user2)

	fmt.Println("&user1.name ",&user1.name)
	fmt.Println("&user2.name ",&user2.name)
	fmt.Println("&user1.age ",&user1.age)
	fmt.Println("&user2.age ",&user2.age)
}

type User struct {
	name string
	age int
}

运行结果:

#### 引用类型实验
user1.name  &{zs 22}
user2.name  &{zs 22}
&user1  0xc00000c030
&user2  0xc00000c038
&user1.name  0xc00000a0a0
&user2.name  0xc00000a0a0
&user1.age  0xc00000a0b0
&user2.age  0xc00000a0b0

复合引用类型的赋值
实验内容:引用类型嵌套引用类型,相互赋值后引用类型成员变量也同样是指向相同的地址空间
从实验可以看出 user1 = user2 的结果是user1和user2指向相同的成员变量空间,如下面代码运行结果显示,user1.cy,user2.cy指向的存储空间地址是相同的,所以user1.cy.name改变后user3.cy.name也会根着变

package main
import (
	"fmt"
)
func main() {
	fmt.Println()
	fmt.Println("#### 复合引用类型实验")
	user1 := new(User)
	user1.name = "wq"
	user1.age = 33
	user1.cy.name = "shanghai"
	user1.cy.area = 7000000
	user2 := new(User)
	user2.name = "lq"
	user2.age = 22
	user2.cy.name = "shenzheng"
	user2.cy.area = 7000000

	user1 = user2
	user1.cy.name = "bj"

	fmt.Println("user1.cy.name ",user1.cy.name)
	fmt.Println("user2.cy.name ",user2.cy.name)

	fmt.Println("&user1.cy ",&user1.cy)
	fmt.Println("&user2.cy ",&user2.cy)

	fmt.Println("&user1.cy.name ",&user1.cy.name)
	fmt.Println("&user2.cy.name ",&user2.cy.name)

	fmt.Println("&user1.cy.area ",&user1.cy.area)
	fmt.Println("&user2.cy.area ",&user2.cy.area)
}

运行结果:

#### 复合引用类型实验
user1.cy.name  bj
user2.cy.name  bj
&user1.cy  &{bj 7000000}
&user2.cy  &{bj 7000000}
&user1.cy.name  0xc0000621c8
&user2.cy.name  0xc0000621c8
&user1.cy.area  0xc0000621d8
&user2.cy.area  0xc0000621d8

奇怪现现解析
碰到个奇怪的问题,一下掉到思维陷进了,其实并不是很复杂,下去抽根烟上来就想通了。
现像描述:
有个切片引用类型变量slice1, 分别传入两个方法,方法1改变了slice1外面没变,方法2改变了slice1外面变了,这纠竟是为什么呢?先上代码

package main
import (
	"fmt"
)
func main() {
	slice1 := []string{"zhang", "san"}
	fmt.Printf("&slice1 %p\n", &slice1)
	modify1(slice1)
	fmt.Println(slice1)
	fmt.Println("----------------")
	modify2(slice1)
	fmt.Println(slice1)
}
func modify1(data []string) {
	fmt.Printf("modify1 &data %p\n",&data)
	data = nil
}
func modify2(data []string) {
	fmt.Printf("modify2 &data %p\n",&data)
	data[1] = "lisi"
}

运行结果:

&slice1 0xc000088020
modify1 &data 0xc000088060
[zhang san]
------------------------------
modify2 &data 0xc0000880a0
[zhang lisi]

问题:上述代码中modify1执行后slice1为什么没有变成nil呢?难道传的不是引用吗?modify2改变后外面也改变了,对于引用类型来说,这个是正常的。

解析:其实上面的代码打印过程已经给出了答案,引用类型传过去的也是值,只不过这个值是指向存储空间的指针,这个指针会在内存中开辟一块新的空间来存储。所以上面运行结果中主方法中的slice1、modify1中的data、modify2中的data的地址是不一样的,但是他们所指向的存储空间是想同的。
接下来就是问题的重点了,为啥modify1中的data=nil没有影响到外面的slice1呢,其实很简单,下面我画个图来说明这个问题

在这里插入图片描述

上图可以看出, modify1=nil只是切断了modify中的data与存储空间的引用关系,但并不影响外面的slice1

猜你喜欢

转载自blog.csdn.net/wangqiang9x/article/details/86214336