Go语言基本语法(三)指针

什么是指针

在Go语言中,"指针是一种存储变量内存地址的数据类型",意味着指针本身是一个特殊的变量,它的值不是数据本身,而是另一个变量在计算机内存中的位置(地址)。形象地说,就像存放了一个数据项在内存中的“门牌号”。

"它允许程序直接操纵内存中的数据",这句话意味着通过指针,程序能够绕过变量本身,直接到达变量在内存中的存储位置,并对那里的数据进行读取或修改。这种能力非常重要,因为:

1. **效率**:当数据结构很大时(如大型数组或结构体),直接操作其内存地址可以避免复制整个数据结构,从而节省时间和空间。
2. **灵活性**:在函数调用时,传递数据的指针而非数据本身的副本,可以使函数有能力修改调用者的数据,这在很多场景下是必要的,比如更新共享状态或配置。
3. **控制权**:指针提供了底层的内存访问能力,这对于系统编程、性能优化和一些高级数据结构的实现至关重要。

例如,如果你有一个很大的数组,想要修改其中的一个元素,直接通过指针定位到那个元素的内存位置并修改它,比起先复制整个数组或结构到函数内部再修改,显然更高效。此外,通过指针,你还可以创建动态数据结构,如链表、树等,因为每个节点可以指向下一个节点的位置。

相比之下,C/C++的指针以其高度灵活性闻名,允许自由的偏移和运算,这为系统级编程和大数据操作提供了强大工具,也是其高性能的来源。然而,这种灵活性也带来了风险,如内存泄漏、指针悬挂、缓冲区溢出等问题,这些安全漏洞常常成为黑客攻击的入口,也是操作系统频繁更新修复的原因之一。

指针的概念

指针地址和指针类型

在Go语言中,理解和操作指针时,"指针地址"和"指针类型"是两个核心概念:

指针地址

指针地址指的是一个变量在内存中的实际存储位置。在Go语言中,你可以使用`&`操作符来获取一个变量的地址。这个地址是一个无符号整数,但它通常以十六进制形式显示,代表了变量在内存中的确切位置。例如:

var num int = 10
var ptr *int = &num

在这个例子中,`&num`就是获取变量`num`的内存地址,并将其赋值给指针变量`ptr`,`ptr`的类型就是指向`int`类型的指针,即`*int`。

指针类型

指针类型定义了指针所指向的数据类型。每个指针都有一个明确的类型,它决定了该指针可以指向哪种类型的变量。在Go语言中,指针类型的声明语法是在数据类型前加上星号`*`。例如,`*int`表示一个指向整型变量的指针,`*string`表示一个指向字符串变量的指针。

指针类型的重要性在于,它确保了类型安全,意味着你不能错误地将一个类型的指针赋值给另一个不匹配类型的指针变量,除非通过类型断言或类型转换(在类型兼容的情况下)。

指针的使用

package main
import (
    "fmt"
)
func main() {
    var money int = 156
    var str string = "ppp"
    fmt.Printf("%p %p", &money, &str)
}

运行结果:

指针取值

当使用`&`操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用`*`操作符,也就是指针取值,代码如下:

package main
import (
    "fmt"
)
func main() {
    // 准备一个字符串类型
    var day01 = "Hello World"
    // 对字符串取地址, tem类型为*string
    tem := &day01
    // 打印tem的类型
    fmt.Printf("tem type: %T\n", tem)
    // 打印ptr的指针地址
    fmt.Printf("address: %p\n", tem)
    // 对指针进行取值操作
    value := *tem
    // 取值后的类型
    fmt.Printf("value type: %T\n", value)
    // 指针取值后就是指向变量的值
    fmt.Printf("value: %s\n", value)
}

运行结果:


取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。

使用指针修改值

通过指针不仅可以取值,也可以修改值:

示例1:

package main

import "fmt"

// 交换函数
func swap(a, b *int) {

    // 取a指针的值, 赋给临时变量t
    t := *a

    // 取b指针的值, 赋给a指针指向的变量,
    *a = *b

    // 将a指针的值赋给b指针指向的变量
    *b = t
}

func main() {

// 准备两个变量, 赋值1和2
    x, y := 1, 2

    // 交换变量值
    swap(&x, &y)

    // 输出变量值
    fmt.Println(x, y)
}

运行结果:

*操作符作为右值时,意义是取指针的值,作为左值时,也就是放在赋值操作符的左边时,表示 a 指针指向的变量。其实归纳起来,*操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。

如果在 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)
}

运行结果:

结果表明,交换是不成功的。上面代码中的 swap() 函数交换的是 a 和 b 的地址,在交换完毕后,a 和 b 的变量值确实被交换。但和 a、b 关联的两个变量并没有实际关联。这就像写有两座房子的卡片放在桌上一字摊开,交换两座房子的卡片后并不会对两座房子有任何影响。

示例2:

package main

import "fmt"

