【go语言】指针

一、指针的定义和使用

       在 Go 语言中,指针是一种变量,用来存储另一个变量的内存地址。通过指针,我们可以间接地操作其他变量的值。Go 语言中的指针与其他语言(如 C 或 C++)的指针有所不同,它不支持指针算术,但提供了简洁和安全的指针操作方式。

1.1 指针的定义

在 Go 中,指针通过 * 来声明,它指向某种类型的变量。

  • * 用于声明指针类型。
  • & 用于取变量的地址(即获取指针)。
package main

import "fmt"

func main() {
    var a int = 58
    var ptr *int = &a  // ptr 是指向 a 的指针

    fmt.Println("a 的值:", a)
    fmt.Println("ptr 指向的地址:", ptr)  // 输出 a 的地址
    fmt.Println("ptr 指向的值:", *ptr)   // 输出 a 的值
}

解释:

  • var ptr *int:声明一个 ptr 变量,类型为指向 int 的指针。
  • &a:取 a 的地址,并将其赋值给指针 ptr
  • *ptr:解引用,获取 ptr 指向的地址上的值。

1.2 使用指针

1.2.1 取地址(&)

通过 & 符号,我们可以获取一个变量的内存地址。这个内存地址可以赋给一个指针。

x := 10
p := &x // p 是指向 x 的指针

1.2.2 解引用(*)

通过 * 符号,我们可以获取指针指向的变量的值,称为解引用。

value := *p // 通过解引用获取 p 指向的值

1.3 修改变量的值

       通过指针,可以直接修改原始变量的值,因为指针存储的是变量的地址,间接操作该地址即能修改变量的值。

package main

import "fmt"

func changeValue(x *int) {
    *x = 20  // 通过指针修改 x 指向的变量的值
}

func main() {
    a := 10
    fmt.Println("修改前 a 的值:", a)
    changeValue(&a)  // 传入 a 的地址
    fmt.Println("修改后 a 的值:", a)
}

1.4 指针与函数参数

       指针在函数参数中有重要应用,特别是在传递较大的数据结构(如数组、切片、结构体)时,使用指针可以避免数据的复制。它还可以用于函数修改外部变量的值。

package main

import "fmt"

func swap(x, y *int) {
    *x, *y = *y, *x  // 交换指针指向的值
}

func main() {
    a, b := 10, 20
    fmt.Println("交换前:", a, b)
    swap(&a, &b)  // 传递 a 和 b 的地址
    fmt.Println("交换后:", a, b)
}

1.5 nil 指针

       Go 语言中的指针可以是 nil,表示它没有指向任何有效的内存地址。一个 nil 指针是一个空指针,它没有指向任何对象。

var ptr *int  // ptr 默认值为 nil
fmt.Println(ptr)  // 输出 nil

使用 nil 指针时,如果尝试解引用,程序会发生 运行时错误(panic)。

var ptr *int
fmt.Println(*ptr)  // 会引发运行时错误:invalid memory address or nil pointer dereference

1.6 指针的应用

1.6.1 高效地传递数据

通过使用指针,可以避免将大型结构体或数组等数据类型进行值拷贝,从而提高性能。

type Person struct {
    Name string
    Age  int
}

func updatePerson(p *Person) {
    p.Age += 1
}

func main() {
    person := Person{Name: "Alice", Age: 30}
    updatePerson(&person)  // 传递指针,避免拷贝
    fmt.Println(person)     // Age 被修改为 31
}

1.6.2 与接口结合

       在 Go 中,指针和接口结合使用时,能够有效修改接口类型的值。例如,可以通过指针来修改结构体字段的值。

type Dog struct {
    Name string
}

func (d *Dog) Speak() {
    fmt.Println(d.Name + " says Woof!")
}

func main() {
    dog := &Dog{Name: "Buddy"}
    dog.Speak()  // 使用指针调用方法
}

二、指针的初始化

       在 Go 语言中,指针的初始化可以通过几种不同的方式进行,主要取决于如何获取一个变量的地址或使用内置的 new 函数。

2.1 通过去变量的地址来初始化指针

       最常见的初始化指针的方法是通过取一个变量的地址。使用 & 运算符来获取变量的地址并赋值给指针。

package main

import "fmt"

func main() {
    a := 42
    var ptr *int = &a  // 通过取变量 a 的地址初始化 ptr

    fmt.Println("a 的值:", a)
    fmt.Println("ptr 指向的地址:", ptr)
    fmt.Println("ptr 指向的值:", *ptr)
}
a 的值: 42
ptr 指向的地址: <地址>
ptr 指向的值: 42

