zhcn 编程语言 Golang 非公開: 关于Go语言指针的详细解释,只需阅读这篇文章即可。

关于Go语言指针的详细解释,只需阅读这篇文章即可。

与Java、.NET等编程语言不同,Go语言为程序员提供了控制数据结构指针的能力,但不允许你执行指针操作。 Go 语言允许您控制给定集合的数据结构、分配数量和内存访问模式。这对于构建正常工作的系统非常重要。指针对性能的影响是不言而喻的,它们是任何系统编程、操作系统或网络应用程序的重要组成部分。

指针(pointers)在Go语言中可以分解为两个核心概念。

  • 类型指针。您可以更改该指针类型的数据。指针可以用来直接传输数据,而不需要复制数据。您不能通过偏移类型指针来操作它们。
  • 一个切片,由指向起始元素的原始指针、元素数量和容量组成。

Go语言中的指针类型变量受益于这些约束和分区,具有无需指针偏移即可高效进行指针访问的特性,避免了关键数据未经授权修改的问题。同时,垃圾收集也方便了无偏移指针的获取和回收。

切片比原始指针更强大、更安全。如果切片越界,运行时将报告崩溃并弹出堆栈,但原始指针只会崩溃。

C/C++ 指针

当谈到C/C++中的指针时,很多人都“望而却步”,尤其是当谈到指针偏移、操作和转换时。

事实上,指针是 C/C++ 语言非常高性能的支柱,在操作大数据块或创建偏移量时非常有用。因此,操作系统仍然是利用C语言和指针的特性来编写的。

C/C++ 中指针批评的根本原因是指​​针操作和内存释放。 C/C++中的原始指针可以自由偏移,有时甚至可以偏移到操作系统的核心区域。计算机操作系统经常需要更新和修复的本质是为了解决由指针越界访问引起的“缓冲区溢出”问题。

要理解指针,需要理解几个概念:指针地址、指针类型、指针值。下面将更详细地解释这些内容。

了解指针地址和指针类型

指针变量可以指向任意值的内存地址,它所指向的值的内存地址在32位和64位机器上分别占用4或8个字节,而不管占用的字节大小是多少。由它指向的值的大小决定。如果定义了指针但未将其分配给变量,则其默认值为 nil。指针变量通常缩写为 ptr。

每个变量在运行时都有一个地址,它代表变量在内存中的位置。 Go语言中,在变量名前面添加&运算符(前缀)来获取变量的内存地址(地址运算),格式如下。

ptr := &v   

其中,v表示要获取地址的变量,变量v的地址由变量ptr接收,ptr的类型为*T ,称为T的指针类型,表示一个*指针。

指针的实际用法可以通过下面的例子来理解。

 package main

import (
    "fmt"
)

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

运行结果:

0xc042052088 0xc0420461b0

代码说明如下:

  • 第 8 行声明整型变量 cat。
  • 字符串变量 str 在第 9 行声明。
  • 第 10 行使用 fmt.Printf 动词%p打印 cat 和 str 变量的内存地址。指针的值是一组以十六进制0x为前缀的数据。

提示:变量、指针和地址之间的关系是每个变量都有一个地址,而指针的值就是地址。

获取指针所指向的值。

使用&运算符获取公共变量的地址并获得指向该变量的指针后,可以在指针上使用*运算符来获取指针的值。这是代码:

 package main

import(
    "fmt"
)

ファンク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: 0xc0420401b0
value type: string
value: Malibu Point 10880, 90265

代码说明如下:

  • 第 10 行准备并分配了一个字符串。
  • 第 13 行获取字符串的地址并将指针存储在变量 ptr 中。
  • 第 16 行输出变量 ptr 的类型。它的类型是*string。
  • 第19行输出ptr指针地址。每次运行时该地址都会发生变化。
  • 第22行,对ptr指针变量进行取值操作,变量值类型为string。
  • 第25行,获取值后打印值的类型。
  • 第 28 行打印 value 的值。

地址检索运算符&和值检索运算符*是一对互补的运算符,其中&检索地址,而*根据地址检索该地址所指向的值。

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

  • 要获取变量的地址,请使用&运算符获取该变量的指针变量。
  • 指针变量的值是指针地址。
  • 使用*运算符获取指针变量的值即可获取指针变量所指向的变量的值。

使用指针修改值

不仅可以通过指针获取值,还可以更改值。

我们已经了解了如何使用多个赋值来交换值,但您也可以使用指针来交换值。

 package main

import "fmt"

// 交換関数
func swap(a、b * int) {

    // aのポインターの値を、一時変数tに代入します
    t:=* a

    // bのポインターの値を、aのポインターが指し示す変数に代入します
    * a = * b

    // aのポインターの値を、bのポインターが指し示す変数に代入します
    * b = t
}