func main() {
	// 声明一个整型变量
	var num int = 10

	// 声明一个指向整型的指针
	var ptr = &num

	//将50赋给ptr指针指向的变量
	*ptr = 50

	fmt.Println(num) //输出50
	fmt.Println(*ptr) //输出50
}

指针的指针

指针的指针,顾名思义,就是一个指针变量,它的值是另一个指针的地址。在Go语言中,正如你可以声明指向任何基本类型或复合类型的指针一样,你也可以声明指向指针的指针。这种多级指针可以用来表示更加复杂的内存关系,或者在某些情况下,为了通过函数传递指针并修改指针本身(而不仅仅是指针指向的值)时使用。

声明与初始化

假设你有一个整型指针*int,那么一个指向这个整型指针的指针就会是**int。声明和初始化一个指针的指针的方式如下:

package main
import "fmt"

func main() {
    // 声明一个整型变量
    var num int = 10
    
    // 声明一个指向整型的指针
    var ptr *int = &num
    
    // 声明一个指向指针的指针(即指针的指针)
    var ptrToPtr **int = &ptr
    
    // 修改通过指针的指针访问的值
    **ptrToPtr = 20
    
    fmt.Println(num)       // 输出: 20
    fmt.Println(*ptr)      // 输出: 20
    fmt.Println(*ptrToPtr) // 输出: 地址,显示ptr的地址
}

使用场景

指针的指针在实际编程中的使用相对较少,但在某些特定场景下非常有用,例如:

  • 当你需要通过函数修改一个指针变量本身(比如让指针指向不同的内存地址)时。
  • 在配置或设置结构体的指针成员时,特别是这些成员也是指针类型。
  • 在某些高级的数据结构或底层系统编程中,用于复杂的数据操作和内存管理。

new() 函数

Go语言还提供了另外一种方法来创建指针变量,格式如下:

new(类型)

一般这样写:

str := new(string)
*str = "Go语言教程"
fmt.Println(*str)

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

参考文章:(6 封私信 / 42 条消息) Go 语言怎么定义和使用指针? - 知乎 (zhihu.com)

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

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

相关文章

multipass和multipassd命令的区别

multipassd通常是multipass服务的后台守护进程,它负责管理和控制虚拟机实例。 命令区别 例: multipass restart my-vm 这个命令用于重启Multipass中的虚拟机实例。例如有一个名为my-vm的虚拟机实例。 multipassd restart 这会重新启动Multipass后台…

一文学会最强大的 node.js 后端框架 nest.js

文章目录 nest cli项目基本结构IOC & DI基础注册值注册时 key 的管理动态注册类工厂函数方式注册设置别名导出 provider 模块功能模块模块的导入导出模块类中使用注入全局模块动态模块 中间件定义中间件注册中间件MiddlewareConsumer 类全局中间件 异常过滤器抛出异常自定义…

华三配置DHCP(基础)

华三交换机配置DHCP&#xff08;基础&#xff09; 1.组网拓扑图&#xff08;交换机-PC&#xff09; 2.通过交换机开启DHCP功能&#xff0c;使PC自动获取192.168.10.0&#xff08;vlan10&#xff09;网段地址 2.使用命令 <H3C>system-view [H3C]vlan 10&#xff08;建立…

Python_4-远程连接Linux

文章目录 使用Python通过SSH自动化Linux主机管理代码执行ls结果&#xff1a;文件传输&#xff1a; 使用Python通过SSH自动化Linux主机管理 在系统管理与自动化运维中&#xff0c;SSH&#xff08;Secure Shell&#xff09;是一个常用的协议&#xff0c;用于安全地访问远程计算机…

【0day】湖南建研工程质量检测系统InstrumentUsageRecordExport接口处存在任意文件读取漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

发那科Fanuc数控网络IP配置设定教程

1.在主面板如图按system键&#xff0c;进入系统界面 2.按右翻页切换键&#xff0c;切换到内嵌选项&#xff0c;按内嵌按钮跳转至设置IP界面&#xff0c;设置ip 3.按Focas2按钮&#xff0c;跳转至设置端口号和超时时间界面。设置端口号和时间之后&#xff0c;重启设备。注意&…

MES生产系统与数字孪生双重结合:智慧制造工厂的新引擎

随着数字化浪潮的推动&#xff0c;制造行业正在经历着前所未有的变革。在这个变革的浪潮中&#xff0c;MES生产制造系统与数字孪生技术的深度融合成为了制造工厂未来发展的核心驱动力。这种结合不仅提升了生产效率&#xff0c;优化了资源配置&#xff0c;降低了运营成本&#x…

2024年 Java 面试八股文——SpringCloud篇

目录 1.Spring Cloud Alibaba 中的 Nacos 是如何进行服务注册和发现的&#xff1f; 2.Spring Cloud Alibaba Sentinel 的流量控制规则有哪些&#xff1f; 3.Spring Cloud Alibaba 中如何实现分布式配置管理&#xff1f; 4.Spring Cloud Alibaba RocketMQ 的主要特点有哪些&…

HCIP的学习(12)