2.2 使用 new 函数初始化指针

       Go 提供了 new 函数来为变量分配内存并返回一个指向该变量的指针。new 会初始化变量为该类型的零值。

package main

import "fmt"

func main() {
    ptr := new(int)  // 使用 new 函数初始化一个指向 int 的指针

    fmt.Println("ptr 指向的值:", *ptr)  // 输出 0,因为 int 的零值是 0

    *ptr = 58  // 修改指针指向的值
    fmt.Println("ptr 指向的值修改后:", *ptr)  // 输出 58
}

2.3 零值指针初始化

       Go 中的指针默认值是 nil,这意味着如果声明一个指针但没有显式初始化它,它的值是 nil。这通常表示该指针尚未指向任何有效的内存地址。

package main

import "fmt"

func main() {
    var ptr *int  // 声明一个指向 int 的指针,但未初始化
    fmt.Println("ptr 的零值:", ptr)  // 输出 nil
}

2.4 通过数组或者切片初始化指针

如果你有一个数组或切片,可以通过取数组元素的地址来初始化指针。

package main

import "fmt"

func main() {
    arr := [3]int{1, 2, 3}
    ptr := &arr[0]  // 取数组第一个元素的地址初始化指针

    fmt.Println("ptr 指向的值:", *ptr)  // 输出 1
}

三、nil 在 go 中的细节

       在 Go 语言中,nil 是一个非常重要的概念,它代表着“无值”或“空值”。它可以赋给不同类型的变量(如指针、切片、映射、通道等),表示这些变量当前没有指向任何有效的值或资源。理解 nil 的细节对编写高效、正确的 Go 代码至关重要。以下是关于 nil 在 Go 中的几个关键细节:

3.1 指针和 nil

       指针类型的变量默认值为 nil,这意味着它没有指向任何有效的内存地址。当你声明一个指针但不对其进行初始化时,它会自动初始化为 nil。你可以显式地将指针设为 nil,这表示该指针不指向任何有效的内存。

package main

import "fmt"

func main() {
    var ptr *int  // 声明一个 int 类型的指针,默认值为 nil
    fmt.Println(ptr) // 输出: <nil>

    var x int = 10
    ptr = &x  // 让指针指向变量 x 的地址
    fmt.Println(ptr) // 输出: 地址值
}

3.2 切片和 nil

       切片是 Go 中的一种动态数组类型,它由三部分组成:指向底层数组的指针、切片的长度和切片的容量。如果一个切片没有被初始化(例如没有使用 make 或直接赋值),它的默认值是 nil。一个 nil 切片的长度和容量都为 0。

package main

import "fmt"

func main() {
    var slice []int  // 声明一个切片,默认值为 nil
    fmt.Println(slice == nil)  // 输出: true
    fmt.Println(len(slice))    // 输出: 0
    fmt.Println(cap(slice))    // 输出: 0

    slice = append(slice, 1, 2, 3)  // 给切片赋值
    fmt.Println(slice)  // 输出: [1 2 3]
}

3.3 映射和 nil

       在 Go 中,映射(map)是一个无序的键值对集合。声明一个映射时,如果没有使用 make 或字面量进行初始化,它的默认值也是 nil。一个 nil 映射不能进行任何操作(如添加、删除键值对),如果你对一个 nil 映射进行读操作,结果是返回类型的零值,但写操作则会引发运行时错误。

package main

import "fmt"

func main() {
    var m map[string]int  // 声明一个映射,默认值为 nil
    fmt.Println(m == nil)  // 输出: true

    // 读取 nil 映射,返回零值
    fmt.Println(m["key"])  // 输出: 0

    // 尝试对 nil 映射进行写操作会引发错误
    // m["key"] = 10  // 运行时错误: assignment to entry in nil map
}

3.4 通道和 nil

       Go 的通道用于在 goroutine 之间进行通信。当通道没有被初始化时,它的默认值为 nil。一个 nil 通道无法发送或接收数据。如果你尝试通过 nil 通道进行通信,程序会阻塞,直到有一个有效的通道。

package main

import "fmt"

func main() {
    var ch chan int  // 声明一个通道,默认值为 nil
    fmt.Println(ch == nil)  // 输出: true

    // 使用 nil 通道会导致阻塞
    // ch <- 1  // 运行时会死锁
}

