0

0

Go语言中指针操作符*与取地址符&的全面解析

聖光之護

聖光之護

发布时间:2025-07-18 11:00:03

|

1011人浏览过

|

来源于php中文网

原创

Go语言中指针操作符*与取地址符&的全面解析

本文深入探讨Go语言中*和&这两个核心操作符的作用。&用于获取变量的内存地址,生成一个指向该变量的指针;而*则用于声明指针类型、对指针进行解引用以访问其指向的值,以及通过指针间接修改变量的值。理解它们对于掌握Go的内存管理和数据传递机制至关重要,尤其是在函数参数传递和结构体操作中。

go语言中,指针是一个核心概念,它允许程序直接操作内存地址,从而实现更灵活的数据管理和高效的程序设计。理解*(星号)和&(取地址符)这两个操作符是掌握go语言指针的关键。

1. & 操作符:取地址(Address-of Operator)

& 操作符用于获取一个变量的内存地址。当应用于一个变量时,它会返回该变量在内存中的地址,这个地址就是一个指向该变量类型的新指针。

语法: &variable

示例:

package main

import "fmt"

func main() {
    value := 100
    ptr := &value // ptr 现在是一个指向 value 变量的指针
    fmt.Printf("变量 value 的值:%d\n", value)
    fmt.Printf("变量 value 的内存地址:%p\n", ptr) // %p 用于打印指针地址
}

在上述示例中,ptr 变量存储的不是 100 这个值,而是 value 变量在内存中的具体位置。

立即学习go语言免费学习笔记(深入)”;

2. * 操作符:指针的声明、解引用与间接赋值

* 操作符在Go语言中扮演了多重角色,其具体含义取决于它出现的位置。

2.1 指针类型声明(Pointer Type Declaration)

当 * 紧跟在一个类型前面时(例如 *int, *string),它表示声明一个指向该类型数据的指针。这意味着这个变量将存储一个内存地址,而这个地址指向的是指定类型的数据。

语法: var variableName *Type

示例:

package main

import "fmt"

func main() {
    var p *int // 声明 p 是一个指向 int 类型数据的指针
    fmt.Printf("p 的零值(未初始化):%v\n", p) // 指针的零值是 nil
}

2.2 指针解引用(Dereferencing)

当 * 放置在一个已声明的指针变量前面时,它被称为解引用操作符。解引用操作用于访问或获取指针所指向的内存地址中存储的实际值。

语法: *pointerVariable

PictoGraphic
PictoGraphic

AI驱动的矢量插图库和插图生成平台

下载

示例:

package main

import "fmt"

func main() {
    value := 200
    ptr := &value // ptr 指向 value 的地址
    fmt.Printf("通过 ptr 解引用获取 value 的值:%d\n", *ptr) // *ptr 获取 ptr 指向的值
}

2.3 指针间接赋值(Indirect Assignment)

通过解引用操作符,我们不仅可以读取指针指向的值,还可以修改该值。当 *pointerVariable 出现在赋值语句的左侧时,它表示修改指针所指向内存地址中的值。

语法: *pointerVariable = newValue

示例:

package main

import "fmt"

func main() {
    num := 300
    ptr := &num // ptr 指向 num 的地址
    fmt.Printf("修改前 num 的值:%d\n", num)

    *ptr = 400 // 通过 ptr 间接修改 num 的值
    fmt.Printf("修改后 num 的值:%d\n", num)
}

3. 综合示例分析

为了更深入地理解 & 和 * 的协同工作,我们来分析一个典型的代码片段:

package main

import (
    "fmt"
    "os"
)

func main() {
    s := "hello" // 声明并初始化字符串变量 s
    if s[1] != 'e' { // 检查字符串 s 的第二个字符是否为 'e'
        os.Exit(1) // 如果不是,则退出程序
    }
    fmt.Printf("s 初始值: %s, 地址: %p\n", s, &s)

    s = "good bye" // 重新赋值 s
    fmt.Printf("s 重新赋值后: %s, 地址: %p\n", s, &s)

    var p *string = &s // 声明一个字符串指针 p,并用 s 的地址初始化它
    fmt.Printf("指针 p 的值 (s 的地址): %p\n", p)
    fmt.Printf("通过指针 p 解引用得到的值: %s\n", *p)

    *p = "ciao" // 通过指针 p 间接修改 s 的值
    fmt.Printf("通过指针 p 修改后 s 的值: %s\n", s)
    fmt.Printf("通过指针 p 修改后,通过 p 解引用得到的值: %s\n", *p)
}