func main() {

// 2つの変数を準備し、1と2を割り当てます
    x、y:=1、2

    // 変数の値を交換する
    swap(& x、& y)

    // 変数の値を出力する
    fmt.Println(x、y)
} 

运行结果:

2 1

代码说明如下:

  • 第 6 行使用参数 a 和 b 定义交换函数。参数a和b都是*int指针类型。
  • 第 9 行获取指针 a 的值并将其分配给变量 t(此时其类型为 int)。
  • 第 12 行检索 b 的指针值并将其分配给指针 a 指向的变量。请注意,本例中的*a并不意味着获取指针的值,而是“a 指向的变量”。
  • 第 15 行,t 的值被赋值给指针 b 指向的变量。
  • 第21行,我们准备了两个变量x和y,并分别给它们赋值1和2,它们的类型是int。
  • 第 24 行将 x 和 y 地址作为参数并调用 swap() 函数。
  • 第27行,交换完成时打印x和y值。

*运算符用作右值时,表示获取指针的值,当用作左值,即放在赋值运算符的左侧时,表示变量指向。通过指针。其实总结一下, *运算符的基本含义就是对指针指向的变量进行操作。如果操作在右值上,则获取变量指向的值;如果操作在左值上,则将值设置为指向的变量。

用swap()函数交换指针值会发生什么情况,可以参考下面的代码。

 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

结果发现兑换不成功。上面代码中的swap()函数交换了a和b的地址。一旦交换完成,a和b的变量值就真正交换了。然而,与a和b相关的两个变量实际上并不相关。就好像把两座房子的牌摊在桌子上一样,交换两座房子的牌后,对两座房子没有任何影响。

示例:使用指针变量从命令行获取输入

Go语言内置的flag包实现了命令行参数解析,flag包让命令行工具的开发变得更加简单。

下面的代码预定义了一些命令行指令和相应的变量,并在运行时填写相应的参数。解析标志包后,您可以检索命令行数据。

[示例] 获取命令行输入。

 package main

// システムパッケージをインポートする
import(
    flag,
    fmt
)

// コマンドライン引数を定義する
Var mode = flag.String("mode", "", "process mode")

func main(){

    // コマンドライン引数を解析する
    flag.Parse()

    // コマンドライン引数を出力する
    fmt.Println(*mode)
} 

将此代码命名为 main.go 并使用以下命令行运行它:

go run main.go –mode=fast

命令行输出为:

fast

代码说明如下:

  • 第 10 行定义了 *string 类型的模式变量,最高可达 flag.String。接下来的三个参数是:
    • 参数名称:在命令行输入参数时使用此名称。
    • 参数值的默认值:对应flag中使用的函数创建的变量类型:String对应字符串,Int对应整数,Bool对应布尔值。
    • 参数说明:使用-help显示在说明中。
  • 第15行解析命令行参数并将结果写入变量模式。
  • 第 18 行打印模式指针指向的变量。

因为之前用flag.String注册了名为mode的命令行参数,所以flags底层知道如何解析命令行并将其值赋给mode*string指针。 Parse 调用完成后不需要执行任何操作。它从标志中获取其值,但最终值是通过它注册的模式指针获得的。下图展示了代码的执行过程。

图:命令行参数与变量的关系
创建指针的另一种方法 - new() 函数

Go语言还提供了另一种创建指针变量的方法。其格式为:

new(type)

一般是这样写的。

 str := new(string)
*str = "Go言語チュートリアル"

fmt.Println(*str) 

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

通俗易懂的解释《关于Go语言指针的详细解释,请阅读这篇文章》。您必须观看的 2 个最佳视频

プログラマーへの道 #116 Go言語で学ぶ値とアドレスとポインタ(プログラミング入門)
https://www.youtube.com/watch?v=ciG4pku8ZcI&pp=ygVtIEdvIOiogOiqnuODneOCpOODs-OCv-OBruips-e0sOOBquiqrO aYjuOBq-OBpOOBhoOBpuOBr-OAgeOBk-OBruiomOS6i-OCkuiqreOCgOOBoOOBkeOBp-WNgeWIhuOBp-OBmeOAgiZobD1KQQ%3D%3D
#15 初見のGo言語で掲示板を作ってみる シャチョーのコードチャレンジ!
https://www.youtube.com/watch?v=46vDyVUIMvM&pp=ygVtIEdvIOiogOiqnuODneOCpOODs-OCv-OBruips-e0sOOBquiqrO aYjuOBq-OBpOOBhoOBpuOBr-OAgeOBk-OBruiomOS6i-OCkuiqreOCgOOBoOOBkeOBp-WNgeWIhuOBp-OBmeOAgiZobD1KQQ%3D%3D
与Java、.NET等编程语言不同,Go语言为程序员提供了控制数据结构指针的能力,但不允许你执行指针操作。 Go 语言允许您控制给定集合的数据结构、分配数量和内存访问模式。这对于构建正常工作的系统非常重要。指针对性能的影响是不言而喻的,它们是任何系统编程、操作系统或网络应用程序的重要组成部分。

