Golang指针详解

news2024/11/25 9:26:39

要搞明白Go语言中的指针需要先知道3个概念:指针地址、指针类型和指针取值。

指针介绍

我们知道变量是用来存储数据的,变量的本质是给存储数据的内存地址起了一个好记的别名。比如我们定义了一个变量 a := 10 ,这个时候可以直接通过 a 这个变量来读取内存中保存的 10 这个值。
在计算机底层 a 这个变量其实对应了一个内存地址。
指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另
一个变量的内存地址。

 

Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。

Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)*(根据地址取值)

指针地址和指针类型

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go 语言中使用&字符放在变量前面对变量进行取地址操作。
Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int、*int64、*string等。
取变量指针的语法如下:
 ptr := &v    // v的类型为T
 v:代表被取地址的变量,类型为T
    ptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。

 

package main

import "fmt"

func main() {

	//在计算机底层 a 这个变量其实对应了一个内存地址
	var a int = 1
	fmt.Printf("a的值为%v a的类型为%T a的内存地址为%v\n",a,a,&a)
	
	// 指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址
	var b int = 10
	c := &b //c指针变量   c的类型 *int(指针类型)
	fmt.Printf("b的值为%v b的类型为%T b的内存地址为%v\n",b,b,&b)
	fmt.Printf("c的值 %v c的类型%T ", c, c)

	//每一个变量都有自己的内存地址
	var f int = 20
	d := &f
	fmt.Printf("f的值 %v f的类型%T f的地址%p\n", f, f, &f)
	fmt.Printf("d的值 %v d的类型%T d的地址%p", d, d, &d)
	



}

b := &a的图示: 

 指针取值

在对普通变量使用&操作符取地址后会获得这个变量的指针,然后可以对指针使用*操作,也就是指针取值,代码如下。

package main

import "fmt"

func main() {
	var e int = 30
	var d *int = &e
	// d := &e
	// *d :表示取出d这个变量对应的内存地址的值
	fmt.Println(d)
	fmt.Println(&e)
	fmt.Println(*d)  

	*d = 40			//改变d这个变量对应的内存地址的值(之前所赋的是e的内存地址的值)
	fmt.Println(e)
	

}

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

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

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

指针传值示例

package main

import "fmt"

func fn1(x int) {
	x = 10
	// fmt.Println(x)
}

func fn2(x *int) {
	*x = 40
	// fmt.Println(*x)
}

func main() {
	var a = 5
	fn1(a)
	fmt.Println(a) //5;方法内的值并未改变a的内存地址,其原本的值所以没有变化
	fn2(&a)
	fmt.Println(a) //40 ;方法内传入的是指针类型,传入a改变了指针对应的内存地址,导致取指针的值变化了

}

 new和make

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

make

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

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

make函数是无可替代的,我们在使用slice、map以及channel的时候,都需要使用make进行初始化,然后才可以对它们进行操作。

 

package main

import "fmt"

func main() {


	// var teseDemo map[string]string  错误
	// teseDemo["卫宫士郎"] = "sabar"
	// fmt.Println(teseDemo)

	var teseDemo = make(map[string]string)
	teseDemo["远坂应"] = "archer"
	fmt.Println(teseDemo)

	var demo []int
	demo = append(demo,1 ) // qppend是可以实现的,但是如果是直接demo[0] = 1
	fmt.Println(demo[0])

	// var demo1 []int
	// demo1[0] = 2 // 如果是直接demo[0] = 1,会因为没有分配内存而panic
	// fmt.Println(demo[0])

	var demo1 = make([]int, 4,4)
	demo1[1] = 12
	fmt.Print(demo1)

	//指针也是引用数据类型 不分配内存空间也会报错  错误
	// var a *int 
	// *a = 100
	// fmt.Println(*a)
}

new

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

    func new(Type) *Type

其中,

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

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

	var a *int
	a = new(int)
	*a = 10
	fmt.Println(*a) //0

	var b = new(bool)
	fmt.Println(*b) //false

package main

