Go语言值,指针,引用类型
原文:https://www.jianshu.com/p/af42cb368cef
----------------------------------------------------
Go语言的指针与C或C++的指针类似,但是Go语言的指针不支持指针运算,这样就消除了在C或C++程序中一些潜在的问题。由于Go语言有自己的垃圾回收器,并且会自动管理内存,所以Go语言也不需要像C或C++一样使用free函数或者delete操作符。
Go语言的指针创建后可以像Java和Python中对象的引用一样使用。
在Go语言中,对于布尔变量或数值类型或字符串类型或数组都是按照值传递的:值在传递给函数或者方法时会被复制一份,然后方法或函数使用的是复制的这份值,也就不会对原值产生什么影响。一般情况下,对于布尔变量或数值类型或字符串类型的按值传递是非常廉价的,Go语言编译器会在传递过程中进行安全优化。
但是在Go语言中,字符串是不可变的,因此在进行修改字符串时(例如使用+=操作),Go语言必须创建一个新的字符串,然后复制原始的字符串并将其添加到新字符串之后,对于大字符串来说,操作的代价可能会比较大。
对于大字符串是这样,对于数组进行值传递也是如此。为了解决可能产生的巨大代价,Go语言使用数组切片来代替数组的使用。传递一个切片的代价跟传递字符串差不多,无论该切片的长度或容量是多大。对切片进行复制修改操作也不会像字符串那样需要创建新的切片,因为切片是可变的,属于引用类型。
func main() {
a := 3
b := 4
c := "abc"
d := [3]int{1,2,3}
fmt.Printf("main方法:a的值为 %v,b的值为 %v,c的值为 %v,d的值为 %v \n",a,b,c,d)
demo(a,b,c,d)
fmt.Printf("main方法:a的值为 %v,b的值为 %v,c的值为 %v,d的值为 %v \n",a,b,c,d)
}
func demo(a,b int,c string,d [3]int) {
a = 5
b = 6
c = "efg"
d[0] = 0
fmt.Printf("demo函数:a的值为 %v,b的值为 %v,c的值为 %v,d的值为 %v\n",a,b,c,d)
}
----output----
main方法: a的值为 3,b的值为 4,c的值为 abc,d的值为 [1 2 3]
demo函数: a的值为 5,b的值为 6,c的值为 efg,d的值为 [0 2 3]
main方法: a的值为 3,b的值为 4,c的值为 abc,d的值为 [1 2 3]
Go语言中的引用类型有:映射(map),数组切片(slice),通道(channel),方法与函数。
由于Go语言存在垃圾回收器,因此在一个本地变量不再被使用时(不再被引用或者不在作用于范围)就会被垃圾回收器回收掉,这时本地变量的生命周期由它们的作用域决定。那如果我们想要管理本地变量的生命周期呢?这时就需要使用指针来管理本地变量,只要该变量至少存在一个指针,那么该变量的生命周期就可以独立于作用域。
使用指针能让我们控制变量的生命周期,不受作用域的影响,另外变量在传递过程中成本最小化,且可以轻易的修改变量的内容,而不是对复制的值进行操作。指针是一个变量,这个变量实际上是保存了另一个变量的内存地址,任何被指针保存了内存地址的变量都可以通过指针来修改内容。指针的传递非常廉价。
在使用指针前,我们需要明白两个操作符的含义
①操作符& : 当作二元操作符时,是按位与操作;当作一元操作符时,是返回该变量的内存地址。
②操作符* : 当作二元操作符时,是相乘的操作;当作一元操作符(解引用操作符)时,是返回该指针指向的变量的值,其实就是解除变量的指针引用,返回该变量的值。
指针的创建与使用,可以看下面的代码实例
func main() {
a := 3
p := &a //这里是获取变量a的内存地址,并将其赋值给变量p
fmt.Printf("a的值为 %v, a的指针是 %v ,p指向的变量的值为 %v\n",a,p,*p)
}
-----output-----
a的值为 3, a的指针是 0xc042060080 ,p指向的变量的值为 3
其实*p和变量a的值是相等的,两者可以交换着使用,两者都与同一块内存地址相关联,任意一个变量进行修改操作都会影响到另一个变量的值,但是若变量p被赋值其他变量的指针就不行了。
关于指针的综合运用,我们看以下的代码实例
func main() {
a := 3
b := 4
p1 := &a //获取变量a的内存地址,并将其赋值给变量p1
p2 := &b //获取变量b的内存地址,并将其赋值给变量p2
fmt.Printf("a的值为 %v, a的指针是 %v ,p1指向的变量的值为 %v\n",a,p1,*p1)
fmt.Printf("b的值为 %v, b的指针是 %v ,p2指向的变量的值为 %v\n",b,p2,*p2)
fmt.Println(demo(p1,p2))
fmt.Printf("a的值为 %v, a的指针是 %v ,p1指向的变量的值为 %v\n",a,p1,*p1)
fmt.Printf("b的值为 %v, b的指针是 %v ,p2指向的变量的值为 %v\n",b,p2,*p2)
}
func demo(a,b *int)int {
*a = 5
*b = 6
return *a * *b //这里出现连续的两个*,Go编译器会根据上下文自动识别乘法与两个引用
}
-----output-----
a的值为 3, a的指针是 0xc042060080 ,p1指向的变量的值为 3
b的值为 4, b的指针是 0xc042060088 ,p2指向的变量的值为 4
30
a的值为 5, a的指针是 0xc042060080 ,p1指向的变量的值为 5
b的值为 6, b的指针是 0xc042060088 ,p2指向的变量的值为 6
根据上面代码,我们可以看到使用指针后,本来是值传递的整数类型在函数或方法中修改会影响到原变量的值。
空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil。
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
查看以下实例:
package main
import "fmt"
func main() {
var ptr *int
fmt.Printf("ptr 的值为 : %x\n", ptr )
}
以上实例输出结果为:
ptr 的值为 : 0
空指针判断:
if(ptr != nil) /* ptr 不是空指针 */
if(ptr == nil) /* ptr 是空指针 */
多重间接引用
以上面代码为例, 在a := 3, p1 := &a中,p1是指向a的内存地址,这种叫做间接引用,若还有一个p2是指向p1的内存地址,p1指向a的内存地址,这种就叫做多重间接引用,不管哪种引用,若其中一个变量进行修改内容操作,均会影响到其他所有变量的内容。
func main() {
a := 3
p1 := &a //p1是指向变量a内存地址的指针
p2 := &p1 //p2是指向变量p1内存地址的指针
fmt.Printf("a:%v, p1:%v, *p1:%v, p2:%v, **p2:%v\n",a,p1,*p1,p2,**p2)
a = 4
fmt.Printf("a:%v, p1:%v, *p1:%v, p2:%v, **p2:%v\n",a,p1,*p1,p2,**p2)
}
-----output-----
a:3, p1:0xc0420080b8, *p1:3, p2:0xc042004028, **p2:3
a:4, p1:0xc0420080b8, *p1:4, p2:0xc042004028, **p2:4
new函数与&操作符
Go语言中提供两种创建变量的方式,同时可以获得指向它们的指针:new函数与&操作符。这两种的使用方式如下面代码所示
type Person struct {
name string
sex string
age int
}
func main() {
person1 := Person{"zhangsan","man",25} //创建一个person1对象
person2 := new(Person)//使用new创建一个person2对象,同时获得person的指针
person2.name,person2.sex,person2.age = "wangwu","man",25
person3 := &Person{"lisi","man",25}//使用&创建一个person3对象,同时获得person的指针
fmt.Printf("person1:%v, person2:%v, person3:%v\n",person1,person2,person3)
}
-----output-----
person1:{zhangsan man 25}, person2:&{wangwu man 25}, person3:&{lisi man 25}
从输出结果来看,new函数与&操作符两种方式区别不大,&操作符创建起来更加的简洁,并且随时可以指定属性初始值。Go语言打印指向person的指针时,会打印person属性的具体内容,并且在前缀上加上&表示该变量是一个指针。
接下来我们来传递一个结构体指针,并修改结构体的属性,看看Go语言是如何操作的。
type Person struct {
name string
sex string
age int
}
func main() {
person1 := Person{"zhangsan","man",25} //创建一个person1对象
fmt.Printf("person1:%v\n",person1)
demo(&person1)
fmt.Printf("person1:%v\n",person1)
}
func demo(person *Person) {
(*person).age = 18 //显示的解引用
person.name = "GoLang" //隐式的解引用
}
main函数中创建一个pserson1对象,设置初始化属性后将它的指针传递到demo函数中,大家可以看到demo函数中有两种解引用(就是将一个指针转化为原对象)的方式,第一种: (*person).age是显示的解引用,第二种是使用 "." 操作符自动的将指针解引用,使用上这两种没什么区别,但第二种更简单。
作者:小杰的快乐时光
链接:https://www.jianshu.com/p/af42cb368cef
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Go语言值,指针,引用类型的更多相关文章
- 深入C#内存管理来分析值类型&引用类型,装箱&拆箱,堆栈几个概念组合之间的区别
C#初学者经常被问的几道辨析题,值类型与引用类型,装箱与拆箱,堆栈,这几个概念组合之间区别,看完此篇应该可以解惑. 俗话说,用思想编程的是文艺程序猿,用经验编程的是普通程序猿,用复制粘贴编程的是2B程 ...
- go语言学习--指针的理解
Go 的原生数据类型可以分为基本类型和高级类型,基本类型主要包含 string, bool, int 及 float 系列,高级类型包含 struct,array/slice,map,chan, fu ...
- go语言的指针类型
一.指针与引用的相关概念 什么是指针? 指针,全称为指针变量,是用来存储内存地址的一种变量.程序中,一般通过指针来访问其指向的内存地址中的内容(数据). 什么是引用? 引用,是C++中提出来的一种新的 ...
- C语言 值传递和地址传递
不少同学在学到C语言的指针部分时感到很困惑,对经常提到的"值传递"和"地址传递"两个概念弄不 明白.实际上,因为地址本身也可以作为一个特殊的"值&qu ...
- C语言二重指针与malloc
(内容主要源于网上,只是加入了些自己的剖析) 假设有一个二重指针: char **p; 同时有一个指针数组 char *name[4]; 如何引用p呢? 首先我们有程序代码如下 #include &l ...
- C语言函数指针基础
本文写的非常详细,因为我想为初学者建立一个意识模型,来帮助他们理解函数指针的语法和基础.如果你不讨厌事无巨细,请尽情阅读吧. 函数指针虽然在语法上让人有些迷惑,但不失为一种有趣而强大的工具.本文将从C ...
- 数往知来C#之接口 值类型与引用类型 静态非静态 异常处理 GC垃圾回收 值类型引用类型内存分配<四>
C# 基础接口篇 一.多态复习 使用个new来实现,使用virtual与override -->new隐藏父类方法 根据当前类型,电泳对应的方法(成员) -->override ...
- iOS: 学习笔记, 值与引用类型(译自: https://developer.apple.com/swift/blog/ Aug 15, 2014 Value and Reference Types)
值和引用类型 Value and Reference Types 在Swift中,有两种数据类型. 一是"值类型"(value type), 它是每一个实例都保存有各自的数据,通常 ...
- C语言的指针变量
C语言的指针变量 在C语言中,变量是固定范围的存储空间,它存储的是赋给他的值, 比如: ; /* 这里是定义一个整型变量a,并把12这个值存储在a的地址空间上 这个地址空间是系统随机分配的,对用户是透 ...
随机推荐
- RestHighLevelClient 之 Scroll
ES中默认最大查询结果为10000,大于10000时查不出结果,报错超过最大值,如把 from调到大于10000. 针对这个问题,有两种解决办法. 第一种,修改 max_result_window 很 ...
- 【DSP开发】硬件信号量在多核处理器核间通信中的应用
硬件信号量在多核处理器核间通信中的应用 刘德保1,汪安民1,韩道文2 1.同方电子科技有限公司研究所,九江 332009:2.解放军电子工程学院 摘要: 在多核处理器的软件设计中,核间通信机制是关键所 ...
- C/C++.字符串分割
1.ZC:只测试使用了 自己改编的函数SplitStr_ZZ(...),其它的 未测试 #include <string> #include <vector> #include ...
- eNSP——Hybrid接口的应用
原理: Hybrid接口既可以连接普通终端的接入链路又可以连接交换机间的干道链路,它允许多个VLAN的帧通过,并可以在出接口方向将某些VLAN帧的标签剥掉. Hybrid接口处理VLAN帧的过程如下: ...
- WebForm——浅拷贝与深拷贝
注:本文整理来自连接 https://www.cnblogs.com/echolun/p/7889848.html ,感谢博主的分享 总结: 1.浅拷贝:只拷贝变量的名,而不拷贝变量的值——常为引用类 ...
- #【Python】【demo实验23】【练习实例】【 三人比赛顺序问题 】
原题: 两个乒乓球队进行比赛,各出三人.甲队为a,b,c三人,乙队为x,y,z三人.已抽签决定比赛名单.有人向队员打听比赛的名单.a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单. 我的 ...
- redis主从复制初识
一.作用 slave会通过被复制同步master上面的数据,形成数据副本 当master节点宕机时,slave可以升级为master节点承担写操作. 允许有一主多从,slave可以承担读操作,提高读性 ...
- Intellj IDEA快捷键入门 之 Ctrl+Space(空格)
Intellj IDEA快捷键入门 之 Ctrl+Space(空格) 时间:2019/11/28 系统:Win10系统 背景: Win10系统下,想把ctrl+space(空格)切换输入法取消, 以防 ...
- (六)springMvc 和 mybatis 整合
目录 文章目录 @[toc] 整合 dao 层 整合 springMvc #整合思路 整合是将spring 看做是一个大的容器,将其他东西整合进来,是以 spring 为大环境的: 整合 spring ...
- (二十四)JDBC应用的事务管理(转账事例)
目录 利用 Dbutils 进行事务操作(以转账为例) 转账实现方式(不优雅的方式) ThreadLocal 类 转账实现方式(优雅的方式) 利用 Dbutils 进行事务操作(以转账为例) 我们只在 ...