指针(pointers)在Go语言中可以分解为两个核心概念。

  • 类型指针。您可以更改该指针类型的数据。指针可以用来直接传输数据,而不需要复制数据。您不能通过偏移类型指针来操作它们。
  • 一个切片,由指向起始元素的原始指针、元素数量和容量组成。

Go语言中的指针类型变量受益于这些约束和分区,具有无需指针偏移即可高效进行指针访问的特性,避免了关键数据未经授权修改的问题。同时,垃圾收集也方便了无偏移指针的获取和回收。

切片比原始指针更强大、更安全。如果切片越界,运行时将报告崩溃并弹出堆栈,但原始指针只会崩溃。

C/C++ 指针

当谈到C/C++中的指针时,很多人都“望而却步”,尤其是当谈到指针偏移、操作和转换时。

事实上,指针是 C/C++ 语言非常高性能的支柱,在操作大数据块或创建偏移量时非常有用。因此,操作系统仍然是利用C语言和指针的特性来编写的。

C/C++ 中指针批评的根本原因是指​​针操作和内存释放。 C/C++中的原始指针可以自由偏移,有时甚至可以偏移到操作系统的核心区域。计算机操作系统经常需要更新和修复的本质是为了解决由指针越界访问引起的“缓冲区溢出”问题。

要理解指针,需要理解几个概念:指针地址、指针类型、指针值。下面将更详细地解释这些内容。

了解指针地址和指针类型

指针变量可以指向任意值的内存地址,它所指向的值的内存地址在32位和64位机器上分别占用4或8个字节,而不管占用的字节大小是多少。由它指向的值的大小决定。如果定义了指针但未将其分配给变量,则其默认值为 nil。指针变量通常缩写为 ptr。

每个变量在运行时都有一个地址,它代表变量在内存中的位置。 Go语言中,在变量名前面添加&运算符(前缀)来获取变量的内存地址(地址运算),格式如下。

ptr := &v   

其中,v表示要获取地址的变量,变量v的地址由变量ptr接收,ptr的类型为*T ,称为T的指针类型,表示一个*指针。

指针的实际用法可以通过下面的例子来理解。

 package main

import (
    "fmt"
)

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

运行结果:

0xc042052088 0xc0420461b0

代码说明如下:

  • 第 8 行声明整型变量 cat。
  • 字符串变量 str 在第 9 行声明。
  • 第 10 行使用 fmt.Printf 动词%p打印 cat 和 str 变量的内存地址。指针的值是一组以十六进制0x为前缀的数据。

提示:变量、指针和地址之间的关系是每个变量都有一个地址,而指针的值就是地址。

获取指针所指向的值。

使用&运算符获取公共变量的地址并获得指向该变量的指针后,可以在指针上使用*运算符来获取指针的值。这是代码:

 package main

import(
    "fmt"
)

ファンク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: 0xc0420401b0
value type: string
value: Malibu Point 10880, 90265

代码说明如下:

  • 第 10 行准备并分配了一个字符串。
  • 第 13 行获取字符串的地址并将指针存储在变量 ptr 中。
  • 第 16 行输出变量 ptr 的类型。它的类型是*string。
  • 第19行输出ptr指针地址。每次运行时该地址都会发生变化。
  • 第22行,对ptr指针变量进行取值操作,变量值类型为string。
  • 第25行,获取值后打印值的类型。
  • 第 28 行打印 value 的值。

地址检索运算符&和值检索运算符*是一对互补的运算符,其中&检索地址,而*根据地址检索该地址所指向的值。

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

  • 要获取变量的地址,请使用&运算符获取该变量的指针变量。
  • 指针变量的值是指针地址。
  • 使用*运算符获取指针变量的值即可获取指针变量所指向的变量的值。

使用指针修改值

不仅可以通过指针获取值,还可以更改值。

我们已经了解了如何使用多个赋值来交换值,但您也可以使用指针来交换值。

 package main

import "fmt"

// 交換関数
func swap(a、b * int) {

    // aのポインターの値を、一時変数tに代入します
    t:=* a

    // bのポインターの値を、aのポインターが指し示す変数に代入します
    * a = * b

    // aのポインターの値を、bのポインターが指し示す変数に代入します
    * b = t
}

