65.9K
CodeProject 正在变化。 阅读更多。
Home

Golang:指针 - 详细概述。

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (1投票)

2019年4月20日

CPOL

6分钟阅读

viewsIcon

5677

什么是“指针”?Golang 中的指针示例。* 和 & 运算符。作为函数参数的指针。函数 - 按值和按引用传递参数。

什么是指针

简而言之,指针是一个变量,它存储另一个存储数据的变量的地址。

指针示例

让我们举一个使用指针的最简单的例子

package main

import "fmt"

func main() {

    a := 1
    b := &a

    fmt.Println("A: ", a)
    fmt.Println("B: ", b)
    fmt.Println("B: ", *b)
}

这里

  1. 创建一个名为 a、类型为整数、值为 1 的变量
  2. 创建一个名为 b、类型为指向整数的指针(见下文)的变量
  3. 并输出数据
    1. 首先只是 a 的值
    2. b 变量的 a(即内容
    3. 最后,我们获取 b 指向的 a 的值(我们将在稍后查看 *& 运算符)

运行代码

$ go run pointers_example.go
A:  1
B:  0xc0000140e8
B:  1

在第二行,我们看到 b 指针指向的内存地址。

在第三行 - 我们从该内存地址获取了值。

指针可以更全面地初始化,通过指定类型而不是使用 :=,这样代码看起来就像

...
func main() {

    var a int = 1
    var b *int = &a

    fmt.Println("A: ", a)
    fmt.Println("B: ", b)
    fmt.Println("B: ", *b)

}
...

var b *int = &a 行,我们设置 b 变量是指向整数数据的指针。

同样,可以通过在其数据类型中使用 *string 而不是 *int 来创建指向字符串数据的指针。

...
    var c *string  
    fmt.Printf("The C var type: %T, default value: %v\n", c, c)
    var d string = "This is a string"
    c = &d
    fmt.Printf("The C var type: %T, default value: %v, string value: %s\n", c, c, *c) 
...

这里

  1. 创建了一个名为 c、类型为指向字符串数据的指针的变量
  2. 使用 Printf() 的修饰符,显示了 c 变量的数据类型 (%T) 及其值 (%v)
  3. 创建了一个名为 d、类型为字符串、值为 “This is a string” 的变量
  4. c 变量设置了 d 变量的内存地址
  5. 使用 Printf() 的修饰符,显示了 c 变量的数据类型 (%T)、其值 (%v) 以及从 c 中存储的内存地址获取的值

Run

$ go run pointers_example.go
The C var type: *string, default value: <nil>
The C var type: *string, default value: 0xc0000101e0, string value: This is a string

* 和 & 运算符

我们在前面的示例中已经使用过它们,但让我们仔细看看。

* 运算符是解引用运算符

这里的解引用意味着我们获取的不是指针(它存储地址)的值,而是从指针指向的内存地址中获取的值。好吧 - 指向的内存地址!🙂

让我们回到之前的例子

...
fmt.Printf("The C var type: %T, default value: %v, string value: %s\n", c, c, *c)
...

这里

  • default value: %vc - 显示存储在 c 变量中的值 - c 指向的内存地址
  • string value: %s*c - 显示从 c 变量调用内存后获得的值

& 运算符返回变量的内存地址。

例如,让我们在之前的示例中添加一行,并使用 Printf()%p 修饰符显示地址

...
    var c *string  
    fmt.Printf("The C var type: %T, default value: %v\n", c, c)
    var d string = "This is a string"
    c = &d
    fmt.Printf("The C var type: %T, default value: %v, string value: %s\n", c, c, *c)
    fmt.Printf("The D adress: %p\nThe C address: %p\nThe C value: %v\n", &d, &c, c)
...

Check

$ go run pointers_example.go
The C var type: *string, default value: <nil>
The C var type: *string, default value: 0xc0000101e0, string value: This is a string
The D adress: 0xc0000101e0
The C address: 0xc00000e028
The C value: 0xc0000101e0

我们得到了 d 变量地址的 0xc0000101e0 值,c 变量地址的 0xc00000e028,但 c 本身存储的是 d 变量的地址 - 0xc0000101e0

实际上,通过获取 d 变量的地址,可以在 c 指针变量中进行数据初始化。

...
c = &d
...

new() 函数

要使用 var pointername *type 符号定义和初始化指针 - 您可以使用内置的 new() Go 函数,该函数接受数据类型作为第一个参数,并返回指向为变量数据分配的内存的指针。

...
    a := 1
    b := new(int)
    fmt.Printf("A: %d, B: %v, %v\n", a, b, *b)
    b = &a
    fmt.Printf("A: %d, B: %v, %v\n", a, b, *b) 
...

这里

  1. 创建了值为 1a 变量
  2. 创建了 b 变量,它将保存 new() 函数返回的内存的指针,并且目前保持 0,因为已分配的内存无法保持 nil
  3. b 的值更新为 a 的地址

Check

$ go run pointers_example.go
A: 1, B: 0xc000014100, 0
A: 1, B: 0xc0000140e8, 1

更改指针的值

好吧,说“更改指针的值”是不正确的,因为指针变量存储的是内存地址 - 而不是值本身。

但是,使用指针,我们可以更改该指针指向的内存位置的值。

例如

...
    a := 1
    b := &a

    fmt.Println("A: ", a)
    fmt.Println("B: ", *b)

    *b = 2
    fmt.Println("B: ", *b)
...

这里

  1. a 变量的值为 1
  2. b 变量具有 a 的地址
  3. 显示 a 的值
  4. 显示从 b 指向的地址获取的值
  5. 我们将此内存中的值更改为 2
  6. 并显示新值

运行代码

$ go run pointers_example.go
A:  1
B:  1
B:  2

将指针作为函数参数传递

您可以将指针作为参数传递给函数。

例如

package main

import "fmt"

func setVal(b *int) {
    *b = 2
}

func main() {

    a := 1
    b := &a

    fmt.Println("Init values")
    fmt.Println("A: ", a)
    fmt.Println("B: ", *b)

    setVal(b)

    fmt.Println("Changed values")
    fmt.Println("A: ", a)
    fmt.Println("B: ", *b)
}

在这里,我们创建了一个名为 asetVal() 函数,它接受一个整数指针作为参数,并将更改该指针传递的地址中的值。

检查一下

$ go run pointers_example.go
Init values
A:  1
B:  1
Changed values
A:  2
B:  2

调用 setVal() 后 - ab 将显示新值。

更进一步 - 我们甚至可以将 a 变量的地址传递给 setVal()

...
func setVal(b *int) {
    *b = 2
}

func main() {

    a := 1

    fmt.Println("Init values")
    fmt.Println("A: ", a)

    setVal(&a)

    fmt.Println("Changed values")
    fmt.Println("A: ", a)
}

结果是:

$ go run pointers_example.go
Init values
A:  1
Changed values
A:  2

函数:按值和按引用传递参数

这里有点离题,但使用上面的例子,也可以显示按值传递参数和按引用传递参数之间的区别。

让我们更新这个例子

...
func setVal(b *int, c int) {
    *b = 2
    c = 4
    fmt.Printf("B from setVal(). Poiner to: %p, val: %v\n", b, *b)
    fmt.Printf("C from setVal(). Addr: %p, val: %v\n", &c, c)
}

func main() {

    a := 1
    b := &a
    c := 3

    fmt.Println("Init values")
    fmt.Printf("A from main(). Addr: %p, val: %v\n", &a, a)
    fmt.Printf("B from main(). Poiner to: %p, val: %v\n", b, *b)
    fmt.Printf("C from main(). Addr: %p, val: %v\n", &c, c)

    fmt.Println("Changed values")
    setVal(b, c)
    
    fmt.Printf("A from main(). Addr: %p, val: %v\n", &a, a)
    fmt.Printf("B from main(). Poiner to: %p, val: %v\n", b, *b)
    fmt.Printf("C from main(). Addr: %p, val: %v\n", &c, c)
}

这里

  1. 获取 a 变量的地址及其值
  2. 获取存储在 b 变量中的地址,然后获取存储在该地址的数据
  3. 获取 c 变量的地址及其值
  4. 调用 setVal() 函数,通过 b 中的引用传递 a 的值,并通过 c - 按值传递
  5. setVal() 中,获取存储在 b 变量中的地址以及存储在那里的值
  6. setVal() 中,获取 c 变量的地址以及存储在该内存区域中的值
  7. main() 中,获取 a 的地址以及存储在那里的值
  8. main() 中,获取存储在 b 变量中的地址以及从那里获取的值
  9. main() 中,获取 c 变量的地址以及存储在那里的值

运行它:

$ go run pointers_example.go

Init values
A from main(). Addr: 0xc0000140e8, val: 1
B from main(). Poiner to: 0xc0000140e8, val: 1
C from main(). Addr: 0xc000014100, val: 3

Changed values
B from setVal(). Poiner to: 0xc0000140e8, val: 2
C from setVal(). Addr: 0xc000014130, val: 4
A from main(). Addr: 0xc0000140e8, val: 2
B from main(). Poiner to: 0xc0000140e8, val: 2
C from main(). Addr: 0xc000014100, val: 3

这里

  1. a 存储在 0xc0000140e8 位置,值为 1
  2. b 指向同一位置 0xc0000140e8,返回相同的值 1
  3. c 存储在 0xc000014100 位置,值为 3
  4. 调用了 setVal()
  5. setVal() 中的 b 仍然指向 0xc0000140e8 位置,其值为 2
  6. setVal() 中的 c 获得了新的地址 0xc000014130,其中存储着 4
  7. main() 中的 a 现在保留来自同一 0xc0000140e8 位置的 2
  8. main() 中的 bsetVal() 中的情况相同 - 指向同一位置并返回相同的值
  9. main() 中,对于 c 变量,没有任何改变,因为 setVal() 中的 c 有自己的地址 0xc000014130,而 main() 中的 c 使用 0xc000014100 位置。

实际上,这就是你需要了解的全部内容,以便更好地理解指针是什么以及如何使用它们。

类似帖子

© . All rights reserved.