3.5 接口和 nil

       接口在 Go 中是一种特殊类型,它表示某种类型的集合。如果一个接口的具体值是 nil,并且该接口的类型也是 nil,那么接口本身就是 nil。但是,如果一个接口的类型非 nil 且值是 nil,那么这个接口不等于 nil

package main

import "fmt"

func main() {
    var i interface{}  // 声明一个空接口,默认值为 nil
    fmt.Println(i == nil)  // 输出: true

    var ptr *int
    i = ptr  // i 的类型是 *int,值为 nil
    fmt.Println(i == nil)  // 输出: false,因为接口类型是 *int(非 nil)

    // 将空接口值赋给 nil 的接口
    i = nil
    fmt.Println(i == nil)  // 输出: true
}

3.6 与 nil 的比较

   nil 可以与指针、切片、映射、通道和接口类型进行比较。需要注意的是,nil 是可以与这些类型的零值进行比较的,但对于不同类型的 nil,Go 会认为它们是不同的。例如,nil 切片与 nil 映射是不同的,因为它们的底层类型不同。

package main

import "fmt"

func main() {
    var p1 *int
    var p2 *string

    fmt.Println(p1 == nil)  // 输出: true
    fmt.Println(p2 == nil)  // 输出: true

    var m1 map[string]int
    var m2 map[int]string

    fmt.Println(m1 == nil)  // 输出: true
    fmt.Println(m2 == nil)  // 输出: true
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/961464.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大数据相关职位介绍之二(数据治理,数据库管理员, 数据资产管理师,数据质量专员)

大数据相关职位介绍之二&#xff08;数据治理&#xff0c;数据库管理员&#xff0c; 数据资产管理师&#xff0c;数据质量专员&#xff09; 文章目录 大数据相关职位介绍之二&#xff08;数据治理&#xff0c;数据库管理员&#xff0c; 数据资产管理师&#xff0c;数据质量专员…

编译dpdk19.08.2中example时一系列报错解决

dpdk19.08编译过程全解 dpdk 介绍问题描述编译过程执行Step 1报错一解决方式 报错二解决方式 继续执行Step 248的时候报错 49没有修改成功输入60退出 使用过程执行make报错一解决方式 继续make报错二解决方式 继续make执行生成文件helloworld报错三解决方式 执行make 完成参考链…

最优化问题 - 内点法

以下是一种循序推理的方式&#xff0c;来帮助你从基础概念出发&#xff0c;理解 内点法&#xff08;Interior-Point Method, IPM&#xff09; 是什么、为什么要用它&#xff0c;以及它是如何工作的。 1. 问题起点&#xff1a;带不等式约束的优化 假设你有一个带不等式约束的优…

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到

Linux下Ubuntun系统报错find_package(BLAS REQUIRED)找不到 这次在windows的WSL2中遇到了一个非常奇怪的错误&#xff0c;就是 CMake Error at /usr/share/cmake-3.22/Modules/FindPackageHandleStandardArgs.cmake:230 (message):Could NOT find BLAS (missing: BLAS_LIBRAR…

Ubuntu Server 安装 XFCE4桌面

Ubuntu Server没有桌面环境&#xff0c;一些软件有桌面环境使用起来才更加方便&#xff0c;所以我尝试安装桌面环境。常用的桌面环境有&#xff1a;GNOME、KDE Plasma、XFCE4等。这里我选择安装XFCE4桌面环境&#xff0c;主要因为它是一个极轻量级的桌面环境&#xff0c;适合内…

芯片AI深度实战:实战篇之vim chat

利用vim-ollama这个vim插件&#xff0c;可以在vim内和本地大模型聊天。 系列文章&#xff1a; 芯片AI深度实战&#xff1a;基础篇之Ollama-CSDN博客 芯片AI深度实战&#xff1a;基础篇之langchain-CSDN博客 芯片AI深度实战&#xff1a;实战篇之vim chat-CSDN博客 芯片AI深度…

线程概念、操作

一、背景知识 1、地址空间进一步理解 在父子进程对同一变量进行修改时发生写时拷贝&#xff0c;这时候拷贝的基本单位是4KB&#xff0c;会将该变量所在的页框全拷贝一份&#xff0c;这是因为修改该变量很有可能会修改其周围的变量&#xff08;局部性原理&#xff09;&#xf…

设置jmeter外观颜色

设置jmeter外观颜色 方法&#xff1a; 步骤一、点击顶部选项 ->外观&#xff0c;这里提供了不同的主题&#xff0c;可选自己喜欢的风格。 步骤二、选择后&#xff0c;弹框提示点击Yes。

2021 年 6 月大学英语四级考试真题(第 1 套)——纯享题目版

&#x1f3e0;个人主页&#xff1a;fo安方的博客✨ &#x1f482;个人简历&#xff1a;大家好&#xff0c;我是fo安方&#xff0c;目前中南大学MBA在读&#xff0c;也考取过HCIE Cloud Computing、CCIE Security、PMP、CISP、RHCE、CCNP RS、PEST 3等证书。&#x1f433; &…

[论文总结] 深度学习在农业领域应用论文笔记14

当下&#xff0c;深度学习在农业领域的研究热度持续攀升&#xff0c;相关论文发表量呈现出迅猛增长的态势。但繁荣背后&#xff0c;质量却不尽人意。相当一部分论文内容空洞无物&#xff0c;缺乏能够落地转化的实际价值&#xff0c;“凑数” 的痕迹十分明显。在农业信息化领域的…

LangGraph系列-1:用LangGraph构建简单聊天机器人

在快速发展的人工智能和大型语言模型&#xff08;llm&#xff09;世界中&#xff0c;开发人员不断寻求创建更灵活、更强大、更直观的人工智能代理的方法。 虽然LangChain已经改变了这个领域的游戏规则&#xff0c;允许创建复杂的链和代理&#xff0c;但对代理运行时的更复杂控制…

【hot100】刷题记录(7)-除自身数组以外的乘积

题目描述&#xff1a; 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#x…

OpenCV边沿检测(Python版)

边缘检测是图像处理中的一项重要任务&#xff0c;用于找到图像中的边界或边缘。它在计算机视觉、图像处理和模式识别等领域中具有广泛的应用。 边缘可以被定义为图像亮度、颜色或纹理的突变区域。边缘检测算法旨在识别这些变化并将其标记为边缘。边缘检测可以用于分割图像、检测…

推荐七节来自NVIDIA、Google、斯坦福的AI课程

英伟达 &#xff08;1&#xff09;在 10 分钟内构建大脑 • 探索神经网络如何使用数据进行学习。 • 了解神经元背后的数学原理。 链接&#xff1a;https://learn.nvidia.com/courses/course-detail?course_idcourse-v1:DLIT-FX-01V1 &#xff08;2&#xff09;构建视频 A…

《从因果关系的角度学习失真不变表示以用于图像恢复》学习笔记

paper&#xff1a;2303.06859 GitHub&#xff1a;lixinustc/Causal-IR-DIL: Distortion invariant feature learning for image restoration from a causality perspective 2023 CVPR 摘要 近年来&#xff0c;我们见证了深度神经网络&#xff08;DNNs&#xff09;在图像恢复…

亚博microros小车-原生ubuntu支持系列:16 机器人状态估计

本来想测试下gmapping建图&#xff0c;但是底层依赖了yahboomcar_bringup做底层的数据处理&#xff0c;所以先把依赖的工程导入。 程序启动后&#xff0c;会订阅imu和odom数据&#xff0c;过滤掉一部分的imu数据后&#xff0c;然后与odom数据进行融合&#xff0c;最后输出一个…

不背单词快捷键(不背单词键盘快捷键)

文章目录 不背单词快捷键 不背单词快捷键 ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ        ‌‍ᅟᅠ    …

VPR概述、资源

SOTA网站&#xff1a; Visual Place Recognition | Papers With Code VPR&#xff08;Visual Place Recognition&#xff09; 是计算机视觉领域的一项关键任务&#xff0c;旨在通过图像匹配和分析来识别场景或位置。它的目标是根据视觉信息判断某个场景是否与数据库中的场景匹…

MYSQL 商城系统设计 商品数据表的设计 商品 商品类别 商品选项卡 多表查询

介绍 在开发商品模块时&#xff0c;通常使用分表的方式进行查询以及关联。在通过表连接的方式进行查询。每个商品都有不同的分类&#xff0c;每个不同分类下面都有商品规格可以选择&#xff0c;每个商品分类对应商品规格都有自己的价格和库存。在实际的开发中应该给这些表进行…

代理模式 -- 学习笔记

代理模式学习笔记 什么是代理&#xff1f; 代理是一种设计模式&#xff0c;用户可以通过代理操作&#xff0c;而真正去进行处理的是我们的目标对象&#xff0c;代理可以在方法增强&#xff08;如&#xff1a;记录日志&#xff0c;添加事务&#xff0c;监控等&#xff09; 拿一…