func main() {

// 2つの変数を準備し、1と2を割り当てます
    x、y:=1、2

    // 変数の値を交換する
    swap(& x、& y)

    // 変数の値を出力する
    fmt.Println(x、y)
} 

运行结果:

2 1

代码说明如下:

  • 第 6 行使用参数 a 和 b 定义交换函数。参数a和b都是*int指针类型。
  • 第 9 行获取指针 a 的值并将其分配给变量 t(此时其类型为 int)。
  • 第 12 行检索 b 的指针值并将其分配给指针 a 指向的变量。请注意,本例中的*a并不意味着获取指针的值,而是“a 指向的变量”。
  • 第 15 行,t 的值被赋值给指针 b 指向的变量。
  • 第21行,我们准备了两个变量x和y,并分别给它们赋值1和2,它们的类型是int。
  • 第 24 行将 x 和 y 地址作为参数并调用 swap() 函数。
  • 第27行,交换完成时打印x和y值。

*运算符用作右值时,表示获取指针的值,当用作左值,即放在赋值运算符的左侧时,表示变量指向。通过指针。其实总结一下, *运算符的基本含义就是对指针指向的变量进行操作。如果操作在右值上,则获取变量指向的值;如果操作在左值上,则将值设置为指向的变量。

用swap()函数交换指针值会发生什么情况,可以参考下面的代码。

 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

结果发现兑换不成功。上面代码中的swap()函数交换了a和b的地址。一旦交换完成,a和b的变量值就真正交换了。然而,与a和b相关的两个变量实际上并不相关。就好像把两座房子的牌摊在桌子上一样,交换两座房子的牌后,对两座房子没有任何影响。

示例:使用指针变量从命令行获取输入

Go语言内置的flag包实现了命令行参数解析,flag包让命令行工具的开发变得更加简单。

下面的代码预定义了一些命令行指令和相应的变量,并在运行时填写相应的参数。解析标志包后,您可以检索命令行数据。

[示例] 获取命令行输入。

 package main

// システムパッケージをインポートする
import(
    flag,
    fmt
)

// コマンドライン引数を定義する
Var mode = flag.String("mode", "", "process mode")

func main(){

    // コマンドライン引数を解析する
    flag.Parse()

    // コマンドライン引数を出力する
    fmt.Println(*mode)
} 

将此代码命名为 main.go 并使用以下命令行运行它:

go run main.go –mode=fast

命令行输出为:

fast

代码说明如下:

  • 第 10 行定义了 *string 类型的模式变量,最高可达 flag.String。接下来的三个参数是:
    • 参数名称:在命令行输入参数时使用此名称。
    • 参数值的默认值:对应flag中使用的函数创建的变量类型:String对应字符串,Int对应整数,Bool对应布尔值。
    • 参数说明:使用-help显示在说明中。
  • 第15行解析命令行参数并将结果写入变量模式。
  • 第 18 行打印模式指针指向的变量。

因为之前用flag.String注册了名为mode的命令行参数,所以flags底层知道如何解析命令行并将其值赋给mode*string指针。 Parse 调用完成后不需要执行任何操作。它从标志中获取其值,但最终值是通过它注册的模式指针获得的。下图展示了代码的执行过程。

图:命令行参数与变量的关系
创建指针的另一种方法 - new() 函数

Go语言还提供了另一种创建指针变量的方法。其格式为:

new(type)

一般是这样写的。

 str := new(string)
*str = "Go言語チュートリアル"

fmt.Println(*str) 

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

通俗易懂的解释《关于Go语言指针的详细解释,请阅读这篇文章》。您必须观看的 2 个最佳视频

プログラマーへの道 #116 Go言語で学ぶ値とアドレスとポインタ(プログラミング入門)
https://www.youtube.com/watch?v=ciG4pku8ZcI&pp=ygVtIEdvIOiogOiqnuODneOCpOODs-OCv-OBruips-e0sOOBquiqrO aYjuOBq-OBpOOBhoOBpuOBr-OAgeOBk-OBruiomOS6i-OCkuiqreOCgOOBoOOBkeOBp-WNgeWIhuOBp-OBmeOAgiZobD1KQQ%3D%3D
#15 初見のGo言語で掲示板を作ってみる シャチョーのコードチャレンジ!
https://www.youtube.com/watch?v=46vDyVUIMvM&pp=ygVtIEdvIOiogOiqnuODneOCpOODs-OCv-OBruips-e0sOOBquiqrO aYjuOBq-OBpOOBhoOBpuOBr-OAgeOBk-OBruiomOS6i-OCkuiqreOCgOOBoOOBkeOBp-WNgeWIhuOBp-OBmeOAgiZobD1KQQ%3D%3D