OSPF优化 ​ OSPF的优化主要目的是为了减少LSA的更新量。 路由汇总-----可以减少骨干区域的LSA数量特殊区域-----可以减少非骨干区域的LSA数量 OSPF路由汇总 域间路由汇总-----在ABR设备上进行操作 [GS-R2-ospf-1-area-0.0.0.1]abr-summary 192.168.0.0 255.255.224.0 [GS-…

什么是抖音橱窗?它和抖音小店有什么区别?普通人更适合做哪个?

大家好&#xff0c;我是电商糖果 相信有很多想在抖音卖货的朋友&#xff0c;都会搞不清抖音橱窗是什么&#xff1f; 甚至会把它和抖音小店当成一个项目&#xff0c;也不知道哪个更适合自己。 自己越了解发现越迷糊&#xff0c;有的说不需要直播&#xff0c;粉丝&#xff0c;…

汇智知了堂鸿蒙课程全新升级,权威师资引领AI新纪元

在人工智能飞速发展的今天&#xff0c;汇智知了堂紧跟时代步伐&#xff0c;全面升级鸿蒙课程&#xff0c;以权威师资、实战导向、互动教学、资源支持为核心&#xff0c;为广大学员带来前所未有的学习体验&#xff01; 首先&#xff0c;汇智知了堂鸿蒙课程汇聚了业内知名专家&…

软件设计师-应用技术-数据流图题1

基础知识及技巧&#xff1a; 0. 概念&#xff1a; 在结构化分析中&#xff0c;数据流图用来记录系统中的数据和数据在特定的过程中的流动&#xff0c;即数据如何被采集、处理、保存和使用的(围绕信息系统的功能)。 1. 元素实例&#xff1a; 补充知识&#xff1a;** 外部实体…

sqlx执行案例

SQLx简介 SQLx是Rust语言中的一个异步SQL数据库连接库&#xff0c;它支持多种数据库&#xff0c;如PostgreSQL、MySQL和SQLite。SQLx提供了简单的API和异步执行查询的能力&#xff0c;使得Rust程序员可以轻松地与数据库交互1。 本章节以PostgreSQL为例。 目录结构 cargo.tom…

区块链开发用的是哪种编程语言?

区块链技术作为近年来备受瞩目的新兴技术之一&#xff0c;其核心的特性之一就是去中心化、安全性高、透明度高和可扩展性强。而区块链的开发语言则是实现这一技术的关键因素之一。那么&#xff0c;区块链开发语言是哪一种编程语言呢&#xff1f; 一、区块链开发语言的特点和选…

五一热度最大,销量最高的十大随身WiFi!某东、某宝倾力推荐!2024随身wifi靠谱品牌推荐!随身wifi怎么选?

还在争论谁才是最强的随身WiFi&#xff1f;要我说别再争了&#xff01;直接用事实说话&#xff01;看看五一小长假期间&#xff0c;消费者购买最多、评价最好的十款随身WiFi&#xff01;数据综合了某宝、某东、某多多&#xff0c;绝对真实可靠&#xff01; 第一名&#xff1a;格…

今日详解,教你如何不直播在视频号卖货

大家好&#xff0c;我是电商笨笨熊 视频号作为背靠微信的平台&#xff0c;从不需要考虑自身的流量问题&#xff0c; 因此在视频号推出之后就有大批的主播从其他平台转入视频号&#xff1b; 而这时候很多普通人应该也发现了新的机会&#xff0c;不再去内卷抖音、快手直播&…

Java Jackson-jr 库使用介绍

介绍 Jackson-jr 是一个轻量级的Java JSON 处理库。这个库被设计用来替代 Jackson 的复杂性。对比 Jackson 的复杂 API&#xff0c;Jackson-jr 的启动速度更快&#xff0c;包大小更小。 虽然Jackson databind&#xff08;如ObjectMapper&#xff09;是通用数据绑定的良好选择…

[SaaS]建筑领域的sd应用

AirchiDesignhttp://www.aiarchi.art/#/建筑学长——千万建筑师的资源库和AI绘图创作平台建筑学长官网,为青年设计师建立的线上资源共享及AI绘图创作渲染平台,免费提供海量设计案例、CAD图纸、SU模型、PS素材、软件插件下载,提供丰富的设计软件教学与灵感参考素材图库。https:/…

贵州航天电科:“小程序+二维码”,消防器材巡检系统上线

近日&#xff0c;网信部完成轻量化的二维码巡检功能&#xff0c;消防器材巡检系统上线&#xff01;通过“小程序二维码”结合方式&#xff0c;实现灭火器、消火栓等消防器材全面巡检。 前期&#xff0c;网信部联合行政保卫部开展消防器材台账信息数据核查&#xff0c;确保台账信…

Unity 性能优化之GPU Instancing(五)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、GPU Instancing使用方法二、使用GPU Instancing的条件三、GPU Instancing弊端四、注意五、检查是否成功总结 前言 GPU Instancing也是一种Draw call…