Go语言中的指针介绍

Go语言中的指针

文章目录

  • Go语言中的指针
    • 一、Go语言中的指针介绍
      • 1.1 指针介绍
      • 1.2 基本语法
      • 1.3 声明和初始化
      • 1.4 Go 指针的3个重要概念
        • 1.4.1 指针地址(Pointer Address)
        • 1.4.2 指针类型(Pointer Type)
        • 1.4.3 指针取值(Pointer Dereferencing)
      • 1.5 获取指针的地址和解引用
      • 1.6 传递指针给函数
      • 1.7 指针的比较
      • 1.8 指针的使用注意事项
    • 二、空指针和指针的零值
    • 三、指针的应用场景
      • 3.1 传递大对象
      • 3.2 指针作为函数参数和修改函数外部变量
      • 3.3 动态分配内存
      • 3.4 函数返回指针
    • 四、new和make
      • 4.1 new
      • 4.2 make
      • 4.3 new与make的区别

一、Go语言中的指针介绍

1.1 指针介绍

指针是一个存储变量内存地址的变量。它们允许程序直接访问和操作内存中的数据,而不是对数据的副本进行操作。以下是指针的一些关键概念:

  • 内存地址: 每个变量在计算机内存中都有一个唯一的地址,指针存储了这个地址。
  • 指针变量: 用于存储其他变量地址的变量称为指针变量。
  • 取地址操作符(&): 可以使用取地址操作符&来获取变量的地址。
  • 解引用操作符(*): 可以使用解引用操作符*来访问指针所指向的变量的值。

Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int*int64*string等。

1.2 基本语法

  • var ptr *int: 声明指针变量ptr,用于指向一个int类型变量的地址。
  • &a: 获取变量a的内存地址,返回一个指向该地址的指针。
  • *ptr: 读取ptr指针指向地址的值,这个操作称为“解引用”。
  • *ptr = 100: 将100赋值给ptr指向的变量。

1.3 声明和初始化

在 Go 语言中,可以使用指针来引用任何类型的变量。指针的声明和初始化可以通过如下语法完成:

var p *int  // 声明一个指向 int 类型的指针 p
var str *string  // 声明一个指向 string 类型的指针 str

初始化指针可以通过 new 函数来分配内存并返回指针的地址:

p := new(int)  // 分配一个 int 类型的内存,并将指针 p 指向该内存

示例代码:

package mainimport "fmt"func main() {var p *intvar str *stringfmt.Printf("p: %v, str: %v\n", p, str) // 输出 p: <nil>, str: <nil>x := 10p = &x // 将指针p指向变量x的地址fmt.Printf("p: %v\n", p)   // 输出 p: 0xc0000100e0fmt.Printf("*p: %d\n", *p) // 输出 *p: 10str = new(string) // 分配一个string类型的内存,并将指针str指向该内存fmt.Printf("str: %v\n", str)   // 输出 str: 0xc000010120fmt.Printf("*str: %s\n", *str) // 输出 *str: ""*str = "Hello, Go!" // 通过指针修改字符串的值fmt.Printf("*str: %s\n", *str) // 输出 *str: Hello, Go!}

1.4 Go 指针的3个重要概念

1.4.1 指针地址(Pointer Address)
  • 在Go语言中,指针地址表示指针所指向的变量或数据在内存中的位置
  • 在Go语言中,与C/C++等语言不同,您不能直接获取指针的具体地址值,因为Go语言为了安全性和内存管理而采用了更抽象的设计。但是,您可以通过获取变量的地址来创建和使用指针,而这个地址由Go语言自动管理。
1.4.2 指针类型(Pointer Type)
  • Go语言的指针类型表示指针可以指向的数据类型
1.4.3 指针取值(Pointer Dereferencing)
  • 指针取值是指通过指针来访问其所指向的内存位置上的数据。在Go语言中,要获取指针所指向的数据的值,您需要使用解引用操作符 *

1.5 获取指针的地址和解引用

通过 & 操作符可以获取变量的地址,例如:

func main() {a := 10b := &a  // 将指针 b 指向变量 a 的地址fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*intfmt.Println(&b)                    // 0xc00000e018
}

我们来看一下b := &a的图示:取变量地址图示

使用*操作符可以解引用指针,获取指针指向的值:

 fmt.Println(*b)  // 输出指针 b 指向的值,即变量 a 的值

示例代码:

func main() {//指针取值a := 10b := &a // 取变量a的地址,将指针保存到b中fmt.Printf("type of b:%T\n", b)c := *b // 指针取值(根据指针去内存取值)fmt.Printf("type of c:%T\n", c)fmt.Printf("value of c:%v\n", c)
}

输出如下:

type of b:*int
type of c:int
value of c:10

总结: 取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。

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

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

1.6 传递指针给函数

您可以将指针作为参数传递给函数,从而可以在函数内部修改原始变量的值,而不是复制。这可以用于实现函数的副作用。

func modify1(x int) {x = 100
}func modify2(x *int) {*x = 100
}func main() {a := 10modify1(a)fmt.Println(a) // 10modify2(&a)fmt.Println(a) // 100
}

1.7 指针的比较

您可以使用==!=运算符来比较指针。它们将比较指针是否引用相同的内存地址。

var x int = 42
var p *int  // 声明一个整数指针
p = &x      // 将变量x的地址分配给指针p
fmt.Println(p == &x) // true,p和&x都指向相同的内存地址

1.8 指针的使用注意事项

  • 谨慎使用指针,以避免悬挂指针(dangling pointers)和内存泄漏等问题。
  • 在Go中,指针通常用于传递大型数据结构,以避免复制数据。
  • Go没有指针运算(如C/C++中的指针算术运算),因此您不能像C/C++那样执行指针加法和减法操作。

二、空指针和指针的零值

  • **指针的零值:**如果您声明了一个指针但没有初始化它,它将具有零值,即nil
  • **空指针:**如果指针没有指向任何有效的内存地址,它将具有nil值,表示空指针。在使用指针之前,通常会检查指针是否为nil
package mainimport "fmt"func main() {var p *stringfmt.Println(p)fmt.Printf("p的值是%s/n", p)if p != nil {fmt.Println("非空")} else {fmt.Println("空值")}
}

三、指针的应用场景

3.1 传递大对象

在函数参数传递时,如果直接传递大对象的副本,会产生额外的内存开销。通过传递指针,可以避免复制整个对象,提高程序的性能。

示例代码:

 package mainimport "fmt"type BigObject struct {// 大对象的定义...}func processObject(obj *BigObject) {// 对大对象进行处理...}func main() {obj := BigObject{}processObject(&obj) // 传递大对象的指针}

3.2 指针作为函数参数和修改函数外部变量

在 Go 语言中,函数的参数传递默认是值传递。通过指针传递,函数可以修改函数外部的变量。这在需要修改外部变量的值时非常有用,特别是在处理复杂数据结构或需要对全局状态进行修改的情况下。

示例代码:

 package mainimport "fmt"func modifyValue(ptr *int) {*ptr = 30 // 修改指针指向的值}func main() {x := 10modifyValue(&x) // 传递x的地址给modifyValue函数fmt.Println(x) // 输出修改后的x的值,即30}

3.3 动态分配内存

指针的另一个重要应用是动态分配内存。通过 new 函数可以在堆上动态分配内存,避免了在栈上分配固定大小的内存空间的限制。这对于需要返回动态分配的数据或创建复杂数据结构非常有用。

示例代码:

 package mainimport "fmt"type ComplexStruct struct {// 复杂数据结构的定义...}func createComplexStruct() *ComplexStruct {cs := new(ComplexStruct) // 动态分配内存并返回指针// 初始化复杂数据结构...return cs}func main() {obj := createComplexStruct()// 对动态分配的数据结构进行操作...}

3.4 函数返回指针

在函数中返回指针可以将函数内部创建的变量的地址传递给调用者。这样做可以避免复制整个变量,并允许调用者直接访问和修改函数内部的数据。

示例代码:

 package mainimport "fmt"func createValue() *int {x := 10 // 在函数内部创建变量return &x // 返回变量的地址}func main() {p := createValue()fmt.Println(*p) // 输出通过指针访问的函数内部变量的值,即10}

四、new和make

我们先来看一个例子:

func main() {var a *int*a = 100fmt.Println(*a)var b map[string]intb["测试"] = 100fmt.Println(b)
}

执行上面的代码会引发panic,为什么呢? 在Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储。而对于值类型的声明不需要分配内存空间,是因为它们在声明的时候已经默认分配好了内存空间。要分配内存,就引出来今天的new和make。 Go语言中new和make是内建的两个函数,主要用来分配内存。

4.1 new

new是一个内置的函数,它的函数签名如下:

func new(Type) *Type

其中,

  • Type表示类型,new函数只接受一个参数,这个参数是一个类型
  • *Type表示类型指针,new函数返回一个指向该类型内存地址的指针。

new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。举个例子:

func main() {a := new(int)b := new(bool)fmt.Printf("%T\n", a) // *intfmt.Printf("%T\n", b) // *boolfmt.Println(*a)       // 0fmt.Println(*b)       // false
}	

本节开始的示例代码中var a *int只是声明了一个指针变量a但是没有初始化,指针作为引用类型需要初始化后才会拥有内存空间,才可以给它赋值。应该按照如下方式使用内置的new函数对a进行初始化之后就可以正常对其赋值了:

func main() {var a *inta = new(int)*a = 10fmt.Println(*a)
}

4.2 make

make也是用于内存分配的,区别于new,它只用于slice、map以及channel的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了。make函数的函数签名如下:

func make(t Type, size ...IntegerType) Type

make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。这个我们在上一章中都有说明,关于channel我们会在后续的章节详细说明。

本节开始的示例中var b map[string]int只是声明变量b是一个map类型的变量,需要像下面的示例代码一样使用make函数进行初始化操作之后,才能对其进行键值对赋值:

func main() {var b map[string]intb = make(map[string]int, 10)b["测试"] = 100fmt.Println(b)
}

4.3 new与make的区别

  1. 二者都是用来做内存分配的。
  2. make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
  3. 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day-08 基于 Docker安装 Nginx 镜像-负载均衡

1、反向代理后&#xff0c;自然而然就引出了负载均衡,下面简单实现负载均衡的效果; 2、实现该效果需要再添加一个 Nginx &#xff0c;所以要增加一个文件夹。 /home|---mutou|----nginx|----conf.d|----html|----conf.d2|----html3 1.创建 html3 文件夹&#xff0c; 新建 index…

mysql面试题20:有哪些合适的分布式主键方案

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:有哪些合适的分布式主键方案? UUID:UUID通常是由一个二进制的128位整数表示,可以保证全局的唯一性。在Java中,可以通过UUID类生成一个UUID。例…

数据结构P46(2-1~2-4)

2-1编写算法查找顺序表中值最小的结点&#xff0c;并删除该结点 #include <stdio.h> #include <stdlib.h> typedef int DataType; struct List {int Max;//最大元素 int n;//实际元素个数 DataType *elem;//首地址 }; typedef struct List*SeqList;//顺序表类型定…

Linux系统之安装cook菜谱工具

Linux系统之安装cook菜谱工具 一、cook菜谱工具介绍二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本3.3 检查系统是否安装pnpm 四、部署Node.js环境4.1 下载Node.js安装包4.2 解压Node.js安装包4.3 复制二进制…

为什么网络安全明明缺口很大,却看起来招聘很少呢?

2023 年我国网络空间安全人才数量缺口超过了 140 万&#xff0c;就业人数却只有 10 多万&#xff0c;缺口高达了 93%。这里就有人会问了&#xff1a; 1、网络安全行业为什么这么缺人&#xff1f; 2、明明人才那么稀缺&#xff0c;为什么招聘时招安全的人员却没有那么多呢&…

一文解释mapState的来龙去脉

mapState Vuex 提供的辅助函数之一&#xff0c;将 store 中的状态映射到组件的计算属性中&#xff0c;使得在组件中可以轻松地访问 Vuex store 中的状态值 MapState(映射状态) 在我们的 Count.vue 组件中&#xff0c;可以使用 mapState 来更简洁地获取 count 的状态值 首先&…

【c++_containers】10分钟带你学会list

前言 链表作为一个像是用“链子”链接起来的容器&#xff0c;在数据的存储等方面极为便捷。虽然单链表单独在实际的应用中没用什么作用&#xff0c;但是当他可以结合其他结构&#xff0c;比如哈希桶之类的。不过今天学习的list其实是一个带头双向链表。 言归正传&#xff0c;让…

【ARM】(1)架构简介

前言 ARM既可以认为是一个公司的名字&#xff0c;也可以认为是对一类微处理器的通称&#xff0c;还可以认为是一种技术的名字。 ARM公司是专门从事基于RISC技术芯片设计开发的公司&#xff0c;作为知识产权&#xff08;IP&#xff09;供应商&#xff0c;本身不直接从事芯片生产…

iphone怎么传大量照片到电脑,这四招你要学会

如果你喜欢用iPhone拍照、总会遇到要把大量照片从iPhone传输到电脑的情况&#xff0c;要是你对这方面不熟悉就很容易浪费时间。下面小编就介绍几种方法可以快速高效的传大量照片到电脑上去。 iPhone传输照片到电脑 方法一&#xff1a;使用iMazing传输 推荐度★★★★★ 有了i…

不断优化的素数算法

前言&#xff1a;素数判断是算法中重要的一环&#xff0c;掌握优秀的素数判断方法是算法player的必修课。本文介绍的是由简到繁的素数算法&#xff0c;便于初学者从入门到精通。 素数&#xff08;质数&#xff09;&#xff1a;只能被 1 和它本身整除的数称作素数&#xff0c;如…

overleaf在线编辑工具使用教程

文章目录 1 用 orcid注册overleaf获取模板2 使用模板 1 用 orcid注册overleaf获取模板 通常来说&#xff0c;在期刊投稿网站information for author中找template 。下载压缩包后上传到over leaf中。 加入找不到官方模板&#xff0c;用overleaf中的 2 使用模板 .bib文件&…

需求变化频繁的情况下,如何实施自动化测试

一.通常来说&#xff0c;具备以下3个主要条件才能开展自动化测试工作: 1.需求变动不频繁 自动化测试脚本变化的频率决定了自动化测试的维护成本。如果需求变动过于频繁&#xff0c;那么测试人员就需要根据变动的需求来不断地更新自动化测试用例&#xff0c;从而适应新的功能。…

【Blender实景合成】会跳舞的神里绫华

效果预览 本文将介绍Blender用于实景合成的工作流程。 先看效果&#xff1a; 神里绫华爬上了我的办公桌 模型和动作资源准备 角色模型 本次主要使用的是原神游戏中&#xff0c;神里绫华的角色模型&#xff0c;该模型米哈游在模之屋网站上进行开源。 下载地址&#xff1a;ht…

react项目从webpack迁移到vite的解决方案

虽然webpack是前端工程编译工具的王者&#xff0c;但是最近vite牛逼吹的震天响&#xff0c;说什么开发/生产打包速度甩webpack 100条街。不管是不是事实&#xff0c;总得尝试一下吧。 于是说干就干&#xff0c;在网上找了很多资料&#xff0c;终于搞定了&#xff0c;以下就是r…

spark on hive

需要提前搭建好hive&#xff0c;并对hive进行配置。 1、将hive的配置文件添加到spark的目录下 cp $HIVE_HOME/conf/hive-site.xml $SPARK_HOME/conf2、开启hive的hivemetastore服务 提前创建好启动日志存放路径 mkdir $HIVE_HOME/logStart nohup /usr/local/lib/apache-hi…

【AI视野·今日CV 计算机视觉论文速览 第262期】Fri, 6 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Fri, 6 Oct 2023 Totally 73 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Improved Baselines with Visual Instruction Tuning Authors Haotian Liu, Chunyuan Li, Yuheng Li, Yong Jae Lee大型多模…

多普勒频率相关内容介绍

图1 多普勒效应 1、径向速度 径向速度是作用于雷达或远离雷达的速度的一部分。 图2 不同的速度 2、喷气发动机调制 JEM是涡轮机的压缩机叶片的旋转的多普勒频率。 3、多普勒困境 最大无模糊范围需要尽可能低的PRF&#xff1b; 最大无模糊速度需要尽可能高的PRF&#xff1b…

Labview 实战 99乘法表

基于新手小白&#xff0c;使用Labview实现99乘法表&#xff0c;敢于发表自己的一点方法&#xff0c;还请各位大侠放过&#xff01; 如下&#xff1a; 运行效果如下&#xff1a; 思路为&#xff1a;将要显示出来的数据&#xff0c;全部转换为字符串形式&#xff0c;再塞入到数组…

Suricata + Wireshark离线流量日志分析

Suricata 环境搭建&#xff1a;基于Ubuntu坏境下的Suricata坏境搭建_奈何&#xff20;_&#xff20;的博客-CSDN博客 suricata&#xff1a;监控日志 wireshark:监控流量 同时使用需要降噪&#xff0c;因为规则有许多重叠 题目及要求我打包上传了&#xff0c;有需要的同学自…

Vmware 静态网络配置

概述 仅主机模式&#xff08;VMware1&#xff09;&#xff1a;使用host-only的方式是不能和外界通信的&#xff0c;只能够和本机的物理网卡通信 桥接&#xff08;VMnet0&#xff09;&#xff1a;使用桥接的方式使得自己的虚拟机和自己的真实机网卡在同一个网段 NAT&#xff0…