import "fmt"

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

 

new与make的区别 

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IPv6 over IPv4

IPv6 over IPv4隧道简介 IPv6 over IPv4隧道可实现IPv6网络孤岛之间通过IPv4网络互连。由于IPv4地址的枯竭和IPv6的先进性,IPv4过渡为IPv6势在必行。因为IPv6与IPv4的不兼容性,所以需要对原有的IPv4设备进行替换。但是如果贸然将IPv4设备大量替换所需成…

明晚直播:可重构计算芯片的AI创新应用分享!

大模型技术的不断升级及应用落地,正在推动人工智能技术发展进入新的阶段,而智能化快速增长和发展的市场对芯片提出了更高的要求:高算力、高性能、灵活性、安全性。可重构计算区别于传统CPU、GPU,以指令驱动的串行执行方式&#xf…

SpringBoot 集成 Elasticsearch

一、版本 spring-boot版本:2.3.7.RELEASEElasticsearch7.8.0版本说明详见 二、Elasticsearch 下载和安装 Elasticsearch 下载 kibana下载 ik分词器下载 配置IK分词器 2.1 解压,在elasticsearch-7.8.0\plugins 路径下新建ik目录 2.2 将ik分词器解压放…

【VB6|第21期】检查SqlServer数据库置疑损坏的小工具(含源码)

日期:2023年7月25日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方&#xf…

Java反射类private私有变量Map并赋值