分析过程:

  1. s := "hello":变量 s 被创建并赋值为 "hello"。
  2. s = "good bye":s 的值被更新为 "good bye"。此时 s 的内存地址保持不变,但其内容已更改。
  3. var p *string = &s:
    • &s:获取变量 s 的内存地址。
    • *string:声明 p 是一个指向 string 类型的指针。
    • =:将 s 的内存地址赋值给 p。现在,p 指向 s 所在的内存位置。
  4. *p = "ciao":
    • *p:解引用 p,表示访问 p 所指向的内存位置(即 s 的内存位置)。
    • =:将 "ciao" 这个字符串赋值到 p 所指向的内存位置。这意味着 s 的值被间接修改为 "ciao"。

运行此代码,您会观察到 s 的值最终变为 "ciao",这证明了通过指针可以实现对原始变量的间接修改。

4. Go语言中的“引用”与“值传递”

Go语言中所有函数参数的传递都是值传递(pass by value)。这意味着当您将一个变量作为参数传递给函数时,函数会接收到该变量的一个副本。对副本的任何修改都不会影响原始变量。

然而,当您传递一个指针的值时,函数接收到的是原始变量的内存地址的副本。虽然这个地址本身是一个副本,但它指向的仍然是原始变量在内存中的位置。因此,通过这个指针副本解引用并修改其指向的值,就能够影响到原始变量。这在效果上类似于其他语言中的“引用传递”,但本质上仍是值传递——只是传递的是一个地址值。

package main

import "fmt"

func modifyValue(x int) { // 值传递,x 是 num 的副本
    x = 20
}

func modifyValueByPointer(ptr *int) { // 值传递,ptr 是 &num 的副本
    *ptr = 20 // 通过指针修改 num
}

func main() {
    num := 10
    fmt.Println("初始值:", num) // 10

    modifyValue(num)
    fmt.Println("值传递后:", num) // 10 (未改变)

    modifyValueByPointer(&num)
    fmt.Println("指针传递后:", num) // 20 (已改变)
}

5. 指针的应用场景与注意事项

5.1 典型应用场景

  • 修改函数外部变量:如上例所示,当需要在函数内部修改函数外部的变量时,传递变量的指针是标准做法。
  • 避免大数据结构复制:当结构体或数组非常大时,将其作为参数传递会导致整个数据结构的复制,消耗大量内存和时间。传递其指针可以避免这种开销,提高性能。
  • 实现链表、树等数据结构:这些数据结构天然依赖于节点间的指针连接。
  • 方法接收者:Go语言中,方法的接收者可以是值类型也可以是指针类型。使用指针接收者可以修改接收者实例的字段。

5.2 注意事项

  • 零值指针(nil):未初始化的指针变量的零值是 nil。尝试解引用 nil 指针会导致运行时错误(panic)。在使用指针前,务必确保其已被正确初始化或检查是否为 nil。
  • Go不支持指针算术:与C/C++不同,Go语言不允许对指针进行加减运算来访问相邻内存地址。这是为了提高内存安全性和代码可读性
  • 垃圾回收:Go语言拥有自动垃圾回收机制,开发者无需手动管理内存的分配和释放。当一个内存地址不再有任何指针指向它时,垃圾回收器会在适当的时候自动回收这块内存。

总结

& 和 * 是Go语言中操作指针的两个基本且强大的操作符。& 用于获取变量的内存地址,生成一个指针;而 * 则用于声明指针类型、解引用指针以访问其指向的值,以及通过指针间接修改变量的值。理解并熟练运用它们,是编写高效、安全Go程序的基础。掌握指针不仅能让您更深入地理解Go的内存模型,也能帮助您在特定场景下优化程序性能和实现复杂的数据结构。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

313

2023.08.02

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

251

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

206

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1437

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

609

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

547

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

539

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

158

2025.07.29

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

150

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Rust 教程
Rust 教程

共28课时 | 4万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 2.2万人学习

Go 教程
Go 教程

共32课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号