golang 11指针

214 阅读5分钟

一、指针

1、声明、赋值

v1 int;v21 指针,指向 v1; v22 int

v1 := 1     //int类型
v21 := &v1  
v22 := *v21 //int类型
输出:
1 0x1400012a060 1 1
int *int int

//v21 int类型指针

2、修改v22变量值

修改v22变量值,赋值

v1 := 1     //int类型
v21 := &v1  
v22 := *v21 //int类型 

v22 = 33
fmt.Println(v1, v21, *v21, v22)
fmt.Println(reflect.TypeOf(v1), reflect.TypeOf(v21), reflect.TypeOf(v22))
输出:
1 0x1400001a108 1 33
int *int int

赋值 修改v22,影响v22 值。

4、修改v1变量值

修改v1变量值,赋值

v1 := 1     //int类型
v21 := &v1  
v22 := *v21 //int类型

v1 = 44
fmt.Println(v1, v21, *v21, v22)
fmt.Println(reflect.TypeOf(v1), reflect.TypeOf(v21), reflect.TypeOf(v22))
输出:
44 0x140000a2060 44 1
int *int int

1、v21变量的声明,实际上创建变量v21、赋值给:类型 指针。指针本身没有值,所以不能通过表达式赋值给指针。但指针指向 内存地址 有值。

2、修改v1变量的值,实际上不会 ”直接“ 影响v21变量的值

3、但同时会修改v21 【指针】【指向】的值,v21指向的就是v1,改了v1 即修改了 v21指针指向的值。通俗点理解软链接 源文件被修改了

4、如何查看v21 指针 指向的【新】值,*v21 表达式可以取得 指针指向的值

二、函数&方法&指针

1、方法声明

在函数追加的【方法】声明时,在【函数名】【前】放一个变量表达式,即 为一个方法。这种写法即 定义了方法的声明,也叫【方法】的接收器。注意 接收器,只出现在 函数追加的方法上。

(p Point) 即构建了 类型 Point 的方法, 为 【所有】 【类型 Point】 追加了方法 Point

追加方法,是要追加在 type上,所以需要先定义类型。

// 定义类型 
type Point struct{ X, Y float64 }
// 声明函数
func Distance(p, q Point) float64 {

// 【Point类型】的 变量p,【追加】方法
func (p Point) Distance(q Point) float64 {

// 只要是 Point类型,都有Distance 方法 p.Distance。故也可以 q.Distance

调用:
p := Point{1, 2}
q := Point{4, 6}

fmt.Println(Distance(p, q)) // "5", function call

fmt.Println(p.Distance(q))  // "5", method call 

2、基于指针对象的方法

基于指针对象的方法,即 方法接收器 为 指针

需要方法接收器 为 【指针】类型 的场景:

1、【需要】修改方法接收器的 值,并【展示】 接收器变量 【值】的变化,需要用指针

默认按值传递,当一个变量作为【参数】传递时,会创建变量副本,然后传递给函数或者方法,对该副本的操作不会影响到原始变量。

使用指针后,当变量作为【指针】传递时,是原始变量指针的副本,和原始指针指向同样内存地址,对该指针的操作会影响到原始变量

本例:(a *Integer) 即为类型 Integer 增加了 Add方法,接收器 为指针类型

*T 是指某种指针类型,*Integer 是指Integer类型的指针


type Integer int

func (a *Integer) Add(b Integer) {
   *a += b
}

var a Integer = 1
a.Add(2)
fmt.Println("a =", a)
輸出: a=3

func (a Integer) Add(b Integer) { a += b
}
var a Integer = 1
a.Add(2)
fmt.Println("a =", a)

但如果是這樣,輸出: a=1

2、使用指针作为 方法接收器的场景2,函数的入参是拷贝传递,如果入参本身较大,比如 struct 为入参类型,拷贝代价太高。而指针拷贝显然更低,节省内存。

三、指針的其他表現形式

1、聲明與賦值

通過var變量聲明的指針類型,這個時候還沒有對應的內存地址,僅僅還只是個變量。值為nil。所以不能直接複製和取值

var mytest1 *int 
fmt.Println(mytest1)

輸出:
<nil>

需要簡短聲明後,才能對變量赋值

使用指针步骤,定义指针变量、为指针赋值、访问指针变量中的指向地址的值

a := 1
var mytest1 *int
mytest1 = &a
fmt.Println(mytest1, &mytest1, *&mytest1, **&mytest1)

輸出:
0x1400001a110 0x1400000e030 0x1400001a110 1

這裡注意,需要**&mytest1才能取到指針對象的指針對象的值

這種用法,不推薦 指針嵌套指針

3、

可以给【任何类型type】,【追加】方法,只要这个类型的 底层类型 不是指针或者interface

type Path []Point

// Path 是【命名类型type】,即用 type 表达式 定义的新类型,这里的Path就是
// []Point 是【底层类型type】,即[]Point这个slice

2、

如果要修改12行 变量a的值或24行 变量a,才需要给方法 Add1传递 stuck 【类型type】

也只有用了指针,才能确保 12行a、24行a 变量为同一个对象,而导致更新值后同时更新

18行、24行 的 a Integer【函数名前,表达式位置】,叫做方法的 接收器。注意方法才有接收器

24行 的 a *Integer ,是 (【命名类型】)为Integer的指针, *T 是指某种指针类型,*Integer 是指Integer类型的指针

image.png

image.png

golang的面向对象

传统面向对象特性:继承、虚函数、构造函数、析构函数、隐藏this指针

golang 给任意对象的方法追加,类似 继承 结构体stuck,和其他语言的class类,地位同等。但go放弃了大量面向对象的特性,只保留了 组合 这个特性

四、stuck 结构体

// 定义结构体
type Rect struct { 
    x, y float64
    width, height float64 
}
// 初始化 Rect
rect1 := new(Rect)
rect2 := &Rect{}
rect3 := &Rect{0, 0, 100, 200}
rect4 := &Rect{width: 100, height: 200}

参考

# go指针详解

# 【Go指针详解】

# 函数闭包指针