Java反射类private私有变量Map并赋值 import java.util.LinkedHashMap; import java.util.Map;public class MyObj {private String KEY "NAME";//目标是通过反射在外部访问cacheprivate Map<String, String> cache new LinkedHashMap<>();public MyOb…

mac 删除自带的ABC输入法保留一个搜狗输入法,搜狗配置一下可以减少很多的敲击键盘和鼠标点击次数

0. 背景 对于开发者来说&#xff0c;经常被中英文切换输入法所困扰&#xff0c;我这边有一个方法&#xff0c;删除mac默认的ABC输入法 仅仅保留搜狗一个输入法&#xff0c;配置一下搜狗输入&#xff1a;哪些指定为英文输入&#xff0c;哪些指定为中文输入&#xff08;符号也可…

七、Kafka源码分析之网络通信

1、生产者网络设计 架构设计图 2、生产者消息缓存机制 1、RecordAccumulator 将消息缓存到RecordAccumulator收集器中, 最后判断是否要发送。这个加入消息收集器&#xff0c;首先得从 Deque 里找到自己的目标分区&#xff0c;如果没有就新建一个批量消息 Deque 加进入 2、消…

限幅器(信捷PLC C语言FC功能函数)

关于限幅器的算法介绍,请参考下面博客文章,这里不再赘述,受水平和能力所限文中难免出现错误和不足之处,欢迎大家批评指正。 限幅器算法介绍 PLC信号处理系列之限幅器(Saturation)_RXXW_Dor的博客-CSDN博客TITLE=限幅器VAR_INPUTrX:REAL;// 输出值// 上限到达 FALSE: Up…

RT thread 之 Nand flash 读写过程分析

文章目录 前言&#xff1a;什么是Nand Flash&#xff1f;1、Nand Flash 读取步骤2、从主存读到Cache2.1 在标准spi接口下读取过程2.2 测试时序&#xff08;SPI频率30MHz&#xff09; 3.从Cache读取数据3.1在标准spi接口读取过程测试时序 前言&#xff1a;什么是Nand Flash&…

服务器数据恢复-误操作导致存储VDisk丢失的数据恢复案例

服务器数据恢复环境&#xff1a; IBM某型号存储&#xff1b; Solaris操作系统&#xff0c;部署Oracle数据库。 服务器故障&#xff1a; 重建MDisk导致对应的存储池中的VDisk丢失&#xff0c;导致Solaris操作系统中的Oracle数据库无法使用。 服务器数据恢复过程&#xff1a; 1、…

【如何训练一个中英翻译模型】LSTM机器翻译模型部署之ncnn(python)(五)

系列文章 【如何训练一个中英翻译模型】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 【如何训练一个中英翻译模型】LSTM机器翻译模型训练与保存&#xff08;二&#xff09; 【如何训练一个中英翻译模型】LSTM机器翻译模型部署&#xff08;三&#xff09; 【如何训练…

MTK系统启动流程

MTK系统启动流程 boot rom -> preloader ->lk ->kernel ->Native -> Android 1、Boot rom:系统开机&#xff0c;最先执行的是固化在芯片内部的bootrom&#xff0c;其作用主要有 a.初始化ISRAM和EMMC b.当系统全擦后 &#xff0c;也会配置USB&#xff0c;用来仿…

Android:RecyclerView封装,打造列表极简加载

前言 mBinding.recycler.linear().divider().set<OrdinaryListBean> {addLayout(R.layout.layout_ordinary_item)}.setList(getList()) 如果我要说&#xff0c;除了数据和布局之外&#xff0c;以上的几行代码&#xff0c;就实现了一个列表加载&#xff0c;有老铁会相信…

T形积木(T puzzle)

目录 积木绘制 积木拼接 练习 1. 停止标志 2. 跳跃旋转 3. 小步平移 积木绘制 &#xff08;1&#xff09;复数欧拉公式&#xff1a; &#xff08;2&#xff09;复数的极坐标形式&#xff1a; 其中 &#xff08;3&#xff09;T形积木问题利用了复数乘以将该复数值旋转b角的…

Spring源码解析(五):循环依赖

Spring源码系列文章 Spring源码解析(一)&#xff1a;环境搭建 Spring源码解析(二)&#xff1a;bean容器的创建、默认后置处理器、扫描包路径bean Spring源码解析(三)&#xff1a;bean容器的刷新 Spring源码解析(四)&#xff1a;单例bean的创建流程 Spring源码解析(五)&…

Ubuntu 20.04.4 LTS安装Terminator终端(Linux系统推荐)

Terminator终端可以在一个窗口中创建多个终端&#xff0c;并且可以水平、垂直分割&#xff0c;运行ROS时很方便。 sudo apt install terminator这样安装完成后&#xff0c;使用快捷键Ctrl Alt T打开的就是新安装的terminator终端&#xff0c;可以使用以下方法仍然打开ubuntu默…

IAR for STM8L标准库基于DMP库驱动MPU6050

IAR for STM8L标准库基于DMP库驱动MPU6050 ✨移植到STM8上&#xff0c;主要对接的是I2C对应的接口函数&#xff0c;也没有什么难度&#xff0c;该型号目前不属于新设计推荐的型号了&#xff0c;如果使用DMP库最好还是需要配合磁力计才能输出比较稳定的数据&#xff0c;使用MPU9…

【App管理04-Bug修正 Objective-C语言】

一、咱们刚才已经把这个给大家做完了吧 1.这个Label怎么显示到上面去了, 我们现在是把它加到我们的控制器的View里面吧 我们看一下这个坐标是怎么算的,来,我们找一个坐标, 咱们的坐标,是不是用这个View的frame,减的吧 来,咱们在这里,输出一下这个Frame,看一下吧 在…

idea的Plugins中搜索不到插件

1、ctrlalts 打开设置 ; 2、搜索框输入plugins; 3、点击plugins; 4、点齿轮按钮&#xff0c;选择HTTP Proxy settings; 如下操作&#xff1a; 5、刷新DNS&#xff0c;ipconfig /flushdns 6、重新打开idea 的plugins 插件列表出来了

EC200U-CN学习(五)

预留抓取CP日志 U15-B引脚学习 LOUDSPK_P: 一个连接到一个功率放大器的音频输出引脚。 MIC_P: MIC_P是一个用于连接麦克风的阳性引脚。通常情况下&#xff0c;MIC_P引脚用于连接麦克风的正极&#xff0c;而MIC_N引脚则用于连接麦克风的负极。麦克风的正极和负极之间通常需要连…